diff options
Diffstat (limited to 'arch/arm')
459 files changed, 56274 insertions, 3539 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 233a222752c0..2e5784e1ae42 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -18,6 +18,8 @@ config ARM select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) select HAVE_GENERIC_DMA_COHERENT + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZO help The ARM series is a line of low-power-consumption RISC chip designs licensed by ARM Ltd and targeted at embedded applications and @@ -50,6 +52,9 @@ config HAVE_TCM bool select GENERIC_ALLOCATOR +config HAVE_PROC_CPU + bool + config NO_IOPORT bool @@ -552,6 +557,7 @@ config ARCH_PNX4008 bool "Philips Nexperia PNX4008 Mobile" select CPU_ARM926T select HAVE_CLK + select COMMON_CLKDEV help This enables support for Philips PNX4008 mobile platform. @@ -573,14 +579,14 @@ config ARCH_PXA config ARCH_MSM bool "Qualcomm MSM" - select CPU_V6 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_RPC bool "RiscPC" @@ -688,6 +694,7 @@ config ARCH_DAVINCI select HAVE_IDE select COMMON_CLKDEV select GENERIC_ALLOCATOR + select ARCH_HAS_HOLES_MEMORYMODEL help Support for TI's DaVinci platform. @@ -699,6 +706,7 @@ config ARCH_OMAP select ARCH_HAS_CPUFREQ select GENERIC_TIME select GENERIC_CLOCKEVENTS + select ARCH_HAS_HOLES_MEMORYMODEL help Support for TI's OMAP platform (OMAP1 and OMAP2). @@ -1226,6 +1234,7 @@ config ALIGNMENT_TRAP bool depends on CPU_CP15_MMU default y if !ARCH_EBSA110 + select HAVE_PROC_CPU if PROC_FS help ARM processors cannot fetch/store information which is not naturally aligned on the bus, i.e., a 4 byte fetch must start at an diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index ce39dc540085..2d4d88ba73bf 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -63,8 +63,12 @@ endif SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ -targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c \ - head.o misc.o $(OBJS) +suffix_$(CONFIG_KERNEL_GZIP) = gzip +suffix_$(CONFIG_KERNEL_LZO) = lzo + +targets := vmlinux vmlinux.lds \ + piggy.$(suffix_y) piggy.$(suffix_y).o \ + font.o font.c head.o misc.o $(OBJS) ifeq ($(CONFIG_FUNCTION_TRACER),y) ORIG_CFLAGS := $(KBUILD_CFLAGS) @@ -87,22 +91,34 @@ endif ifneq ($(PARAMS_PHYS),) LDFLAGS_vmlinux += --defsym params_phys=$(PARAMS_PHYS) endif -LDFLAGS_vmlinux += -p --no-undefined -X \ - $(shell $(CC) $(KBUILD_CFLAGS) --print-libgcc-file-name) -T +# ? +LDFLAGS_vmlinux += -p +# Report unresolved symbol references +LDFLAGS_vmlinux += --no-undefined +# Delete all temporary local symbols +LDFLAGS_vmlinux += -X +# Next argument is a linker script +LDFLAGS_vmlinux += -T + +# For __aeabi_uidivmod +lib1funcs = $(obj)/lib1funcs.o + +$(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S FORCE + $(call cmd,shipped) # Don't allow any static data in misc.o, which # would otherwise mess up our GOT table CFLAGS_misc.o := -Dstatic= -$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \ - $(addprefix $(obj)/, $(OBJS)) FORCE +$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \ + $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) FORCE $(call if_changed,ld) @: -$(obj)/piggy.gz: $(obj)/../Image FORCE - $(call if_changed,gzip) +$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE + $(call if_changed,$(suffix_y)) -$(obj)/piggy.o: $(obj)/piggy.gz FORCE +$(obj)/piggy.$(suffix_y).o: $(obj)/piggy.$(suffix_y) FORCE CFLAGS_font.o := -Dstatic= diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 17153b54613b..7e0fe4d42c7b 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -18,10 +18,15 @@ unsigned int __machine_arch_type; +#define _LINUX_STRING_H_ + #include <linux/compiler.h> /* for inline */ #include <linux/types.h> /* for size_t */ #include <linux/stddef.h> /* for NULL */ #include <asm/string.h> +#include <linux/linkage.h> + +#include <asm/unaligned.h> #ifdef STANDALONE_DEBUG #define putstr printf @@ -188,34 +193,8 @@ static inline __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, /* * gzip delarations */ -#define OF(args) args #define STATIC static -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -#define WSIZE 0x8000 /* Window size must be at least 32k, */ - /* and a power of two */ - -static uch *inbuf; /* input buffer */ -static uch window[WSIZE]; /* Sliding window buffer */ - -static unsigned insize; /* valid bytes in inbuf */ -static unsigned inptr; /* index of next byte to be processed in inbuf */ -static unsigned outcnt; /* bytes in output buffer */ - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - /* Diagnostic functions */ #ifdef DEBUG # define Assert(cond,msg) {if(!(cond)) error(msg);} @@ -233,24 +212,20 @@ static unsigned outcnt; /* bytes in output buffer */ # define Tracecv(c,x) #endif -static int fill_inbuf(void); -static void flush_window(void); static void error(char *m); extern char input_data[]; extern char input_data_end[]; -static uch *output_data; -static ulg output_ptr; -static ulg bytes_out; +static unsigned char *output_data; +static unsigned long output_ptr; static void error(char *m); static void putstr(const char *); -extern int end; -static ulg free_mem_ptr; -static ulg free_mem_end_ptr; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; #ifdef STANDALONE_DEBUG #define NO_INFLATE_MALLOC @@ -258,46 +233,13 @@ static ulg free_mem_end_ptr; #define ARCH_HAS_DECOMP_WDOG -#include "../../../../lib/inflate.c" - -/* =========================================================================== - * Fill the input buffer. This is called only when the buffer is empty - * and at least one byte is really needed. - */ -int fill_inbuf(void) -{ - if (insize != 0) - error("ran out of input data"); - - inbuf = input_data; - insize = &input_data_end[0] - &input_data[0]; - - inptr = 1; - return inbuf[0]; -} +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.c" +#endif -/* =========================================================================== - * Write the output window window[0..outcnt-1] and update crc and bytes_out. - * (Used for the decompressed data only.) - */ -void flush_window(void) -{ - ulg c = crc; - unsigned n; - uch *in, *out, ch; - - in = window; - out = &output_data[output_ptr]; - for (n = 0; n < outcnt; n++) { - ch = *out++ = *in++; - c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); - } - crc = c; - bytes_out += (ulg)outcnt; - output_ptr += (ulg)outcnt; - outcnt = 0; - putstr("."); -} +#ifdef CONFIG_KERNEL_LZO +#include "../../../../lib/decompress_unlzo.c" +#endif #ifndef arch_error #define arch_error(x) @@ -314,22 +256,33 @@ static void error(char *x) while(1); /* Halt */ } +asmlinkage void __div0(void) +{ + error("Attempting division by 0!"); +} + #ifndef STANDALONE_DEBUG -ulg -decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, - int arch_id) +unsigned long +decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, + unsigned long free_mem_ptr_end_p, + int arch_id) { - output_data = (uch *)output_start; /* Points to kernel start */ + unsigned char *tmp; + + output_data = (unsigned char *)output_start; free_mem_ptr = free_mem_ptr_p; free_mem_end_ptr = free_mem_ptr_end_p; __machine_arch_type = arch_id; arch_decomp_setup(); - makecrc(); + tmp = (unsigned char *) (((unsigned long)input_data_end) - 4); + output_ptr = get_unaligned_le32(tmp); + putstr("Uncompressing Linux..."); - gunzip(); + decompress(input_data, input_data_end - input_data, + NULL, NULL, output_data, NULL, error); putstr(" done, booting the kernel.\n"); return output_ptr; } @@ -341,11 +294,10 @@ int main() { output_data = output_buffer; - makecrc(); putstr("Uncompressing Linux..."); - gunzip(); + decompress(input_data, input_data_end - input_data, + NULL, NULL, output_data, NULL, error); putstr("done.\n"); return 0; } #endif - diff --git a/arch/arm/boot/compressed/piggy.gzip.S b/arch/arm/boot/compressed/piggy.gzip.S new file mode 100644 index 000000000000..a68adf91a165 --- /dev/null +++ b/arch/arm/boot/compressed/piggy.gzip.S @@ -0,0 +1,6 @@ + .section .piggydata,#alloc + .globl input_data +input_data: + .incbin "arch/arm/boot/compressed/piggy.gzip" + .globl input_data_end +input_data_end: diff --git a/arch/arm/boot/compressed/piggy.S b/arch/arm/boot/compressed/piggy.lzo.S index 54c951800ebd..a425ad95959a 100644 --- a/arch/arm/boot/compressed/piggy.S +++ b/arch/arm/boot/compressed/piggy.lzo.S @@ -1,6 +1,6 @@ .section .piggydata,#alloc .globl input_data input_data: - .incbin "arch/arm/boot/compressed/piggy.gz" + .incbin "arch/arm/boot/compressed/piggy.lzo" .globl input_data_end input_data_end: diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c index aae5bc01acc8..446b696196e3 100644 --- a/arch/arm/common/clkdev.c +++ b/arch/arm/common/clkdev.c @@ -99,6 +99,16 @@ void clkdev_add(struct clk_lookup *cl) } EXPORT_SYMBOL(clkdev_add); +void __init clkdev_add_table(struct clk_lookup *cl, size_t num) +{ + mutex_lock(&clocks_mutex); + while (num--) { + list_add_tail(&cl->node, &clocks); + cl++; + } + mutex_unlock(&clocks_mutex); +} + #define MAX_DEV_ID 20 #define MAX_CON_ID 16 diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index cc32c1e54a59..cc0a932bbea9 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -277,7 +277,7 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size, * We don't need to sync the DMA buffer since * it was allocated via the coherent allocators. */ - dma_cache_maint(ptr, size, dir); + __dma_single_cpu_to_dev(ptr, size, dir); } return dma_addr; @@ -315,6 +315,8 @@ static inline void unmap_single(struct device *dev, dma_addr_t dma_addr, __cpuc_flush_dcache_area(ptr, size); } free_safe_buffer(dev->archdata.dmabounce, buf); + } else { + __dma_single_dev_to_cpu(dma_to_virt(dev, dma_addr), size, dir); } } diff --git a/arch/arm/configs/raumfeld_defconfig b/arch/arm/configs/raumfeld_defconfig new file mode 100644 index 000000000000..acb1a8f30e31 --- /dev/null +++ b/arch/arm/configs/raumfeld_defconfig @@ -0,0 +1,1898 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.32-rc5 +# Sun Nov 1 21:57:32 2009 +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_GROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +CONFIG_SLOW_WORK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_BCMRING is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# + +# +# Supported PXA3xx Processor Variants +# +CONFIG_CPU_PXA300=y +# CONFIG_CPU_PXA310 is not set +CONFIG_CPU_PXA320=y +# CONFIG_CPU_PXA930 is not set +# CONFIG_CPU_PXA935 is not set +# CONFIG_CPU_PXA950 is not set + +# +# Intel/Marvell Dev Platforms (sorted by hardware release time) +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_LITTLETON is not set +# CONFIG_MACH_TAVOREVB is not set +# CONFIG_MACH_SAAR is not set + +# +# Third Party Dev Platforms (sorted by vendor name) +# +# CONFIG_ARCH_PXA_IDP is not set +# CONFIG_ARCH_VIPER is not set +# CONFIG_MACH_BALLOON3 is not set +# CONFIG_MACH_CSB726 is not set +# CONFIG_MACH_ARMCORE is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_EXEDA is not set +# CONFIG_MACH_CM_X300 is not set +# CONFIG_ARCH_GUMSTIX is not set +# CONFIG_MACH_INTELMOTE2 is not set +# CONFIG_MACH_STARGATE2 is not set +# CONFIG_MACH_XCEP is not set +# CONFIG_TRIZEPS_PXA is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_PCM027 is not set +# CONFIG_MACH_COLIBRI is not set +# CONFIG_MACH_COLIBRI300 is not set +# CONFIG_MACH_COLIBRI320 is not set + +# +# End-user Products (sorted by vendor name) +# +# CONFIG_MACH_H4700 is not set +# CONFIG_MACH_H5000 is not set +# CONFIG_MACH_HIMALAYA is not set +# CONFIG_MACH_MAGICIAN is not set +# CONFIG_MACH_MIOA701 is not set +# CONFIG_PXA_EZX is not set +# CONFIG_MACH_MP900C is not set +# CONFIG_ARCH_PXA_PALM is not set +CONFIG_MACH_RAUMFELD_RC=y +CONFIG_MACH_RAUMFELD_CONNECTOR=y +CONFIG_MACH_RAUMFELD_PROTO=y +CONFIG_MACH_RAUMFELD_SPEAKER=y +# CONFIG_PXA_SHARPSL is not set +# CONFIG_ARCH_PXA_ESERIES is not set +CONFIG_PXA3xx=y +CONFIG_PXA_SSP=y +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSC3=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_IO_36=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_CACHE_XSC3L2=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="console=ttyS0,115200 rw" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_APM_EMULATION=y +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +CONFIG_CFG80211_REG_DEBUG=y +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_CFG80211_DEFAULT_PS_VALUE=1 +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_RC_MINSTREL=y +# CONFIG_MAC80211_RC_DEFAULT_PID is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel" +# CONFIG_MAC80211_MESH is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +CONFIG_NFTL=y +CONFIG_NFTL_RW=y +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_BLOCK2MTD=y + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_SHARPSL is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_ISL29003=y +CONFIG_TI_DAC7512=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +CONFIG_LIBERTAS=y +# CONFIG_LIBERTAS_USB is not set +CONFIG_LIBERTAS_SDIO=m +# CONFIG_LIBERTAS_SPI is not set +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_P54_COMMON is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_HOSTAP is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_ZD1211RW is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL12XX is not set +# CONFIG_IWM is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +CONFIG_USB_NET_MCS7830=y +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +CONFIG_INPUT_POLLDEV=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +CONFIG_TOUCHSCREEN_EETI=m +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +CONFIG_INPUT_GPIO_ROTARY_ENCODER=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +# CONFIG_SPI_PXA2XX is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_DEBUG_GPIO=y +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +CONFIG_W1=m + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS1WM is not set +CONFIG_W1_MASTER_GPIO=m + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +CONFIG_W1_SLAVE_DS2760=m +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +CONFIG_PDA_POWER=y +# CONFIG_APM_POWER is not set +CONFIG_BATTERY_DS2760=m +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_SENSORS_LIS3_SPI=y +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +CONFIG_REGULATOR_MAX8660=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +# CONFIG_FB_PXA_OVERLAY is not set +# CONFIG_FB_PXA_SMARTPANEL is not set +# CONFIG_FB_PXA_PARAMETERS is not set +CONFIG_PXA3XX_GCU=y +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_PWM=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_LOGO_RAUMFELD_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_DRIVERS is not set +CONFIG_SND_ARM=y +CONFIG_SND_PXA2XX_LIB=y +# CONFIG_SND_PXA2XX_AC97 is not set +CONFIG_SND_SPI=y +# CONFIG_SND_USB is not set +CONFIG_SND_SOC=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA_SOC_SSP=y +CONFIG_SND_SOC_RAUMFELD=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_AK4104=y +CONFIG_SND_SOC_CS4270=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +# CONFIG_DRAGONRISE_FF is not set +CONFIG_HID_EZKEY=y +CONFIG_HID_KYE=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +# CONFIG_GREENASIA_FF is not set +CONFIG_HID_SMARTJOYPLUS=y +# CONFIG_SMARTJOYPLUS_FF is not set +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +CONFIG_USB_DEBUG=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA=m +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_LT3593=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_SA1100 is not set +CONFIG_RTC_DRV_PXA=y +CONFIG_DMADEVICES=y + +# +# DMA Devices +# +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +# CONFIG_UIO_SMX is not set +# CONFIG_UIO_SERCOS3 is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT2_FS_XIP=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_FS_XIP=y +CONFIG_JBD=y +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +CONFIG_FSCACHE=y +CONFIG_FSCACHE_STATS=y +# CONFIG_FSCACHE_HISTOGRAM is not set +# CONFIG_FSCACHE_DEBUG is not set +CONFIG_CACHEFILES=y +# CONFIG_CACHEFILES_DEBUG is not set +# CONFIG_CACHEFILES_HISTOGRAM is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +CONFIG_NFS_FSCACHE=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=y +CONFIG_NLS_CODEPAGE_775=y +CONFIG_NLS_CODEPAGE_850=y +CONFIG_NLS_CODEPAGE_852=y +CONFIG_NLS_CODEPAGE_855=y +CONFIG_NLS_CODEPAGE_857=y +CONFIG_NLS_CODEPAGE_860=y +CONFIG_NLS_CODEPAGE_861=y +CONFIG_NLS_CODEPAGE_862=y +CONFIG_NLS_CODEPAGE_863=y +CONFIG_NLS_CODEPAGE_864=y +CONFIG_NLS_CODEPAGE_865=y +CONFIG_NLS_CODEPAGE_866=y +CONFIG_NLS_CODEPAGE_869=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_CODEPAGE_950=y +CONFIG_NLS_CODEPAGE_932=y +CONFIG_NLS_CODEPAGE_949=y +CONFIG_NLS_CODEPAGE_874=y +CONFIG_NLS_ISO8859_8=y +CONFIG_NLS_CODEPAGE_1250=y +CONFIG_NLS_CODEPAGE_1251=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=y +CONFIG_NLS_ISO8859_3=y +CONFIG_NLS_ISO8859_4=y +CONFIG_NLS_ISO8859_5=y +CONFIG_NLS_ISO8859_6=y +CONFIG_NLS_ISO8859_7=y +CONFIG_NLS_ISO8859_9=y +CONFIG_NLS_ISO8859_13=y +CONFIG_NLS_ISO8859_14=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_KOI8_R=y +CONFIG_NLS_KOI8_U=y +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/u300_defconfig b/arch/arm/configs/u300_defconfig index 610ac3c47b0f..9155196e623b 100644 --- a/arch/arm/configs/u300_defconfig +++ b/arch/arm/configs/u300_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.32-rc5 -# Sat Oct 17 23:32:24 2009 +# Linux kernel version: 2.6.33-rc2 +# Wed Jan 6 00:01:36 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -46,6 +46,7 @@ CONFIG_SYSVIPC_SYSCTL=y # CONFIG_TREE_RCU=y # CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 # CONFIG_RCU_FANOUT_EXACT is not set @@ -119,14 +120,41 @@ CONFIG_BLOCK=y # IO Schedulers # CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set CONFIG_IOSCHED_DEADLINE=y # CONFIG_IOSCHED_CFQ is not set -# CONFIG_DEFAULT_AS is not set CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="deadline" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set # CONFIG_FREEZER is not set # @@ -155,6 +183,7 @@ CONFIG_MMU=y # CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_IXP4XX is not set # CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set # CONFIG_ARCH_KIRKWOOD is not set # CONFIG_ARCH_LOKI is not set # CONFIG_ARCH_MV78XX0 is not set @@ -177,6 +206,7 @@ CONFIG_ARCH_U300=y # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set # CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_U8500 is not set # # ST-Ericsson AB U300/U330/U335/U365 Platform @@ -265,12 +295,10 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_SPLIT_PTLOCK_CPUS=4096 +CONFIG_SPLIT_PTLOCK_CPUS=999999 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -CONFIG_HAVE_MLOCK=y -CONFIG_HAVE_MLOCKED_PAGE_BIT=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 CONFIG_ALIGNMENT_TRAP=y @@ -499,14 +527,21 @@ CONFIG_MTD_NAND_IDS=y CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set # CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set # CONFIG_ISL29003 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set # CONFIG_C2PORT is not set # @@ -517,6 +552,7 @@ CONFIG_MISC_DEVICES=y # CONFIG_EEPROM_LEGACY is not set # CONFIG_EEPROM_MAX6875 is not set # CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -539,6 +575,7 @@ CONFIG_HAVE_IDE=y CONFIG_INPUT=y # CONFIG_INPUT_FF_MEMLESS is not set # CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set # # Userland interfaces @@ -645,7 +682,6 @@ CONFIG_I2C_STU300=y # # Miscellaneous I2C Chip support # -# CONFIG_DS1682 is not set # CONFIG_SENSORS_TSL2550 is not set # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set @@ -661,6 +697,8 @@ CONFIG_SPI_MASTER=y # CONFIG_SPI_BITBANG is not set # CONFIG_SPI_GPIO is not set CONFIG_SPI_PL022=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set # # SPI Protocol Masters @@ -708,6 +746,7 @@ CONFIG_SSB_POSSIBLE=y # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set # CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set # CONFIG_MFD_WM8400 is not set # CONFIG_MFD_WM831X is not set # CONFIG_MFD_WM8350_I2C is not set @@ -716,6 +755,8 @@ CONFIG_SSB_POSSIBLE=y CONFIG_AB3100_CORE=y CONFIG_AB3100_OTP=y # CONFIG_EZX_PCAP is not set +# CONFIG_MFD_88PM8607 is not set +# CONFIG_AB4500_CORE is not set CONFIG_REGULATOR=y # CONFIG_REGULATOR_DEBUG is not set # CONFIG_REGULATOR_FIXED_VOLTAGE is not set @@ -723,6 +764,7 @@ CONFIG_REGULATOR=y # CONFIG_REGULATOR_USERSPACE_CONSUMER is not set # CONFIG_REGULATOR_BQ24022 is not set # CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8660 is not set # CONFIG_REGULATOR_LP3971 is not set CONFIG_REGULATOR_AB3100=y # CONFIG_REGULATOR_TPS65023 is not set @@ -840,7 +882,9 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_LP3944 is not set # CONFIG_LEDS_PCA955X is not set # CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set # CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set # # LED Triggers @@ -882,6 +926,7 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_PCF8563 is not set # CONFIG_RTC_DRV_PCF8583 is not set # CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set # CONFIG_RTC_DRV_S35390A is not set # CONFIG_RTC_DRV_FM3130 is not set # CONFIG_RTC_DRV_RX8581 is not set @@ -911,7 +956,9 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_M48T86 is not set # CONFIG_RTC_DRV_M48T35 is not set # CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set # CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set # CONFIG_RTC_DRV_V3020 is not set CONFIG_RTC_DRV_AB3100=y @@ -926,6 +973,15 @@ CONFIG_DMADEVICES=y # # DMA Devices # +CONFIG_COH901318=y +CONFIG_DMA_ENGINE=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set # CONFIG_AUXDISPLAY is not set # CONFIG_UIO is not set @@ -1018,7 +1074,7 @@ CONFIG_MISC_FILESYSTEMS=y CONFIG_MSDOS_PARTITION=y CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -1135,6 +1191,7 @@ CONFIG_ARM_UNWIND=y # CONFIG_DEBUG_ERRORS is not set # CONFIG_DEBUG_STACK_USAGE is not set # CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set # # Security options @@ -1142,7 +1199,11 @@ CONFIG_ARM_UNWIND=y # CONFIG_KEYS is not set # CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" # CONFIG_CRYPTO is not set # CONFIG_BINARY_PRINTF is not set diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 730aefcfbee3..be8b4d79cf41 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -182,21 +182,6 @@ * DMA Cache Coherency * =================== * - * dma_inv_range(start, end) - * - * Invalidate (discard) the specified virtual address range. - * May not write back any entries. If 'start' or 'end' - * are not cache line aligned, those lines must be written - * back. - * - start - virtual start address - * - end - virtual end address - * - * dma_clean_range(start, end) - * - * Clean (write back) the specified virtual address range. - * - start - virtual start address - * - end - virtual end address - * * dma_flush_range(start, end) * * Clean and invalidate the specified virtual address range. @@ -213,8 +198,9 @@ struct cpu_cache_fns { void (*coherent_user_range)(unsigned long, unsigned long); void (*flush_kern_dcache_area)(void *, size_t); - void (*dma_inv_range)(const void *, const void *); - void (*dma_clean_range)(const void *, const void *); + void (*dma_map_area)(const void *, size_t, int); + void (*dma_unmap_area)(const void *, size_t, int); + void (*dma_flush_range)(const void *, const void *); }; @@ -244,8 +230,8 @@ extern struct cpu_cache_fns cpu_cache; * is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ -#define dmac_inv_range cpu_cache.dma_inv_range -#define dmac_clean_range cpu_cache.dma_clean_range +#define dmac_map_area cpu_cache.dma_map_area +#define dmac_unmap_area cpu_cache.dma_unmap_area #define dmac_flush_range cpu_cache.dma_flush_range #else @@ -270,12 +256,12 @@ extern void __cpuc_flush_dcache_area(void *, size_t); * is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ -#define dmac_inv_range __glue(_CACHE,_dma_inv_range) -#define dmac_clean_range __glue(_CACHE,_dma_clean_range) +#define dmac_map_area __glue(_CACHE,_dma_map_area) +#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) #define dmac_flush_range __glue(_CACHE,_dma_flush_range) -extern void dmac_inv_range(const void *, const void *); -extern void dmac_clean_range(const void *, const void *); +extern void dmac_map_area(const void *, size_t, int); +extern void dmac_unmap_area(const void *, size_t, int); extern void dmac_flush_range(const void *, const void *); #endif @@ -316,12 +302,8 @@ static inline void outer_flush_range(unsigned long start, unsigned long end) * processes address space. Really, we want to allow our "user * space" model to handle this. */ -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ - do { \ - memcpy(dst, src, len); \ - flush_ptrace_access(vma, page, vaddr, dst, len, 1);\ - } while (0) - +extern void copy_to_user_page(struct vm_area_struct *, struct page *, + unsigned long, void *, const void *, unsigned long); #define copy_from_user_page(vma, page, vaddr, dst, src, len) \ do { \ memcpy(dst, src, len); \ @@ -355,17 +337,6 @@ vivt_flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig } } -static inline void -vivt_flush_ptrace_access(struct vm_area_struct *vma, struct page *page, - unsigned long uaddr, void *kaddr, - unsigned long len, int write) -{ - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { - unsigned long addr = (unsigned long)kaddr; - __cpuc_coherent_kern_range(addr, addr + len); - } -} - #ifndef CONFIG_CPU_CACHE_VIPT #define flush_cache_mm(mm) \ vivt_flush_cache_mm(mm) @@ -373,15 +344,10 @@ vivt_flush_ptrace_access(struct vm_area_struct *vma, struct page *page, vivt_flush_cache_range(vma,start,end) #define flush_cache_page(vma,addr,pfn) \ vivt_flush_cache_page(vma,addr,pfn) -#define flush_ptrace_access(vma,page,ua,ka,len,write) \ - vivt_flush_ptrace_access(vma,page,ua,ka,len,write) #else extern void flush_cache_mm(struct mm_struct *mm); extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn); -extern void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, - unsigned long uaddr, void *kaddr, - unsigned long len, int write); #endif #define flush_cache_dup_mm(mm) flush_cache_mm(mm) diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h index b6ec7c627b39..7a0690da5e63 100644 --- a/arch/arm/include/asm/clkdev.h +++ b/arch/arm/include/asm/clkdev.h @@ -27,4 +27,7 @@ struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, void clkdev_add(struct clk_lookup *cl); void clkdev_drop(struct clk_lookup *cl); +void clkdev_add_table(struct clk_lookup *, size_t); +int clk_add_alias(const char *, const char *, char *, struct device *); + #endif diff --git a/arch/arm/include/asm/cpu.h b/arch/arm/include/asm/cpu.h index 634b2d7c612a..793968173bef 100644 --- a/arch/arm/include/asm/cpu.h +++ b/arch/arm/include/asm/cpu.h @@ -11,6 +11,7 @@ #define __ASM_ARM_CPU_H #include <linux/percpu.h> +#include <linux/cpu.h> struct cpuinfo_arm { struct cpu cpu; diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index a96300bf83fd..256ee1c9f51a 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -57,18 +57,58 @@ static inline dma_addr_t virt_to_dma(struct device *dev, void *addr) #endif /* - * DMA-consistent mapping functions. These allocate/free a region of - * uncached, unwrite-buffered mapped memory space for use with DMA - * devices. This is the "generic" version. The PCI specific version - * is in pci.h + * The DMA API is built upon the notion of "buffer ownership". A buffer + * is either exclusively owned by the CPU (and therefore may be accessed + * by it) or exclusively owned by the DMA device. These helper functions + * represent the transitions between these two ownership states. * - * Note: Drivers should NOT use this function directly, as it will break - * platforms with CONFIG_DMABOUNCE. - * Use the driver DMA support - see dma-mapping.h (dma_sync_*) + * Note, however, that on later ARMs, this notion does not work due to + * speculative prefetches. We model our approach on the assumption that + * the CPU does do speculative prefetches, which means we clean caches + * before transfers and delay cache invalidation until transfer completion. + * + * Private support functions: these are not part of the API and are + * liable to change. Drivers must not use these. */ -extern void dma_cache_maint(const void *kaddr, size_t size, int rw); -extern void dma_cache_maint_page(struct page *page, unsigned long offset, - size_t size, int rw); +static inline void __dma_single_cpu_to_dev(const void *kaddr, size_t size, + enum dma_data_direction dir) +{ + extern void ___dma_single_cpu_to_dev(const void *, size_t, + enum dma_data_direction); + + if (!arch_is_coherent()) + ___dma_single_cpu_to_dev(kaddr, size, dir); +} + +static inline void __dma_single_dev_to_cpu(const void *kaddr, size_t size, + enum dma_data_direction dir) +{ + extern void ___dma_single_dev_to_cpu(const void *, size_t, + enum dma_data_direction); + + if (!arch_is_coherent()) + ___dma_single_dev_to_cpu(kaddr, size, dir); +} + +static inline void __dma_page_cpu_to_dev(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + extern void ___dma_page_cpu_to_dev(struct page *, unsigned long, + size_t, enum dma_data_direction); + + if (!arch_is_coherent()) + ___dma_page_cpu_to_dev(page, off, size, dir); +} + +static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + extern void ___dma_page_dev_to_cpu(struct page *, unsigned long, + size_t, enum dma_data_direction); + + if (!arch_is_coherent()) + ___dma_page_dev_to_cpu(page, off, size, dir); +} /* * Return whether the given device DMA address mask can be supported @@ -304,8 +344,7 @@ static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, { BUG_ON(!valid_dma_direction(dir)); - if (!arch_is_coherent()) - dma_cache_maint(cpu_addr, size, dir); + __dma_single_cpu_to_dev(cpu_addr, size, dir); return virt_to_dma(dev, cpu_addr); } @@ -329,8 +368,7 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, { BUG_ON(!valid_dma_direction(dir)); - if (!arch_is_coherent()) - dma_cache_maint_page(page, offset, size, dir); + __dma_page_cpu_to_dev(page, offset, size, dir); return page_to_dma(dev, page) + offset; } @@ -352,7 +390,7 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, static inline void dma_unmap_single(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - /* nothing to do */ + __dma_single_dev_to_cpu(dma_to_virt(dev, handle), size, dir); } /** @@ -372,7 +410,8 @@ static inline void dma_unmap_single(struct device *dev, dma_addr_t handle, static inline void dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - /* nothing to do */ + __dma_page_dev_to_cpu(dma_to_page(dev, handle), handle & ~PAGE_MASK, + size, dir); } #endif /* CONFIG_DMABOUNCE */ @@ -400,7 +439,10 @@ static inline void dma_sync_single_range_for_cpu(struct device *dev, { BUG_ON(!valid_dma_direction(dir)); - dmabounce_sync_for_cpu(dev, handle, offset, size, dir); + if (!dmabounce_sync_for_cpu(dev, handle, offset, size, dir)) + return; + + __dma_single_dev_to_cpu(dma_to_virt(dev, handle) + offset, size, dir); } static inline void dma_sync_single_range_for_device(struct device *dev, @@ -412,8 +454,7 @@ static inline void dma_sync_single_range_for_device(struct device *dev, if (!dmabounce_sync_for_device(dev, handle, offset, size, dir)) return; - if (!arch_is_coherent()) - dma_cache_maint(dma_to_virt(dev, handle) + offset, size, dir); + __dma_single_cpu_to_dev(dma_to_virt(dev, handle) + offset, size, dir); } static inline void dma_sync_single_for_cpu(struct device *dev, diff --git a/arch/arm/include/asm/dma.h b/arch/arm/include/asm/dma.h index 7edf3536df24..ca51143f97f1 100644 --- a/arch/arm/include/asm/dma.h +++ b/arch/arm/include/asm/dma.h @@ -138,12 +138,12 @@ extern int get_dma_residue(unsigned int chan); #define NO_DMA 255 #endif +#endif /* CONFIG_ISA_DMA_API */ + #ifdef CONFIG_PCI extern int isa_dma_bridge_buggy; #else #define isa_dma_bridge_buggy (0) #endif -#endif /* CONFIG_ISA_DMA_API */ - #endif /* __ASM_ARM_DMA_H */ diff --git a/arch/arm/include/asm/entry-macro-vic2.S b/arch/arm/include/asm/entry-macro-vic2.S new file mode 100644 index 000000000000..3ceb85e43850 --- /dev/null +++ b/arch/arm/include/asm/entry-macro-vic2.S @@ -0,0 +1,57 @@ +/* arch/arm/include/asm/entry-macro-vic2.S + * + * Originally arch/arm/mach-s3c6400/include/mach/entry-macro.S + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * Low-level IRQ helper macros for a device with two VICs + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. +*/ + +/* This should be included from <mach/entry-macro.S> with the necessary + * defines for virtual addresses and IRQ bases for the two vics. + * + * The code needs the following defined: + * IRQ_VIC0_BASE IRQ number of VIC0's first IRQ + * IRQ_VIC1_BASE IRQ number of VIC1's first IRQ + * VA_VIC0 Virtual address of VIC0 + * VA_VIC1 Virtual address of VIC1 + * + * Note, code assumes VIC0's virtual address is an ARM immediate constant + * away from VIC1. +*/ + +#include <asm/hardware/vic.h> + + .macro disable_fiq + .endm + + .macro get_irqnr_preamble, base, tmp + ldr \base, =VA_VIC0 + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + + @ check the vic0 + mov \irqnr, #IRQ_VIC0_BASE + 31 + ldr \irqstat, [ \base, # VIC_IRQ_STATUS ] + teq \irqstat, #0 + + @ otherwise try vic1 + addeq \tmp, \base, #(VA_VIC1 - VA_VIC0) + addeq \irqnr, \irqnr, #(IRQ_VIC1_BASE - IRQ_VIC0_BASE) + ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ] + teqeq \irqstat, #0 + + clzne \irqstat, \irqstat + subne \irqnr, \irqnr, \irqstat + .endm diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index d2a59cfc30ce..3082d5b70e3b 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -69,9 +69,16 @@ extern void __raw_readsl(const void __iomem *addr, void *data, int longlen); /* * __arm_ioremap takes CPU physical address. * __arm_ioremap_pfn takes a Page Frame Number and an offset into that page + * The _caller variety takes a __builtin_return_address(0) value for + * /proc/vmalloc to use - and should only be used in non-inline functions. */ -extern void __iomem * __arm_ioremap_pfn(unsigned long, unsigned long, size_t, unsigned int); -extern void __iomem * __arm_ioremap(unsigned long, size_t, unsigned int); +extern void __iomem *__arm_ioremap_pfn_caller(unsigned long, unsigned long, + size_t, unsigned int, void *); +extern void __iomem *__arm_ioremap_caller(unsigned long, size_t, unsigned int, + void *); + +extern void __iomem *__arm_ioremap_pfn(unsigned long, unsigned long, size_t, unsigned int); +extern void __iomem *__arm_ioremap(unsigned long, size_t, unsigned int); extern void __iounmap(volatile void __iomem *addr); /* @@ -240,10 +247,14 @@ extern void _memset_io(volatile void __iomem *, int, size_t); #define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; }) #define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; }) #define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; }) +#define ioread16be(p) ({ unsigned int __v = be16_to_cpu((__force __be16)__raw_readw(p)); __v; }) +#define ioread32be(p) ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); __v; }) #define iowrite8(v,p) __raw_writeb(v, p) #define iowrite16(v,p) __raw_writew((__force __u16)cpu_to_le16(v), p) #define iowrite32(v,p) __raw_writel((__force __u32)cpu_to_le32(v), p) +#define iowrite16be(v,p) __raw_writew((__force __u16)cpu_to_be16(v), p) +#define iowrite32be(v,p) __raw_writel((__force __u32)cpu_to_be32(v), p) #define ioread8_rep(p,d,c) __raw_readsb(p,d,c) #define ioread16_rep(p,d,c) __raw_readsw(p,d,c) diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h index 67af4b841984..17c69ae4c309 100644 --- a/arch/arm/include/asm/kgdb.h +++ b/arch/arm/include/asm/kgdb.h @@ -11,6 +11,7 @@ #define __ARM_KGDB_H__ #include <linux/ptrace.h> +#include <linux/notifier.h> /* * GDB assumes that we're a user process being debugged, so @@ -95,6 +96,16 @@ extern int kgdb_fault_expected; #define _PC 15 #define _CPSR (GDB_MAX_REGS - 1) +#ifdef CONFIG_KGDB +int kgdb_die_hook(int cmd, const char *str, struct pt_regs *regs, int err); +#else +static inline int kgdb_die_hook(int cmd, const char *str, + struct pt_regs *regs, int err) +{ + return NOTIFY_DONE; +} +#endif + /* * So that we can denote the end of a frame for tracing, * in the simple case: diff --git a/arch/arm/include/asm/kmap_types.h b/arch/arm/include/asm/kmap_types.h index c019949a5189..3a9fb574fd8d 100644 --- a/arch/arm/include/asm/kmap_types.h +++ b/arch/arm/include/asm/kmap_types.h @@ -19,6 +19,7 @@ enum km_type { KM_SOFTIRQ0, KM_SOFTIRQ1, KM_L2_CACHE, + KM_KDB, KM_TYPE_NR }; diff --git a/arch/arm/include/asm/mach/time.h b/arch/arm/include/asm/mach/time.h index b2cc1fcd0400..8bffc3ff3acf 100644 --- a/arch/arm/include/asm/mach/time.h +++ b/arch/arm/include/asm/mach/time.h @@ -46,12 +46,4 @@ struct sys_timer { extern struct sys_timer *system_timer; extern void timer_tick(void); -/* - * Kernel time keeping support. - */ -struct timespec; -extern int (*set_rtc)(void); -extern void save_time_delta(struct timespec *delta, struct timespec *rtc); -extern void restore_time_delta(struct timespec *delta, struct timespec *rtc); - #endif diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h index 3a32af4cce30..a485ac3c8696 100644 --- a/arch/arm/include/asm/page.h +++ b/arch/arm/include/asm/page.h @@ -117,11 +117,12 @@ #endif struct page; +struct vm_area_struct; struct cpu_user_fns { void (*cpu_clear_user_highpage)(struct page *page, unsigned long vaddr); void (*cpu_copy_user_highpage)(struct page *to, struct page *from, - unsigned long vaddr); + unsigned long vaddr, struct vm_area_struct *vma); }; #ifdef MULTI_USER @@ -137,7 +138,7 @@ extern struct cpu_user_fns cpu_user; extern void __cpu_clear_user_highpage(struct page *page, unsigned long vaddr); extern void __cpu_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr); + unsigned long vaddr, struct vm_area_struct *vma); #endif #define clear_user_highpage(page,vaddr) \ @@ -145,7 +146,7 @@ extern void __cpu_copy_user_highpage(struct page *to, struct page *from, #define __HAVE_ARCH_COPY_USER_HIGHPAGE #define copy_user_highpage(to,from,vaddr,vma) \ - __cpu_copy_user_highpage(to, from, vaddr) + __cpu_copy_user_highpage(to, from, vaddr, vma) #define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) extern void copy_page(void *to, const void *from); diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index bbecccda76d0..eec6e897ceb2 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -97,9 +97,15 @@ * stack during a system call. Note that sizeof(struct pt_regs) * has to be a multiple of 8. */ +#ifndef __KERNEL__ struct pt_regs { long uregs[18]; }; +#else /* __KERNEL__ */ +struct pt_regs { + unsigned long uregs[18]; +}; +#endif /* __KERNEL__ */ #define ARM_cpsr uregs[16] #define ARM_pc uregs[15] diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 5ccce0a9b03c..f392fb4437af 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -223,18 +223,6 @@ extern struct meminfo meminfo; #define bank_phys_end(bank) ((bank)->start + (bank)->size) #define bank_phys_size(bank) (bank)->size -/* - * Early command line parameters. - */ -struct early_params { - const char *arg; - void (*fn)(char **p); -}; - -#define __early_param(name,fn) \ -static struct early_params __early_##fn __used \ -__attribute__((__section__(".early_param.init"))) = { name, fn } - #endif /* __KERNEL__ */ #endif diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index 59303e200845..e6215305544a 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -13,4 +13,9 @@ static inline int tlb_ops_need_broadcast(void) return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; } +static inline int cache_ops_need_broadcast(void) +{ + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 1; +} + #endif diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 4e506d09e5f9..cf9cdaa2d4d4 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -391,6 +391,7 @@ #define __NR_pwritev (__NR_SYSCALL_BASE+362) #define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363) #define __NR_perf_event_open (__NR_SYSCALL_BASE+364) +#define __NR_recvmmsg (__NR_SYSCALL_BASE+365) /* * The following SWIs are ARM private. diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index dd00f747e2ad..eb0284cd8661 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -17,6 +17,7 @@ obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ process.o ptrace.o return_address.o setup.o signal.o \ sys_arm.o stacktrace.o time.o traps.o +obj-$(CONFIG_LEDS) += leds.o obj-$(CONFIG_OC_ETM) += etm.o obj-$(CONFIG_ISA_DMA_API) += dma.o diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 4a881258bb17..883511522fca 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -12,6 +12,7 @@ */ #include <linux/sched.h> #include <linux/mm.h> +#include <linux/dma-mapping.h> #include <asm/mach/arch.h> #include <asm/thread_info.h> #include <asm/memory.h> @@ -112,5 +113,9 @@ int main(void) #ifdef MULTI_PABORT DEFINE(PROCESSOR_PABT_FUNC, offsetof(struct processor, _prefetch_abort)); #endif + BLANK(); + DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL); + DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE); + DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE); return 0; } diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index d2903e3bc861..6c5cf369183b 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -957,9 +957,7 @@ kuser_cmpxchg_fixup: #else -#ifdef CONFIG_SMP - mcr p15, 0, r0, c7, c10, 5 @ dmb -#endif + smp_dmb 1: ldrex r3, [r2] subs r3, r3, r0 strexeq r3, r1, [r2] diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index ba8ccfede964..3c0e5e533e95 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -10,6 +10,7 @@ * Deepak Saxena <dsaxena@plexity.net> */ #include <linux/kgdb.h> +#include <linux/notifier.h> #include <asm/traps.h> /* Make a local copy of the registers passed into the handler (bletch) */ @@ -97,6 +98,11 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) gdb_regs[_CPSR] = thread_regs->ARM_cpsr; } +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->ARM_pc = pc; +} + static int compiled_break; int kgdb_arch_handle_exception(int exception_vector, int signo, @@ -184,6 +190,13 @@ void kgdb_arch_exit(void) unregister_undef_hook(&kgdb_compiled_brkpt_hook); } +int kgdb_die_hook(int cmd, const char *str, struct pt_regs *regs, int err) +{ + if (kgdb_handle_exception(1, err, cmd, regs)) + return NOTIFY_DONE; + return NOTIFY_STOP; +} + /* * Register our undef instruction hooks with ARM undef core. * We regsiter a hook specifically looking for the KGB break inst diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c new file mode 100644 index 000000000000..31a316c1777b --- /dev/null +++ b/arch/arm/kernel/leds.c @@ -0,0 +1,115 @@ +/* + * LED support code, ripped out of arch/arm/kernel/time.c + * + * Copyright (C) 1994-2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sysdev.h> + +#include <asm/leds.h> + +static void dummy_leds_event(led_event_t evt) +{ +} + +void (*leds_event)(led_event_t) = dummy_leds_event; + +struct leds_evt_name { + const char name[8]; + int on; + int off; +}; + +static const struct leds_evt_name evt_names[] = { + { "amber", led_amber_on, led_amber_off }, + { "blue", led_blue_on, led_blue_off }, + { "green", led_green_on, led_green_off }, + { "red", led_red_on, led_red_off }, +}; + +static ssize_t leds_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + int ret = -EINVAL, len = strcspn(buf, " "); + + if (len > 0 && buf[len] == '\0') + len--; + + if (strncmp(buf, "claim", len) == 0) { + leds_event(led_claim); + ret = size; + } else if (strncmp(buf, "release", len) == 0) { + leds_event(led_release); + ret = size; + } else { + int i; + + for (i = 0; i < ARRAY_SIZE(evt_names); i++) { + if (strlen(evt_names[i].name) != len || + strncmp(buf, evt_names[i].name, len) != 0) + continue; + if (strncmp(buf+len, " on", 3) == 0) { + leds_event(evt_names[i].on); + ret = size; + } else if (strncmp(buf+len, " off", 4) == 0) { + leds_event(evt_names[i].off); + ret = size; + } + break; + } + } + return ret; +} + +static SYSDEV_ATTR(event, 0200, NULL, leds_store); + +static int leds_suspend(struct sys_device *dev, pm_message_t state) +{ + leds_event(led_stop); + return 0; +} + +static int leds_resume(struct sys_device *dev) +{ + leds_event(led_start); + return 0; +} + +static int leds_shutdown(struct sys_device *dev) +{ + leds_event(led_halted); + return 0; +} + +static struct sysdev_class leds_sysclass = { + .name = "leds", + .shutdown = leds_shutdown, + .suspend = leds_suspend, + .resume = leds_resume, +}; + +static struct sys_device leds_device = { + .id = 0, + .cls = &leds_sysclass, +}; + +static int __init leds_init(void) +{ + int ret; + ret = sysdev_class_register(&leds_sysclass); + if (ret == 0) + ret = sysdev_register(&leds_device); + if (ret == 0) + ret = sysdev_create_file(&leds_device, &attr_event); + return ret; +} + +device_initcall(leds_init); + +EXPORT_SYMBOL(leds_event); diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 67304138a2ca..ba2adefa53f7 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -212,7 +212,8 @@ void __show_regs(struct pt_regs *regs) char buf[64]; printk("CPU: %d %s (%s %.*s)\n", - smp_processor_id(), print_tainted(), init_utsname()->release, + raw_smp_processor_id(), print_tainted(), + init_utsname()->release, (int)strcspn(init_utsname()->version, " "), init_utsname()->version); print_symbol("PC is at %s\n", instruction_pointer(regs)); diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index c6c57b640b6b..b01a56a03ed8 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/smp.h> #include <linux/fs.h> +#include <linux/proc_fs.h> #include <asm/unified.h> #include <asm/cpu.h> @@ -417,10 +418,11 @@ static int __init arm_add_memory(unsigned long start, unsigned long size) * Pick out the memory size. We look for mem=size@start, * where start and size are "size[KkMm]" */ -static void __init early_mem(char **p) +static int __init early_mem(char *p) { static int usermem __initdata = 0; unsigned long size, start; + char *endp; /* * If the user specifies memory size, we @@ -433,52 +435,15 @@ static void __init early_mem(char **p) } start = PHYS_OFFSET; - size = memparse(*p, p); - if (**p == '@') - start = memparse(*p + 1, p); + size = memparse(p, &endp); + if (*endp == '@') + start = memparse(endp + 1, NULL); arm_add_memory(start, size); -} -__early_param("mem=", early_mem); -/* - * Initial parsing of the command line. - */ -static void __init parse_cmdline(char **cmdline_p, char *from) -{ - char c = ' ', *to = command_line; - int len = 0; - - for (;;) { - if (c == ' ') { - extern struct early_params __early_begin, __early_end; - struct early_params *p; - - for (p = &__early_begin; p < &__early_end; p++) { - int arglen = strlen(p->arg); - - if (memcmp(from, p->arg, arglen) == 0) { - if (to != command_line) - to -= 1; - from += arglen; - p->fn(&from); - - while (*from != ' ' && *from != '\0') - from++; - break; - } - } - } - c = *from++; - if (!c) - break; - if (COMMAND_LINE_SIZE <= ++len) - break; - *to++ = c; - } - *to = '\0'; - *cmdline_p = command_line; + return 0; } +early_param("mem", early_mem); static void __init setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz) @@ -739,9 +704,15 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; - memcpy(boot_command_line, from, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; - parse_cmdline(cmdline_p, from); + /* parse_early_param needs a boot_command_line */ + strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); + + /* populate command_line too for later use, preserving boot_command_line */ + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); + *cmdline_p = command_line; + + parse_early_param(); + paging_init(mdesc); request_standard_resources(&meminfo, mdesc); @@ -782,9 +753,21 @@ static int __init topology_init(void) return 0; } - subsys_initcall(topology_init); +#ifdef CONFIG_HAVE_PROC_CPU +static int __init proc_cpu_init(void) +{ + struct proc_dir_entry *res; + + res = proc_mkdir("cpu", NULL); + if (!res) + return -ENOMEM; + return 0; +} +fs_initcall(proc_cpu_init); +#endif + static const char *hwcap_str[] = { "swp", "half", diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index d38cdf2c8276..28753805d2d1 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -10,11 +10,6 @@ * * This file contains the ARM-specific time handling details: * reading the RTC at bootup, etc... - * - * 1994-07-02 Alan Modra - * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime - * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include <linux/module.h> #include <linux/kernel.h> @@ -77,11 +72,6 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); #endif -/* - * hook for setting the RTC's idea of the current time. - */ -int (*set_rtc)(void); - #ifndef CONFIG_GENERIC_TIME static unsigned long dummy_gettimeoffset(void) { @@ -89,140 +79,6 @@ static unsigned long dummy_gettimeoffset(void) } #endif -static unsigned long next_rtc_update; - -/* - * If we have an externally synchronized linux clock, then update - * CMOS clock accordingly every ~11 minutes. set_rtc() has to be - * called as close as possible to 500 ms before the new second - * starts. - */ -static inline void do_set_rtc(void) -{ - if (!ntp_synced() || set_rtc == NULL) - return; - - if (next_rtc_update && - time_before((unsigned long)xtime.tv_sec, next_rtc_update)) - return; - - if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) && - xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1)) - return; - - if (set_rtc()) - /* - * rtc update failed. Try again in 60s - */ - next_rtc_update = xtime.tv_sec + 60; - else - next_rtc_update = xtime.tv_sec + 660; -} - -#ifdef CONFIG_LEDS - -static void dummy_leds_event(led_event_t evt) -{ -} - -void (*leds_event)(led_event_t) = dummy_leds_event; - -struct leds_evt_name { - const char name[8]; - int on; - int off; -}; - -static const struct leds_evt_name evt_names[] = { - { "amber", led_amber_on, led_amber_off }, - { "blue", led_blue_on, led_blue_off }, - { "green", led_green_on, led_green_off }, - { "red", led_red_on, led_red_off }, -}; - -static ssize_t leds_store(struct sys_device *dev, - struct sysdev_attribute *attr, - const char *buf, size_t size) -{ - int ret = -EINVAL, len = strcspn(buf, " "); - - if (len > 0 && buf[len] == '\0') - len--; - - if (strncmp(buf, "claim", len) == 0) { - leds_event(led_claim); - ret = size; - } else if (strncmp(buf, "release", len) == 0) { - leds_event(led_release); - ret = size; - } else { - int i; - - for (i = 0; i < ARRAY_SIZE(evt_names); i++) { - if (strlen(evt_names[i].name) != len || - strncmp(buf, evt_names[i].name, len) != 0) - continue; - if (strncmp(buf+len, " on", 3) == 0) { - leds_event(evt_names[i].on); - ret = size; - } else if (strncmp(buf+len, " off", 4) == 0) { - leds_event(evt_names[i].off); - ret = size; - } - break; - } - } - return ret; -} - -static SYSDEV_ATTR(event, 0200, NULL, leds_store); - -static int leds_suspend(struct sys_device *dev, pm_message_t state) -{ - leds_event(led_stop); - return 0; -} - -static int leds_resume(struct sys_device *dev) -{ - leds_event(led_start); - return 0; -} - -static int leds_shutdown(struct sys_device *dev) -{ - leds_event(led_halted); - return 0; -} - -static struct sysdev_class leds_sysclass = { - .name = "leds", - .shutdown = leds_shutdown, - .suspend = leds_suspend, - .resume = leds_resume, -}; - -static struct sys_device leds_device = { - .id = 0, - .cls = &leds_sysclass, -}; - -static int __init leds_init(void) -{ - int ret; - ret = sysdev_class_register(&leds_sysclass); - if (ret == 0) - ret = sysdev_register(&leds_device); - if (ret == 0) - ret = sysdev_create_file(&leds_device, &attr_event); - return ret; -} - -device_initcall(leds_init); - -EXPORT_SYMBOL(leds_event); -#endif - #ifdef CONFIG_LEDS_TIMER static inline void do_leds(void) { @@ -295,39 +151,6 @@ int do_settimeofday(struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); #endif /* !CONFIG_GENERIC_TIME */ -/** - * save_time_delta - Save the offset between system time and RTC time - * @delta: pointer to timespec to store delta - * @rtc: pointer to timespec for current RTC time - * - * Return a delta between the system time and the RTC time, such - * that system time can be restored later with restore_time_delta() - */ -void save_time_delta(struct timespec *delta, struct timespec *rtc) -{ - set_normalized_timespec(delta, - xtime.tv_sec - rtc->tv_sec, - xtime.tv_nsec - rtc->tv_nsec); -} -EXPORT_SYMBOL(save_time_delta); - -/** - * restore_time_delta - Restore the current system time - * @delta: delta returned by save_time_delta() - * @rtc: pointer to timespec for current RTC time - */ -void restore_time_delta(struct timespec *delta, struct timespec *rtc) -{ - struct timespec ts; - - set_normalized_timespec(&ts, - delta->tv_sec + rtc->tv_sec, - delta->tv_nsec + rtc->tv_nsec); - - do_settimeofday(&ts); -} -EXPORT_SYMBOL(restore_time_delta); - #ifndef CONFIG_GENERIC_CLOCKEVENTS /* * Kernel system timer support. @@ -336,7 +159,6 @@ void timer_tick(void) { profile_tick(CPU_PROFILING); do_leds(); - do_set_rtc(); write_seqlock(&xtime_lock); do_timer(1); write_sequnlock(&xtime_lock); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 3f361a783f43..707e82418885 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -21,6 +21,9 @@ #include <linux/hardirq.h> #include <linux/init.h> #include <linux/uaccess.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/notifier.h> #include <asm/atomic.h> #include <asm/cacheflush.h> @@ -254,6 +257,8 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) { struct thread_info *thread = current_thread_info(); + kgdb_die_hook(DIE_OOPS, str, regs, err); + oops_enter(); spin_lock_irq(&die_lock); diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 4957e13ef55b..b16c07914b55 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -43,10 +43,6 @@ SECTIONS INIT_SETUP(16) - __early_begin = .; - *(.early_param.init) - __early_end = .; - INIT_CALLS CON_INITCALL SECURITY_INITCALL diff --git a/arch/arm/mach-bcmring/core.c b/arch/arm/mach-bcmring/core.c index e590bbe0a7b4..72e405df0fb0 100644 --- a/arch/arm/mach-bcmring/core.c +++ b/arch/arm/mach-bcmring/core.c @@ -142,8 +142,7 @@ void __init bcmring_amba_init(void) chipcHw_busInterfaceClockEnable(bus_clock); - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 033bfede6b67..0ebe185610bf 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -91,10 +91,14 @@ config MACH_DAVINCI_DM6467_EVM bool "TI DM6467 EVM" default ARCH_DAVINCI_DM646x depends on ARCH_DAVINCI_DM646x + select MACH_DAVINCI_DM6467TEVM help Configure this option to specify the whether the board used for development is a DM6467 EVM +config MACH_DAVINCI_DM6467TEVM + bool + config MACH_DAVINCI_DM365_EVM bool "TI DM365 EVM" default ARCH_DAVINCI_DM365 diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index eeb9230d8844..6aac880eb794 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -26,7 +26,7 @@ obj-$(CONFIG_MACH_SFFSDR) += board-sffsdr.o obj-$(CONFIG_MACH_NEUROS_OSD2) += board-neuros-osd2.o obj-$(CONFIG_MACH_DAVINCI_DM355_EVM) += board-dm355-evm.o obj-$(CONFIG_MACH_DM355_LEOPARD) += board-dm355-leopard.o -obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o +obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o cdce949.o obj-$(CONFIG_MACH_DAVINCI_DM365_EVM) += board-dm365-evm.o obj-$(CONFIG_MACH_DAVINCI_DA830_EVM) += board-da830-evm.o obj-$(CONFIG_MACH_DAVINCI_DA850_EVM) += board-da850-evm.o @@ -34,3 +34,4 @@ obj-$(CONFIG_MACH_DAVINCI_DA850_EVM) += board-da850-evm.o # Power Management obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o +obj-$(CONFIG_SUSPEND) += pm.o sleep.o diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 31dc9901e556..dc19870b23cd 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -112,7 +112,7 @@ static __init void da830_evm_usb_init(void) * Set up USB clock/mode in the CFGCHIP2 register. * FYI: CFGCHIP2 is 0x0000ef00 initially. */ - cfgchip2 = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG)); + cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); /* USB2.0 PHY reference clock is 24 MHz */ cfgchip2 &= ~CFGCHIP2_REFFREQ; @@ -139,7 +139,7 @@ static __init void da830_evm_usb_init(void) cfgchip2 |= CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN; #endif - __raw_writel(cfgchip2, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG)); + __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); /* USB_REFCLKIN is not used. */ ret = davinci_cfg_reg(DA830_USB0_DRVVBUS); diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 07de8db14581..411284d0b0fa 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -46,8 +46,20 @@ static struct mtd_partition da850_evm_norflash_partition[] = { { - .name = "NOR filesystem", + .name = "bootloaders + env", .offset = 0, + .size = SZ_512K, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0, + }, + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, .mask_flags = 0, }, @@ -77,6 +89,18 @@ static struct platform_device da850_evm_norflash_device = { .resource = da850_evm_norflash_resource, }; +static struct davinci_pm_config da850_pm_pdata = { + .sleepcount = 128, +}; + +static struct platform_device da850_pm_device = { + .name = "pm-davinci", + .dev = { + .platform_data = &da850_pm_pdata, + }, + .id = -1, +}; + /* DA850/OMAP-L138 EVM includes a 512 MByte large-page NAND flash * (128K blocks). It may be used instead of the (default) SPI flash * to boot, using TI's tools to install the secondary boot loader @@ -119,6 +143,7 @@ static struct davinci_nand_pdata da850_evm_nandflash_data = { .parts = da850_evm_nandflash_partition, .nr_parts = ARRAY_SIZE(da850_evm_nandflash_partition), .ecc_mode = NAND_ECC_HW, + .ecc_bits = 4, .options = NAND_USE_FLASH_BBT, }; @@ -537,7 +562,7 @@ static int __init da850_evm_config_emac(void) if (!machine_is_davinci_da850_evm()) return 0; - cfg_chip3_base = DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG); + cfg_chip3_base = DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG); val = __raw_readl(cfg_chip3_base); @@ -696,6 +721,11 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: cpuidle registration failed: %d\n", ret); + + ret = da850_register_pm(&da850_pm_device); + if (ret) + pr_warning("da850_evm_init: suspend registration failed: %d\n", + ret); } #ifdef CONFIG_SERIAL_8250_CONSOLE diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index a9b650dcc172..077ecf4fecda 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -236,6 +236,7 @@ static struct vpfe_subdev_info vpfe_sub_devs[] = { static struct vpfe_config vpfe_cfg = { .num_subdevs = ARRAY_SIZE(vpfe_sub_devs), + .i2c_adapter_id = 1, .sub_devs = vpfe_sub_devs, .card_name = "DM355 EVM", .ccdc = "DM355 CCDC", diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 289fe1b7d25a..b476395d2cd4 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -192,7 +192,11 @@ static struct davinci_i2c_platform_data i2c_pdata = { .bus_delay = 0 /* usec */, }; -#ifdef CONFIG_KEYBOARD_DAVINCI +static int dm365evm_keyscan_enable(struct device *dev) +{ + return davinci_cfg_reg(DM365_KEYSCAN); +} + static unsigned short dm365evm_keymap[] = { KEY_KP2, KEY_LEFT, @@ -214,6 +218,7 @@ static unsigned short dm365evm_keymap[] = { }; static struct davinci_ks_platform_data dm365evm_ks_data = { + .device_enable = dm365evm_keyscan_enable, .keymap = dm365evm_keymap, .keymapsize = ARRAY_SIZE(dm365evm_keymap), .rep = 1, @@ -222,7 +227,6 @@ static struct davinci_ks_platform_data dm365evm_ks_data = { .interval = 0x2, .matrix_type = DAVINCI_KEYSCAN_MATRIX_4X4, }; -#endif static int cpld_mmc_get_cd(int module) { @@ -511,10 +515,7 @@ static __init void dm365_evm_init(void) dm365_init_asp(&dm365_evm_snd_data); dm365_init_rtc(); - -#ifdef CONFIG_KEYBOARD_DAVINCI dm365_init_ks(&dm365evm_ks_data); -#endif } static __init void dm365_evm_irq_init(void) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index fd0398bc6db3..e9612cf727b7 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -247,6 +247,7 @@ static struct vpfe_subdev_info vpfe_sub_devs[] = { static struct vpfe_config vpfe_cfg = { .num_subdevs = ARRAY_SIZE(vpfe_sub_devs), + .i2c_adapter_id = 1, .sub_devs = vpfe_sub_devs, .card_name = "DM6446 EVM", .ccdc = "DM6446 CCDC", diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 8d0b0e01c59b..815067b2413e 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -30,6 +30,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/clk.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -39,54 +40,13 @@ #include <mach/serial.h> #include <mach/i2c.h> #include <mach/nand.h> +#include <mach/clock.h> +#include <mach/cdce949.h> -#if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \ - defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE) -#define HAS_ATA 1 -#else -#define HAS_ATA 0 -#endif - -#define DAVINCI_ASYNC_EMIF_CONTROL_BASE 0x20008000 -#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE 0x42000000 +#include "clock.h" #define NAND_BLOCK_SIZE SZ_128K -/* CPLD Register 0 bits to control ATA */ -#define DM646X_EVM_ATA_RST BIT(0) -#define DM646X_EVM_ATA_PWD BIT(1) - -#define DM646X_EVM_PHY_MASK (0x2) -#define DM646X_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */ - -#define VIDCLKCTL_OFFSET (DAVINCI_SYSTEM_MODULE_BASE + 0x38) -#define VSCLKDIS_OFFSET (DAVINCI_SYSTEM_MODULE_BASE + 0x6c) -#define VCH2CLK_MASK (BIT_MASK(10) | BIT_MASK(9) | BIT_MASK(8)) -#define VCH2CLK_SYSCLK8 (BIT(9)) -#define VCH2CLK_AUXCLK (BIT(9) | BIT(8)) -#define VCH3CLK_MASK (BIT_MASK(14) | BIT_MASK(13) | BIT_MASK(12)) -#define VCH3CLK_SYSCLK8 (BIT(13)) -#define VCH3CLK_AUXCLK (BIT(14) | BIT(13)) - -#define VIDCH2CLK (BIT(10)) -#define VIDCH3CLK (BIT(11)) -#define VIDCH1CLK (BIT(4)) -#define TVP7002_INPUT (BIT(4)) -#define TVP5147_INPUT (~BIT(4)) -#define VPIF_INPUT_ONE_CHANNEL (BIT(5)) -#define VPIF_INPUT_TWO_CHANNEL (~BIT(5)) -#define TVP5147_CH0 "tvp514x-0" -#define TVP5147_CH1 "tvp514x-1" - -static void __iomem *vpif_vidclkctl_reg; -static void __iomem *vpif_vsclkdis_reg; -/* spin lock for updating above registers */ -static spinlock_t vpif_reg_lock; - -static struct davinci_uart_config uart_config __initdata = { - .enabled_uarts = (1 << 0), -}; - /* Note: We are setting first partition as 'bootloader' constituting UBL, U-Boot * and U-Boot environment this avoids dependency on any particular combination * of UBL, U-Boot or flashing tools etc. @@ -120,6 +80,9 @@ static struct davinci_nand_pdata davinci_nand_data = { .options = 0, }; +#define DAVINCI_ASYNC_EMIF_CONTROL_BASE 0x20008000 +#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE 0x42000000 + static struct resource davinci_nand_resources[] = { { .start = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE, @@ -144,6 +107,17 @@ static struct platform_device davinci_nand_device = { }, }; +#if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \ + defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE) +#define HAS_ATA 1 +#else +#define HAS_ATA 0 +#endif + +/* CPLD Register 0 bits to control ATA */ +#define DM646X_EVM_ATA_RST BIT(0) +#define DM646X_EVM_ATA_PWD BIT(1) + /* CPLD Register 0 Client: used for I/O Control */ static int cpld_reg0_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -417,6 +391,9 @@ static struct i2c_board_info __initdata i2c_info[] = { { I2C_BOARD_INFO("cpld_video", 0x3b), }, + { + I2C_BOARD_INFO("cdce949", 0x6c), + }, }; static struct davinci_i2c_platform_data i2c_pdata = { @@ -424,6 +401,30 @@ static struct davinci_i2c_platform_data i2c_pdata = { .bus_delay = 0 /* usec */, }; +#define VIDCLKCTL_OFFSET (DAVINCI_SYSTEM_MODULE_BASE + 0x38) +#define VSCLKDIS_OFFSET (DAVINCI_SYSTEM_MODULE_BASE + 0x6c) +#define VCH2CLK_MASK (BIT_MASK(10) | BIT_MASK(9) | BIT_MASK(8)) +#define VCH2CLK_SYSCLK8 (BIT(9)) +#define VCH2CLK_AUXCLK (BIT(9) | BIT(8)) +#define VCH3CLK_MASK (BIT_MASK(14) | BIT_MASK(13) | BIT_MASK(12)) +#define VCH3CLK_SYSCLK8 (BIT(13)) +#define VCH3CLK_AUXCLK (BIT(14) | BIT(13)) + +#define VIDCH2CLK (BIT(10)) +#define VIDCH3CLK (BIT(11)) +#define VIDCH1CLK (BIT(4)) +#define TVP7002_INPUT (BIT(4)) +#define TVP5147_INPUT (~BIT(4)) +#define VPIF_INPUT_ONE_CHANNEL (BIT(5)) +#define VPIF_INPUT_TWO_CHANNEL (~BIT(5)) +#define TVP5147_CH0 "tvp514x-0" +#define TVP5147_CH1 "tvp514x-1" + +static void __iomem *vpif_vidclkctl_reg; +static void __iomem *vpif_vsclkdis_reg; +/* spin lock for updating above registers */ +static spinlock_t vpif_reg_lock; + static int set_vpif_clock(int mux_mode, int hd) { unsigned long flags; @@ -685,11 +686,44 @@ static void __init evm_init_i2c(void) evm_init_video(); } +#define CDCE949_XIN_RATE 27000000 + +/* CDCE949 support - "lpsc" field is overridden to work as clock number */ +static struct clk cdce_clk_in = { + .name = "cdce_xin", + .rate = CDCE949_XIN_RATE, +}; + +static struct davinci_clk cdce_clks[] = { + CLK(NULL, "xin", &cdce_clk_in), + CLK(NULL, NULL, NULL), +}; + +static void __init cdce_clk_init(void) +{ + struct davinci_clk *c; + struct clk *clk; + + for (c = cdce_clks; c->lk.clk; c++) { + clk = c->lk.clk; + clkdev_add(&c->lk); + clk_register(clk); + } +} + static void __init davinci_map_io(void) { dm646x_init(); + cdce_clk_init(); } +static struct davinci_uart_config uart_config __initdata = { + .enabled_uarts = (1 << 0), +}; + +#define DM646X_EVM_PHY_MASK (0x2) +#define DM646X_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */ + static __init void evm_init(void) { struct davinci_soc_info *soc_info = &davinci_soc_info; @@ -713,6 +747,17 @@ static __init void davinci_dm646x_evm_irq_init(void) davinci_irq_init(); } +#define DM646X_EVM_REF_FREQ 27000000 +#define DM6467T_EVM_REF_FREQ 33000000 + +void __init dm646x_board_setup_refclk(struct clk *clk) +{ + if (machine_is_davinci_dm6467tevm()) + clk->rate = DM6467T_EVM_REF_FREQ; + else + clk->rate = DM646X_EVM_REF_FREQ; +} + MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .phys_io = IO_PHYS, .io_pg_offst = (__IO_ADDRESS(IO_PHYS) >> 18) & 0xfffc, @@ -723,3 +768,13 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .init_machine = evm_init, MACHINE_END +MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") + .phys_io = IO_PHYS, + .io_pg_offst = (__IO_ADDRESS(IO_PHYS) >> 18) & 0xfffc, + .boot_params = (0x80000100), + .map_io = davinci_map_io, + .init_irq = davinci_dm646x_evm_irq_init, + .timer = &davinci_timer, + .init_machine = evm_init, +MACHINE_END + diff --git a/arch/arm/mach-davinci/cdce949.c b/arch/arm/mach-davinci/cdce949.c new file mode 100644 index 000000000000..6af3289e0527 --- /dev/null +++ b/arch/arm/mach-davinci/cdce949.c @@ -0,0 +1,289 @@ +/* + * TI CDCE949 clock synthesizer driver + * + * Note: This implementation assumes an input of 27MHz to the CDCE. + * This is by no means constrained by CDCE hardware although the datasheet + * does use this as an example for all illustrations and more importantly: + * that is the crystal input on boards it is currently used on. + * + * Copyright (C) 2009 Texas Instruments Incorporated. http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> + +#include <mach/clock.h> + +#include "clock.h" + +static struct i2c_client *cdce_i2c_client; + +/* CDCE register descriptor */ +struct cdce_reg { + u8 addr; + u8 val; +}; + +/* Per-Output (Y1, Y2 etc.) frequency descriptor */ +struct cdce_freq { + /* Frequency in KHz */ + unsigned long frequency; + /* + * List of registers to program to obtain a particular frequency. + * 0x0 in register address and value is the end of list marker. + */ + struct cdce_reg *reglist; +}; + +#define CDCE_FREQ_TABLE_ENTRY(line, out) \ +{ \ + .reglist = cdce_y ##line## _ ##out, \ + .frequency = out, \ +} + +/* List of CDCE outputs */ +struct cdce_output { + /* List of frequencies on this output */ + struct cdce_freq *freq_table; + /* Number of possible frequencies */ + int size; +}; + +/* + * Finding out the values to program into CDCE949 registers for a particular + * frequency output is not a simple calculation. Have a look at the datasheet + * for the details. There is desktop software available to help users with + * the calculations. Here, we just depend on the output of that software + * (or hand calculations) instead trying to runtime calculate the register + * values and inflicting misery on ourselves. + */ +static struct cdce_reg cdce_y1_148500[] = { + { 0x13, 0x00 }, + /* program PLL1_0 multiplier */ + { 0x18, 0xaf }, + { 0x19, 0x50 }, + { 0x1a, 0x02 }, + { 0x1b, 0xc9 }, + /* program PLL1_11 multiplier */ + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1e, 0x02 }, + { 0x1f, 0xc9 }, + /* output state selection */ + { 0x15, 0x00 }, + { 0x14, 0xef }, + /* switch MUX to PLL1 output */ + { 0x14, 0x6f }, + { 0x16, 0x06 }, + /* set P2DIV divider, P3DIV and input crystal */ + { 0x17, 0x06 }, + { 0x01, 0x00 }, + { 0x05, 0x48 }, + { 0x02, 0x80 }, + /* enable and disable PLL */ + { 0x02, 0xbc }, + { 0x03, 0x01 }, + { }, +}; + +static struct cdce_reg cdce_y1_74250[] = { + { 0x13, 0x00 }, + { 0x18, 0xaf }, + { 0x19, 0x50 }, + { 0x1a, 0x02 }, + { 0x1b, 0xc9 }, + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1e, 0x02 }, + { 0x1f, 0xc9 }, + /* output state selection */ + { 0x15, 0x00 }, + { 0x14, 0xef }, + /* switch MUX to PLL1 output */ + { 0x14, 0x6f }, + { 0x16, 0x06 }, + /* set P2DIV divider, P3DIV and input crystal */ + { 0x17, 0x06 }, + { 0x01, 0x00 }, + { 0x05, 0x48 }, + { 0x02, 0x80 }, + /* enable and disable PLL */ + { 0x02, 0xbc }, + { 0x03, 0x02 }, + { }, +}; + +static struct cdce_reg cdce_y1_27000[] = { + { 0x13, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x40 }, + { 0x1a, 0x02 }, + { 0x1b, 0x08 }, + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1e, 0x02 }, + { 0x1f, 0x08 }, + { 0x15, 0x02 }, + { 0x14, 0xed }, + { 0x16, 0x01 }, + { 0x17, 0x01 }, + { 0x01, 0x00 }, + { 0x05, 0x50 }, + { 0x02, 0xb4 }, + { 0x03, 0x01 }, + { }, +}; + +static struct cdce_freq cdce_y1_freqs[] = { + CDCE_FREQ_TABLE_ENTRY(1, 148500), + CDCE_FREQ_TABLE_ENTRY(1, 74250), + CDCE_FREQ_TABLE_ENTRY(1, 27000), +}; + +static struct cdce_reg cdce_y5_13500[] = { + { 0x27, 0x08 }, + { 0x28, 0x00 }, + { 0x29, 0x40 }, + { 0x2a, 0x02 }, + { 0x2b, 0x08 }, + { 0x24, 0x6f }, + { }, +}; + +static struct cdce_reg cdce_y5_16875[] = { + { 0x27, 0x08 }, + { 0x28, 0x9f }, + { 0x29, 0xb0 }, + { 0x2a, 0x02 }, + { 0x2b, 0x89 }, + { 0x24, 0x6f }, + { }, +}; + +static struct cdce_reg cdce_y5_27000[] = { + { 0x27, 0x04 }, + { 0x28, 0x00 }, + { 0x29, 0x40 }, + { 0x2a, 0x02 }, + { 0x2b, 0x08 }, + { 0x24, 0x6f }, + { }, +}; +static struct cdce_reg cdce_y5_54000[] = { + { 0x27, 0x04 }, + { 0x28, 0xff }, + { 0x29, 0x80 }, + { 0x2a, 0x02 }, + { 0x2b, 0x07 }, + { 0x24, 0x6f }, + { }, +}; + +static struct cdce_reg cdce_y5_81000[] = { + { 0x27, 0x02 }, + { 0x28, 0xbf }, + { 0x29, 0xa0 }, + { 0x2a, 0x03 }, + { 0x2b, 0x0a }, + { 0x24, 0x6f }, + { }, +}; + +static struct cdce_freq cdce_y5_freqs[] = { + CDCE_FREQ_TABLE_ENTRY(5, 13500), + CDCE_FREQ_TABLE_ENTRY(5, 16875), + CDCE_FREQ_TABLE_ENTRY(5, 27000), + CDCE_FREQ_TABLE_ENTRY(5, 54000), + CDCE_FREQ_TABLE_ENTRY(5, 81000), +}; + + +static struct cdce_output output_list[] = { + [1] = { cdce_y1_freqs, ARRAY_SIZE(cdce_y1_freqs) }, + [5] = { cdce_y5_freqs, ARRAY_SIZE(cdce_y5_freqs) }, +}; + +int cdce_set_rate(struct clk *clk, unsigned long rate) +{ + int i, ret = 0; + struct cdce_freq *freq_table = output_list[clk->lpsc].freq_table; + struct cdce_reg *regs = NULL; + + if (!cdce_i2c_client) + return -ENODEV; + + if (!freq_table) + return -EINVAL; + + for (i = 0; i < output_list[clk->lpsc].size; i++) { + if (freq_table[i].frequency == rate / 1000) { + regs = freq_table[i].reglist; + break; + } + } + + if (!regs) + return -EINVAL; + + for (i = 0; regs[i].addr; i++) { + ret = i2c_smbus_write_byte_data(cdce_i2c_client, + regs[i].addr | 0x80, regs[i].val); + if (ret) + return ret; + } + + clk->rate = rate; + + return 0; +} + +static int cdce_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + cdce_i2c_client = client; + return 0; +} + +static int __devexit cdce_remove(struct i2c_client *client) +{ + cdce_i2c_client = NULL; + return 0; +} + +static const struct i2c_device_id cdce_id[] = { + {"cdce949", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, cdce_id); + +static struct i2c_driver cdce_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cdce949", + }, + .probe = cdce_probe, + .remove = __devexit_p(cdce_remove), + .id_table = cdce_id, +}; + +static int __init cdce_init(void) +{ + return i2c_add_driver(&cdce_driver); +} +subsys_initcall(cdce_init); + +static void __exit cdce_exit(void) +{ + i2c_del_driver(&cdce_driver); +} +module_exit(cdce_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("CDCE949 clock synthesizer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index baece65cb9c0..123839332d50 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -49,7 +49,8 @@ static void __clk_disable(struct clk *clk) { if (WARN_ON(clk->usecount == 0)) return; - if (--clk->usecount == 0 && !(clk->flags & CLK_PLL)) + if (--clk->usecount == 0 && !(clk->flags & CLK_PLL) && + (clk->flags & CLK_PSC)) davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 0); if (clk->parent) __clk_disable(clk->parent); @@ -376,7 +377,7 @@ int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, locktime = ((2000 * prediv) / 100); prediv = (prediv - 1) | PLLDIV_EN; } else { - locktime = 20; + locktime = PLL_LOCK_TIME; } if (postdiv) postdiv = (postdiv - 1) | PLLDIV_EN; @@ -389,12 +390,7 @@ int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); __raw_writel(ctrl, pll->base + PLLCTL); - /* - * Wait for 4 OSCIN/CLKIN cycles to ensure that the PLLC has switched - * to bypass mode. Delay of 1us ensures we are good for all > 4MHz - * OSCIN/CLKIN inputs. Typically the input is ~25MHz. - */ - udelay(1); + udelay(PLL_BYPASS_TIME); /* Reset and enable PLL */ ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS); @@ -408,11 +404,7 @@ int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, if (pll->flags & PLL_HAS_POSTDIV) __raw_writel(postdiv, pll->base + POSTDIV); - /* - * Wait for PLL to reset properly, OMAP-L138 datasheet says - * 'min' time = 125ns - */ - udelay(1); + udelay(PLL_RESET_TIME); /* Bring PLL out of reset */ ctrl |= PLLCTL_PLLRST; @@ -468,24 +460,10 @@ int __init davinci_clk_init(struct davinci_clk *clocks) return 0; } -#ifdef CONFIG_PROC_FS -#include <linux/proc_fs.h> -#include <linux/seq_file.h> - -static void *davinci_ck_start(struct seq_file *m, loff_t *pos) -{ - return *pos < 1 ? (void *)1 : NULL; -} +#ifdef CONFIG_DEBUG_FS -static void *davinci_ck_next(struct seq_file *m, void *v, loff_t *pos) -{ - ++*pos; - return NULL; -} - -static void davinci_ck_stop(struct seq_file *m, void *v) -{ -} +#include <linux/debugfs.h> +#include <linux/seq_file.h> #define CLKNAME_MAX 10 /* longest clock name */ #define NEST_DELTA 2 @@ -525,41 +503,38 @@ dump_clock(struct seq_file *s, unsigned nest, struct clk *parent) static int davinci_ck_show(struct seq_file *m, void *v) { - /* Show clock tree; we know the main oscillator is first. - * We trust nonzero usecounts equate to PSC enables... + struct clk *clk; + + /* + * Show clock tree; We trust nonzero usecounts equate to PSC enables... */ mutex_lock(&clocks_mutex); - if (!list_empty(&clocks)) - dump_clock(m, 0, list_first_entry(&clocks, struct clk, node)); + list_for_each_entry(clk, &clocks, node) + if (!clk->parent) + dump_clock(m, 0, clk); mutex_unlock(&clocks_mutex); return 0; } -static const struct seq_operations davinci_ck_op = { - .start = davinci_ck_start, - .next = davinci_ck_next, - .stop = davinci_ck_stop, - .show = davinci_ck_show -}; - static int davinci_ck_open(struct inode *inode, struct file *file) { - return seq_open(file, &davinci_ck_op); + return single_open(file, davinci_ck_show, NULL); } -static const struct file_operations proc_davinci_ck_operations = { +static const struct file_operations davinci_ck_operations = { .open = davinci_ck_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = single_release, }; -static int __init davinci_ck_proc_init(void) +static int __init davinci_clk_debugfs_init(void) { - proc_create("davinci_clocks", 0, NULL, &proc_davinci_ck_operations); + debugfs_create_file("davinci_clocks", S_IFREG | S_IRUGO, NULL, NULL, + &davinci_ck_operations); return 0; } -__initcall(davinci_ck_proc_init); -#endif /* CONFIG_DEBUG_PROC_FS */ +device_initcall(davinci_clk_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index c92d77a3008d..31fb6eac712c 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -12,9 +12,6 @@ #ifndef __ARCH_ARM_DAVINCI_CLOCK_H #define __ARCH_ARM_DAVINCI_CLOCK_H -#include <linux/list.h> -#include <asm/clkdev.h> - #define DAVINCI_PLL1_BASE 0x01c40800 #define DAVINCI_PLL2_BASE 0x01c40c00 #define MAX_PLL 2 @@ -53,6 +50,26 @@ #define PLLDIV_EN BIT(15) #define PLLDIV_RATIO_MASK 0x1f +/* + * OMAP-L138 system reference guide recommends a wait for 4 OSCIN/CLKIN + * cycles to ensure that the PLLC has switched to bypass mode. Delay of 1us + * ensures we are good for all > 4MHz OSCIN/CLKIN inputs. Typically the input + * is ~25MHz. Units are micro seconds. + */ +#define PLL_BYPASS_TIME 1 +/* From OMAP-L138 datasheet table 6-4. Units are micro seconds */ +#define PLL_RESET_TIME 1 +/* + * From OMAP-L138 datasheet table 6-4; assuming prediv = 1, sqrt(pllm) = 4 + * Units are micro seconds. + */ +#define PLL_LOCK_TIME 20 + +#ifndef __ASSEMBLER__ + +#include <linux/list.h> +#include <asm/clkdev.h> + struct pll_data { u32 phys_base; void __iomem *base; @@ -109,3 +126,5 @@ int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, extern struct platform_device davinci_wdt_device; #endif + +#endif diff --git a/arch/arm/mach-davinci/common.c b/arch/arm/mach-davinci/common.c index c2de94cde56a..94f27cbcd55a 100644 --- a/arch/arm/mach-davinci/common.c +++ b/arch/arm/mach-davinci/common.c @@ -11,13 +11,13 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/etherdevice.h> +#include <linux/davinci_emac.h> #include <asm/tlb.h> #include <asm/mach/map.h> #include <mach/common.h> #include <mach/cputype.h> -#include <mach/emac.h> #include "clock.h" diff --git a/arch/arm/mach-davinci/cp_intc.c b/arch/arm/mach-davinci/cp_intc.c index 52b287cf3a42..37311d1830eb 100644 --- a/arch/arm/mach-davinci/cp_intc.c +++ b/arch/arm/mach-davinci/cp_intc.c @@ -81,12 +81,23 @@ static int cp_intc_set_irq_type(unsigned int irq, unsigned int flow_type) return 0; } +/* + * Faking this allows us to to work with suspend functions of + * generic drivers which call {enable|disable}_irq_wake for + * wake up interrupt sources (eg RTC on DA850). + */ +static int cp_intc_set_wake(unsigned int irq, unsigned int on) +{ + return 0; +} + static struct irq_chip cp_intc_irq_chip = { .name = "cp_intc", .ack = cp_intc_ack_irq, .mask = cp_intc_mask_irq, .unmask = cp_intc_unmask_irq, .set_type = cp_intc_set_irq_type, + .set_wake = cp_intc_set_wake, }; void __init cp_intc_init(void __iomem *base, unsigned short num_irq, diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c index 97a90f36fc92..bd59f31b8a95 100644 --- a/arch/arm/mach-davinci/cpuidle.c +++ b/arch/arm/mach-davinci/cpuidle.c @@ -19,6 +19,7 @@ #include <asm/proc-fns.h> #include <mach/cpuidle.h> +#include <mach/memory.h> #define DAVINCI_CPUIDLE_MAX_STATES 2 @@ -39,10 +40,6 @@ static struct cpuidle_driver davinci_idle_driver = { static DEFINE_PER_CPU(struct cpuidle_device, davinci_cpuidle_device); static void __iomem *ddr2_reg_base; -#define DDR2_SDRCR_OFFSET 0xc -#define DDR2_SRPD_BIT BIT(23) -#define DDR2_LPMODEN_BIT BIT(31) - static void davinci_save_ddr_power(int enter, bool pdown) { u32 val; @@ -109,8 +106,6 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) int ret; struct cpuidle_device *device; struct davinci_cpuidle_config *pdata = pdev->dev.platform_data; - struct resource *ddr2_regs; - resource_size_t len; device = &per_cpu(davinci_cpuidle_device, smp_processor_id()); @@ -119,28 +114,12 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) return -ENOENT; } - ddr2_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ddr2_regs) { - dev_err(&pdev->dev, "cannot get DDR2 controller register base"); - return -ENODEV; - } - - len = resource_size(ddr2_regs); - - ddr2_regs = request_mem_region(ddr2_regs->start, len, ddr2_regs->name); - if (!ddr2_regs) - return -EBUSY; - - ddr2_reg_base = ioremap(ddr2_regs->start, len); - if (!ddr2_reg_base) { - ret = -ENOMEM; - goto ioremap_fail; - } + ddr2_reg_base = pdata->ddr2_ctlr_base; ret = cpuidle_register_driver(&davinci_idle_driver); if (ret) { dev_err(&pdev->dev, "failed to register driver\n"); - goto driver_register_fail; + return ret; } /* Wait for interrupt state */ @@ -167,18 +146,11 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev) ret = cpuidle_register_device(device); if (ret) { dev_err(&pdev->dev, "failed to register device\n"); - goto device_register_fail; + cpuidle_unregister_driver(&davinci_idle_driver); + return ret; } return 0; - -device_register_fail: - cpuidle_unregister_driver(&davinci_idle_driver); -driver_register_fail: - iounmap(ddr2_reg_base); -ioremap_fail: - release_mem_region(ddr2_regs->start, len); - return ret; } static struct platform_driver davinci_cpuidle_driver = { diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c index b22b5cf04250..54796050a2ff 100644 --- a/arch/arm/mach-davinci/da830.c +++ b/arch/arm/mach-davinci/da830.c @@ -1208,13 +1208,13 @@ static struct davinci_soc_info davinci_soc_info_da830 = { void __init da830_init(void) { - da8xx_syscfg_base = ioremap(DA8XX_SYSCFG_BASE, SZ_4K); - if (WARN(!da8xx_syscfg_base, "Unable to map syscfg module")) + da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); + if (WARN(!da8xx_syscfg0_base, "Unable to map syscfg0 module")) return; davinci_soc_info_da830.jtag_id_base = - DA8XX_SYSCFG_VIRT(DA8XX_JTAG_ID_REG); - davinci_soc_info_da830.pinmux_base = DA8XX_SYSCFG_VIRT(0x120); + DA8XX_SYSCFG0_VIRT(DA8XX_JTAG_ID_REG); + davinci_soc_info_da830.pinmux_base = DA8XX_SYSCFG0_VIRT(0x120); davinci_common_init(&davinci_soc_info_da830); } diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 717806c6cef9..b9a7b3bc36b2 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -26,6 +26,7 @@ #include <mach/time.h> #include <mach/da8xx.h> #include <mach/cpufreq.h> +#include <mach/pm.h> #include "clock.h" #include "mux.h" @@ -40,6 +41,7 @@ #define DA850_REF_FREQ 24000000 #define CFGCHIP3_ASYNC3_CLKSRC BIT(4) +#define CFGCHIP3_PLL1_MASTER_LOCK BIT(5) #define CFGCHIP0_PLL_MASTER_LOCK BIT(4) static int da850_set_armrate(struct clk *clk, unsigned long rate); @@ -535,6 +537,7 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, GPIO2_15, 5, 0, 15, 8, false) MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) + MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) #endif }; @@ -770,6 +773,12 @@ static struct map_desc da850_io_desc[] = { .length = DA8XX_CP_INTC_SIZE, .type = MT_DEVICE }, + { + .virtual = SRAM_VIRT, + .pfn = __phys_to_pfn(DA8XX_ARM_RAM_BASE), + .length = SZ_8K, + .type = MT_DEVICE + }, }; static void __iomem *da850_psc_bases[] = { @@ -838,12 +847,12 @@ static void da850_set_async3_src(int pllnum) } } - v = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG)); + v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); if (pllnum) v |= CFGCHIP3_ASYNC3_CLKSRC; else v &= ~CFGCHIP3_ASYNC3_CLKSRC; - __raw_writel(v, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG)); + __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); } #ifdef CONFIG_CPU_FREQ @@ -987,7 +996,6 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) unsigned int prediv, mult, postdiv; struct da850_opp *opp; struct pll_data *pll = clk->pll_data; - unsigned int v; int ret; opp = (struct da850_opp *) da850_freq_table[index].index; @@ -995,11 +1003,6 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) mult = opp->mult; postdiv = opp->postdiv; - /* Unlock writing to PLL registers */ - v = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP0_REG)); - v &= ~CFGCHIP0_PLL_MASTER_LOCK; - __raw_writel(v, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP0_REG)); - ret = davinci_set_pllrate(pll, prediv, mult, postdiv); if (WARN_ON(ret)) return ret; @@ -1028,6 +1031,43 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) } #endif +int da850_register_pm(struct platform_device *pdev) +{ + int ret; + struct davinci_pm_config *pdata = pdev->dev.platform_data; + + ret = davinci_cfg_reg(DA850_RTC_ALARM); + if (ret) + return ret; + + pdata->ddr2_ctlr_base = da8xx_get_mem_ctlr(); + pdata->deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG); + pdata->ddrpsc_num = DA8XX_LPSC1_EMIF3C; + + pdata->cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K); + if (!pdata->cpupll_reg_base) + return -ENOMEM; + + pdata->ddrpll_reg_base = ioremap(DA8XX_PLL1_BASE, SZ_4K); + if (!pdata->ddrpll_reg_base) { + ret = -ENOMEM; + goto no_ddrpll_mem; + } + + pdata->ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K); + if (!pdata->ddrpsc_reg_base) { + ret = -ENOMEM; + goto no_ddrpsc_mem; + } + + return platform_device_register(pdev); + +no_ddrpsc_mem: + iounmap(pdata->ddrpll_reg_base); +no_ddrpll_mem: + iounmap(pdata->cpupll_reg_base); + return ret; +} static struct davinci_soc_info davinci_soc_info_da850 = { .io_desc = da850_io_desc, @@ -1049,17 +1089,25 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, + .sram_dma = DA8XX_ARM_RAM_BASE, + .sram_len = SZ_8K, }; void __init da850_init(void) { - da8xx_syscfg_base = ioremap(DA8XX_SYSCFG_BASE, SZ_4K); - if (WARN(!da8xx_syscfg_base, "Unable to map syscfg module")) + unsigned int v; + + da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); + if (WARN(!da8xx_syscfg0_base, "Unable to map syscfg0 module")) + return; + + da8xx_syscfg1_base = ioremap(DA8XX_SYSCFG1_BASE, SZ_4K); + if (WARN(!da8xx_syscfg1_base, "Unable to map syscfg1 module")) return; davinci_soc_info_da850.jtag_id_base = - DA8XX_SYSCFG_VIRT(DA8XX_JTAG_ID_REG); - davinci_soc_info_da850.pinmux_base = DA8XX_SYSCFG_VIRT(0x120); + DA8XX_SYSCFG0_VIRT(DA8XX_JTAG_ID_REG); + davinci_soc_info_da850.pinmux_base = DA8XX_SYSCFG0_VIRT(0x120); davinci_common_init(&davinci_soc_info_da850); @@ -1071,4 +1119,14 @@ void __init da850_init(void) * be any noticible change even in non-DVFS use cases. */ da850_set_async3_src(1); + + /* Unlock writing to PLL0 registers */ + v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG)); + v &= ~CFGCHIP0_PLL_MASTER_LOCK; + __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG)); + + /* Unlock writing to PLL1 registers */ + v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); + v &= ~CFGCHIP3_PLL1_MASTER_LOCK; + __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG)); } diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index dd2d32c4ce86..0a96791d3b0f 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -24,8 +24,10 @@ #include "clock.h" #define DA8XX_TPCC_BASE 0x01c00000 +#define DA850_TPCC1_BASE 0x01e30000 #define DA8XX_TPTC0_BASE 0x01c08000 #define DA8XX_TPTC1_BASE 0x01c08400 +#define DA850_TPTC2_BASE 0x01e38000 #define DA8XX_WDOG_BASE 0x01c21000 /* DA8XX_TIMER64P1_BASE */ #define DA8XX_I2C0_BASE 0x01c22000 #define DA8XX_RTC_BASE 0x01C23000 @@ -42,7 +44,8 @@ #define DA8XX_MDIO_REG_OFFSET 0x4000 #define DA8XX_EMAC_CTRL_RAM_SIZE SZ_8K -void __iomem *da8xx_syscfg_base; +void __iomem *da8xx_syscfg0_base; +void __iomem *da8xx_syscfg1_base; static struct plat_serial8250_port da8xx_serial_pdata[] = { { @@ -82,11 +85,6 @@ struct platform_device da8xx_serial_device = { }, }; -static const s8 da8xx_dma_chan_no_event[] = { - 20, 21, - -1 -}; - static const s8 da8xx_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, @@ -101,20 +99,52 @@ static const s8 da8xx_queue_priority_mapping[][2] = { {-1, -1} }; -static struct edma_soc_info da8xx_edma_info[] = { +static const s8 da850_queue_tc_mapping[][2] = { + /* {event queue no, TC no} */ + {0, 0}, + {-1, -1} +}; + +static const s8 da850_queue_priority_mapping[][2] = { + /* {event queue no, Priority} */ + {0, 3}, + {-1, -1} +}; + +static struct edma_soc_info da830_edma_info[] = { + { + .n_channel = 32, + .n_region = 4, + .n_slot = 128, + .n_tc = 2, + .n_cc = 1, + .queue_tc_mapping = da8xx_queue_tc_mapping, + .queue_priority_mapping = da8xx_queue_priority_mapping, + }, +}; + +static struct edma_soc_info da850_edma_info[] = { { .n_channel = 32, .n_region = 4, .n_slot = 128, .n_tc = 2, .n_cc = 1, - .noevent = da8xx_dma_chan_no_event, .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, }, + { + .n_channel = 32, + .n_region = 4, + .n_slot = 128, + .n_tc = 1, + .n_cc = 1, + .queue_tc_mapping = da850_queue_tc_mapping, + .queue_priority_mapping = da850_queue_priority_mapping, + }, }; -static struct resource da8xx_edma_resources[] = { +static struct resource da830_edma_resources[] = { { .name = "edma_cc0", .start = DA8XX_TPCC_BASE, @@ -145,19 +175,91 @@ static struct resource da8xx_edma_resources[] = { }, }; -static struct platform_device da8xx_edma_device = { +static struct resource da850_edma_resources[] = { + { + .name = "edma_cc0", + .start = DA8XX_TPCC_BASE, + .end = DA8XX_TPCC_BASE + SZ_32K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "edma_tc0", + .start = DA8XX_TPTC0_BASE, + .end = DA8XX_TPTC0_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "edma_tc1", + .start = DA8XX_TPTC1_BASE, + .end = DA8XX_TPTC1_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "edma_cc1", + .start = DA850_TPCC1_BASE, + .end = DA850_TPCC1_BASE + SZ_32K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "edma_tc2", + .start = DA850_TPTC2_BASE, + .end = DA850_TPTC2_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "edma0", + .start = IRQ_DA8XX_CCINT0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "edma0_err", + .start = IRQ_DA8XX_CCERRINT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "edma1", + .start = IRQ_DA850_CCINT1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "edma1_err", + .start = IRQ_DA850_CCERRINT1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device da830_edma_device = { .name = "edma", .id = -1, .dev = { - .platform_data = da8xx_edma_info, + .platform_data = da830_edma_info, }, - .num_resources = ARRAY_SIZE(da8xx_edma_resources), - .resource = da8xx_edma_resources, + .num_resources = ARRAY_SIZE(da830_edma_resources), + .resource = da830_edma_resources, +}; + +static struct platform_device da850_edma_device = { + .name = "edma", + .id = -1, + .dev = { + .platform_data = da850_edma_info, + }, + .num_resources = ARRAY_SIZE(da850_edma_resources), + .resource = da850_edma_resources, }; int __init da8xx_register_edma(void) { - return platform_device_register(&da8xx_edma_device); + struct platform_device *pdev; + + if (cpu_is_davinci_da830()) + pdev = &da830_edma_device; + else if (cpu_is_davinci_da850()) + pdev = &da850_edma_device; + else + return -ENODEV; + + return platform_device_register(pdev); } static struct resource da8xx_i2c_resources0[] = { @@ -481,11 +583,31 @@ static struct platform_device da8xx_rtc_device = { int da8xx_register_rtc(void) { + int ret; + /* Unlock the rtc's registers */ __raw_writel(0x83e70b13, IO_ADDRESS(DA8XX_RTC_BASE + 0x6c)); __raw_writel(0x95a4f1e0, IO_ADDRESS(DA8XX_RTC_BASE + 0x70)); - return platform_device_register(&da8xx_rtc_device); + ret = platform_device_register(&da8xx_rtc_device); + if (!ret) + /* Atleast on DA850, RTC is a wakeup source */ + device_init_wakeup(&da8xx_rtc_device.dev, true); + + return ret; +} + +static void __iomem *da8xx_ddr2_ctlr_base; +void __iomem * __init da8xx_get_mem_ctlr(void) +{ + if (da8xx_ddr2_ctlr_base) + return da8xx_ddr2_ctlr_base; + + da8xx_ddr2_ctlr_base = ioremap(DA8XX_DDR2_CTL_BASE, SZ_32K); + if (!da8xx_ddr2_ctlr_base) + pr_warning("%s: Unable to map DDR2 controller", __func__); + + return da8xx_ddr2_ctlr_base; } static struct resource da8xx_cpuidle_resources[] = { @@ -513,6 +635,7 @@ static struct platform_device da8xx_cpuidle_device = { int __init da8xx_register_cpuidle(void) { + da8xx_cpuidle_pdata.ddr2_ctlr_base = da8xx_get_mem_ctlr(); + return platform_device_register(&da8xx_cpuidle_device); } - diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index dedf4d4f3a27..b1185f82ffb3 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -564,13 +564,6 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 dma_chan_dm355_no_event[] = { - 12, 13, 24, 56, 57, - 58, 59, 60, 61, 62, - 63, - -1 -}; - static const s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ @@ -594,7 +587,6 @@ static struct edma_soc_info dm355_edma_info[] = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .noevent = dma_chan_dm355_no_event, .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, }, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 2ec619ec1657..b4a00c4a6e78 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -754,7 +754,7 @@ static struct edma_soc_info dm365_edma_info[] = { .n_cc = 1, .queue_tc_mapping = dm365_queue_tc_mapping, .queue_priority_mapping = dm365_queue_priority_mapping, - .default_queue = EVENTQ_2, + .default_queue = EVENTQ_3, }, }; @@ -993,7 +993,6 @@ void __init dm365_init_asp(struct snd_platform_data *pdata) void __init dm365_init_ks(struct davinci_ks_platform_data *pdata) { - davinci_cfg_reg(DM365_KEYSCAN); dm365_ks_device.dev.platform_data = pdata; platform_device_register(&dm365_ks_device); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 2cd008156dea..fc060e7aefdf 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -479,15 +479,6 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 dma_chan_dm644x_no_event[] = { - 0, 1, 12, 13, 14, - 15, 25, 30, 31, 45, - 46, 47, 55, 56, 57, - 58, 59, 60, 61, 62, - 63, - -1 -}; - static const s8 queue_tc_mapping[][2] = { /* {event queue no, TC no} */ @@ -511,7 +502,6 @@ static struct edma_soc_info dm644x_edma_info[] = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .noevent = dma_chan_dm644x_no_event, .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, }, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 829a44bcf799..7eb34e9253c6 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -42,7 +42,6 @@ /* * Device specific clocks */ -#define DM646X_REF_FREQ 27000000 #define DM646X_AUX_FREQ 24000000 static struct pll_data pll1_data = { @@ -57,7 +56,6 @@ static struct pll_data pll2_data = { static struct clk ref_clk = { .name = "ref_clk", - .rate = DM646X_REF_FREQ, }; static struct clk aux_clkin = { @@ -513,14 +511,6 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static const s8 dma_chan_dm646x_no_event[] = { - 0, 1, 2, 3, 13, - 14, 15, 24, 25, 26, - 27, 30, 31, 54, 55, - 56, - -1 -}; - /* Four Transfer Controllers on DM646x */ static const s8 dm646x_queue_tc_mapping[][2] = { @@ -549,7 +539,6 @@ static struct edma_soc_info dm646x_edma_info[] = { .n_slot = 512, .n_tc = 4, .n_cc = 1, - .noevent = dma_chan_dm646x_no_event, .queue_tc_mapping = dm646x_queue_tc_mapping, .queue_priority_mapping = dm646x_queue_priority_mapping, }, @@ -925,6 +914,7 @@ void dm646x_setup_vpif(struct vpif_display_config *display_config, void __init dm646x_init(void) { + dm646x_board_setup_refclk(&ref_clk); davinci_common_init(&davinci_soc_info_dm646x); } diff --git a/arch/arm/mach-davinci/dma.c b/arch/arm/mach-davinci/dma.c index 648fbb760ae1..15dd886df04c 100644 --- a/arch/arm/mach-davinci/dma.c +++ b/arch/arm/mach-davinci/dma.c @@ -226,11 +226,11 @@ struct edma { */ DECLARE_BITMAP(edma_inuse, EDMA_MAX_PARAMENTRY); - /* The edma_noevent bit for each channel is clear unless - * it doesn't trigger DMA events on this platform. It uses a - * bit of SOC-specific initialization code. + /* The edma_unused bit for each channel is clear unless + * it is not being used on this platform. It uses a bit + * of SOC-specific initialization code. */ - DECLARE_BITMAP(edma_noevent, EDMA_MAX_DMACH); + DECLARE_BITMAP(edma_unused, EDMA_MAX_DMACH); unsigned irq_res_start; unsigned irq_res_end; @@ -243,6 +243,7 @@ struct edma { }; static struct edma *edma_info[EDMA_MAX_CC]; +static int arch_num_cc; /* dummy param set used to (re)initialize parameter RAM slots */ static const struct edmacc_param dummy_paramset = { @@ -555,8 +556,27 @@ static int reserve_contiguous_slots(int ctlr, unsigned int id, return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1); } +static int prepare_unused_channel_list(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int i, ctlr; + + for (i = 0; i < pdev->num_resources; i++) { + if ((pdev->resource[i].flags & IORESOURCE_DMA) && + (int)pdev->resource[i].start >= 0) { + ctlr = EDMA_CTLR(pdev->resource[i].start); + clear_bit(EDMA_CHAN_SLOT(pdev->resource[i].start), + edma_info[ctlr]->edma_unused); + } + } + + return 0; +} + /*-----------------------------------------------------------------------*/ +static bool unused_chan_list_done; + /* Resource alloc/free: dma channels, parameter RAM slots */ /** @@ -594,7 +614,22 @@ int edma_alloc_channel(int channel, void *data, enum dma_event_q eventq_no) { - unsigned i, done, ctlr = 0; + unsigned i, done = 0, ctlr = 0; + int ret = 0; + + if (!unused_chan_list_done) { + /* + * Scan all the platform devices to find out the EDMA channels + * used and clear them in the unused list, making the rest + * available for ARM usage. + */ + ret = bus_for_each_dev(&platform_bus_type, NULL, NULL, + prepare_unused_channel_list); + if (ret < 0) + return ret; + + unused_chan_list_done = true; + } if (channel >= 0) { ctlr = EDMA_CTLR(channel); @@ -602,15 +637,15 @@ int edma_alloc_channel(int channel, } if (channel < 0) { - for (i = 0; i < EDMA_MAX_CC; i++) { + for (i = 0; i < arch_num_cc; i++) { channel = 0; for (;;) { channel = find_next_bit(edma_info[i]-> - edma_noevent, + edma_unused, edma_info[i]->num_channels, channel); if (channel == edma_info[i]->num_channels) - return -ENOMEM; + break; if (!test_and_set_bit(channel, edma_info[i]->edma_inuse)) { done = 1; @@ -622,6 +657,8 @@ int edma_alloc_channel(int channel, if (done) break; } + if (!done) + return -ENOMEM; } else if (channel >= edma_info[ctlr]->num_channels) { return -EINVAL; } else if (test_and_set_bit(channel, edma_info[ctlr]->edma_inuse)) { @@ -642,7 +679,7 @@ int edma_alloc_channel(int channel, map_dmach_queue(ctlr, channel, eventq_no); - return channel; + return EDMA_CTLR_CHAN(ctlr, channel); } EXPORT_SYMBOL(edma_alloc_channel); @@ -1219,7 +1256,7 @@ int edma_start(unsigned channel) unsigned int mask = (1 << (channel & 0x1f)); /* EDMA channels without event association */ - if (test_bit(channel, edma_info[ctlr]->edma_noevent)) { + if (test_bit(channel, edma_info[ctlr]->edma_unused)) { pr_debug("EDMA: ESR%d %08x\n", j, edma_shadow0_read_array(ctlr, SH_ESR, j)); edma_shadow0_write_array(ctlr, SH_ESR, j, mask); @@ -1344,7 +1381,6 @@ static int __init edma_probe(struct platform_device *pdev) const s8 (*queue_tc_mapping)[2]; int i, j, found = 0; int status = -1; - const s8 *noevent; int irq[EDMA_MAX_CC] = {0, 0}; int err_irq[EDMA_MAX_CC] = {0, 0}; struct resource *r[EDMA_MAX_CC] = {NULL}; @@ -1407,11 +1443,9 @@ static int __init edma_probe(struct platform_device *pdev) memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i), &dummy_paramset, PARM_SIZE); - noevent = info[j].noevent; - if (noevent) { - while (*noevent != -1) - set_bit(*noevent++, edma_info[j]->edma_noevent); - } + /* Mark all channels as unused */ + memset(edma_info[j]->edma_unused, 0xff, + sizeof(edma_info[j]->edma_unused)); sprintf(irq_name, "edma%d", j); irq[j] = platform_get_irq_byname(pdev, irq_name); @@ -1467,6 +1501,7 @@ static int __init edma_probe(struct platform_device *pdev) edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); edma_write_array(j, EDMA_QRAE, i, 0x0); } + arch_num_cc++; } if (tc_errs_handled) { diff --git a/arch/arm/mach-davinci/include/mach/cdce949.h b/arch/arm/mach-davinci/include/mach/cdce949.h new file mode 100644 index 000000000000..c73331fae341 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/cdce949.h @@ -0,0 +1,19 @@ +/* + * TI CDCE949 off-chip clock synthesizer support + * + * 2009 (C) Texas Instruments, Inc. http://www.ti.com/ + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +#ifndef _MACH_DAVINCI_CDCE949_H +#define _MACH_DAVINCI_CDCE949_H + +#include <linux/clk.h> + +#include <mach/clock.h> + +int cdce_set_rate(struct clk *clk, unsigned long rate); + +#endif diff --git a/arch/arm/mach-davinci/include/mach/cpuidle.h b/arch/arm/mach-davinci/include/mach/cpuidle.h index cbfc6a9c81b4..74f088b0edfb 100644 --- a/arch/arm/mach-davinci/include/mach/cpuidle.h +++ b/arch/arm/mach-davinci/include/mach/cpuidle.h @@ -12,6 +12,7 @@ struct davinci_cpuidle_config { u32 ddr2_pdown; + void __iomem *ddr2_ctlr_base; }; #endif diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 90704910d343..cc9be7fee627 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -13,15 +13,17 @@ #include <video/da8xx-fb.h> +#include <linux/davinci_emac.h> #include <mach/serial.h> #include <mach/edma.h> #include <mach/i2c.h> -#include <mach/emac.h> #include <mach/asp.h> #include <mach/mmc.h> #include <mach/usb.h> +#include <mach/pm.h> -extern void __iomem *da8xx_syscfg_base; +extern void __iomem *da8xx_syscfg0_base; +extern void __iomem *da8xx_syscfg1_base; /* * The cp_intc interrupt controller for the da8xx isn't in the same @@ -34,13 +36,17 @@ extern void __iomem *da8xx_syscfg_base; #define DA8XX_CP_INTC_SIZE SZ_8K #define DA8XX_CP_INTC_VIRT (IO_VIRT - DA8XX_CP_INTC_SIZE - SZ_4K) -#define DA8XX_SYSCFG_BASE (IO_PHYS + 0x14000) -#define DA8XX_SYSCFG_VIRT(x) (da8xx_syscfg_base + (x)) +#define DA8XX_SYSCFG0_BASE (IO_PHYS + 0x14000) +#define DA8XX_SYSCFG0_VIRT(x) (da8xx_syscfg0_base + (x)) #define DA8XX_JTAG_ID_REG 0x18 #define DA8XX_CFGCHIP0_REG 0x17c #define DA8XX_CFGCHIP2_REG 0x184 #define DA8XX_CFGCHIP3_REG 0x188 +#define DA8XX_SYSCFG1_BASE (IO_PHYS + 0x22C000) +#define DA8XX_SYSCFG1_VIRT(x) (da8xx_syscfg1_base + (x)) +#define DA8XX_DEEPSLEEP_REG 0x8 + #define DA8XX_PSC0_BASE 0x01c10000 #define DA8XX_PLL0_BASE 0x01c11000 #define DA8XX_TIMER64P0_BASE 0x01c20000 @@ -48,11 +54,13 @@ extern void __iomem *da8xx_syscfg_base; #define DA8XX_GPIO_BASE 0x01e26000 #define DA8XX_PSC1_BASE 0x01e27000 #define DA8XX_LCD_CNTRL_BASE 0x01e13000 +#define DA8XX_PLL1_BASE 0x01e1a000 #define DA8XX_MMCSD0_BASE 0x01c40000 #define DA8XX_AEMIF_CS2_BASE 0x60000000 #define DA8XX_AEMIF_CS3_BASE 0x62000000 #define DA8XX_AEMIF_CTL_BASE 0x68000000 #define DA8XX_DDR2_CTL_BASE 0xb0000000 +#define DA8XX_ARM_RAM_BASE 0xffff0000 #define PINMUX0 0x00 #define PINMUX1 0x04 @@ -90,6 +98,8 @@ void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata); int da8xx_register_rtc(void); int da850_register_cpufreq(void); int da8xx_register_cpuidle(void); +void __iomem * __init da8xx_get_mem_ctlr(void); +int da850_register_pm(struct platform_device *pdev); extern struct platform_device da8xx_serial_device; extern struct emac_platform_data da8xx_emac_pdata; diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index f1710a30e7ba..3c07a88b4249 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -14,8 +14,8 @@ #define __ASM_ARCH_DM665_H #include <linux/platform_device.h> +#include <linux/davinci_emac.h> #include <mach/hardware.h> -#include <mach/emac.h> #include <mach/asp.h> #include <mach/keyscan.h> diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 44e8f0fae9ea..1a8b09ccc3c8 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -22,8 +22,8 @@ #ifndef __ASM_ARCH_DM644X_H #define __ASM_ARCH_DM644X_H +#include <linux/davinci_emac.h> #include <mach/hardware.h> -#include <mach/emac.h> #include <mach/asp.h> #include <media/davinci/vpfe_capture.h> diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 8cec746ae9d2..846da98b619a 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -12,10 +12,11 @@ #define __ASM_ARCH_DM646X_H #include <mach/hardware.h> -#include <mach/emac.h> #include <mach/asp.h> #include <linux/i2c.h> #include <linux/videodev2.h> +#include <linux/clk.h> +#include <linux/davinci_emac.h> #define DM646X_EMAC_BASE (0x01C80000) #define DM646X_EMAC_CNTRL_OFFSET (0x0000) @@ -30,6 +31,7 @@ void __init dm646x_init(void); void __init dm646x_init_ide(void); void __init dm646x_init_mcasp0(struct snd_platform_data *pdata); void __init dm646x_init_mcasp1(struct snd_platform_data *pdata); +void __init dm646x_board_setup_refclk(struct clk *clk); void dm646x_video_init(void); diff --git a/arch/arm/mach-davinci/include/mach/edma.h b/arch/arm/mach-davinci/include/mach/edma.h index eb8bfd7925e7..ced3092af5ba 100644 --- a/arch/arm/mach-davinci/include/mach/edma.h +++ b/arch/arm/mach-davinci/include/mach/edma.h @@ -280,8 +280,6 @@ struct edma_soc_info { unsigned n_cc; enum dma_event_q default_queue; - /* list of channels with no even trigger; terminated by "-1" */ - const s8 *noevent; const s8 (*queue_tc_mapping)[2]; const s8 (*queue_priority_mapping)[2]; }; diff --git a/arch/arm/mach-davinci/include/mach/emac.h b/arch/arm/mach-davinci/include/mach/emac.h deleted file mode 100644 index beff4fb7c845..000000000000 --- a/arch/arm/mach-davinci/include/mach/emac.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * TI DaVinci EMAC platform support - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef _MACH_DAVINCI_EMAC_H -#define _MACH_DAVINCI_EMAC_H - -#include <linux/if_ether.h> -#include <linux/memory.h> - -struct emac_platform_data { - char mac_addr[ETH_ALEN]; - u32 ctrl_reg_offset; - u32 ctrl_mod_reg_offset; - u32 ctrl_ram_offset; - u32 mdio_reg_offset; - u32 ctrl_ram_size; - u32 phy_mask; - u32 mdio_max_freq; - u8 rmii_en; - u8 version; -}; - -enum { - EMAC_VERSION_1, /* DM644x */ - EMAC_VERSION_2, /* DM646x */ -}; - -void davinci_get_mac_addr(struct memory_accessor *mem_acc, void *context); -#endif diff --git a/arch/arm/mach-davinci/include/mach/i2c.h b/arch/arm/mach-davinci/include/mach/i2c.h index c248e9b7e825..44bdea13cc8c 100644 --- a/arch/arm/mach-davinci/include/mach/i2c.h +++ b/arch/arm/mach-davinci/include/mach/i2c.h @@ -1,5 +1,5 @@ /* - * DaVinci I2C controller platfrom_device info + * DaVinci I2C controller platform_device info * * Author: Vladimir Barinov, MontaVista Software, Inc. <source@mvista.com> * diff --git a/arch/arm/mach-davinci/include/mach/keyscan.h b/arch/arm/mach-davinci/include/mach/keyscan.h index b4e21a2976d1..7a560e05bda8 100644 --- a/arch/arm/mach-davinci/include/mach/keyscan.h +++ b/arch/arm/mach-davinci/include/mach/keyscan.h @@ -29,6 +29,7 @@ enum davinci_matrix_types { }; struct davinci_ks_platform_data { + int (*device_enable)(struct device *dev); unsigned short *keymap; u32 keymapsize; u8 rep:1; diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h index 80309aed534a..a91edfb8beea 100644 --- a/arch/arm/mach-davinci/include/mach/memory.h +++ b/arch/arm/mach-davinci/include/mach/memory.h @@ -31,6 +31,11 @@ #define PHYS_OFFSET DAVINCI_DDR_BASE #endif +#define DDR2_SDRCR_OFFSET 0xc +#define DDR2_SRPD_BIT BIT(23) +#define DDR2_MCLKSTOPEN_BIT BIT(30) +#define DDR2_LPMODEN_BIT BIT(31) + /* * Increase size of DMA-consistent memory region */ diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index b60c693985ff..137bfba51d1f 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -899,6 +899,7 @@ enum davinci_da850_index { DA850_GPIO2_15, DA850_GPIO4_0, DA850_GPIO4_1, + DA850_RTC_ALARM, }; #ifdef CONFIG_DAVINCI_MUX diff --git a/arch/arm/mach-davinci/include/mach/pm.h b/arch/arm/mach-davinci/include/mach/pm.h new file mode 100644 index 000000000000..37b19bf35a85 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/pm.h @@ -0,0 +1,54 @@ +/* + * TI DaVinci platform support for power management. + * + * Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MACH_DAVINCI_PM_H +#define _MACH_DAVINCI_PM_H + +/* + * Caution: Assembly code in sleep.S makes assumtion on the order + * of the members of this structure. + */ +struct davinci_pm_config { + void __iomem *ddr2_ctlr_base; + void __iomem *ddrpsc_reg_base; + int ddrpsc_num; + void __iomem *ddrpll_reg_base; + void __iomem *deepsleep_reg; + void __iomem *cpupll_reg_base; + /* + * Note on SLEEPCOUNT: + * The SLEEPCOUNT feature is mainly intended for cases in which + * the internal oscillator is used. The internal oscillator is + * fully disabled in deep sleep mode. When you exist deep sleep + * mode, the oscillator will be turned on and will generate very + * small oscillations which will not be detected by the deep sleep + * counter. Eventually those oscillations will grow to an amplitude + * large enough to start incrementing the deep sleep counter. + * In this case recommendation from hardware engineers is that the + * SLEEPCOUNT be set to 4096. This means that 4096 valid clock cycles + * must be detected before the clock is passed to the rest of the + * system. + * In the case that the internal oscillator is not used and the + * clock is generated externally, the SLEEPCOUNT value can be very + * small since the clock input is assumed to be stable before SoC + * is taken out of deepsleep mode. A value of 128 would be more than + * adequate. + */ + int sleepcount; +}; + +extern unsigned int davinci_cpu_suspend_sz; +extern void davinci_cpu_suspend(struct davinci_pm_config *); + +#endif diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index 171173c1dbad..651f6d8158fa 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -180,8 +180,23 @@ #define DA8XX_LPSC1_CR_P3_SS 26 #define DA8XX_LPSC1_L3_CBA_RAM 31 +/* PSC register offsets */ +#define EPCPR 0x070 +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT 0x200 +#define PDCTL1 0x304 +#define MDSTAT 0x800 +#define MDCTL 0xA00 + +#define MDSTAT_STATE_MASK 0x1f + +#ifndef __ASSEMBLER__ + extern int davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id); extern void davinci_psc_config(unsigned int domain, unsigned int ctlr, unsigned int id, char enable); +#endif + #endif /* __ASM_ARCH_PSC_H */ diff --git a/arch/arm/mach-davinci/include/mach/timex.h b/arch/arm/mach-davinci/include/mach/timex.h index 52827567841d..9b885298f106 100644 --- a/arch/arm/mach-davinci/include/mach/timex.h +++ b/arch/arm/mach-davinci/include/mach/timex.h @@ -11,7 +11,12 @@ #ifndef __ASM_ARCH_TIMEX_H #define __ASM_ARCH_TIMEX_H -/* The source frequency for the timers is the 27MHz clock */ +/* + * Alert: Not all timers of the DaVinci family run at a frequency of 27MHz, + * but we should be fine as long as CLOCK_TICK_RATE or LATCH (see include/ + * linux/jiffies.h) are not used directly in code. Currently none of the + * code relevant to DaVinci platform depends on these values directly. + */ #define CLOCK_TICK_RATE 27000000 #endif /* __ASM_ARCH_TIMEX_H__ */ diff --git a/arch/arm/mach-davinci/io.c b/arch/arm/mach-davinci/io.c index 49912b48b1b0..a1c0b6b99edf 100644 --- a/arch/arm/mach-davinci/io.c +++ b/arch/arm/mach-davinci/io.c @@ -24,7 +24,7 @@ void __iomem *davinci_ioremap(unsigned long p, size_t size, unsigned int type) if (BETWEEN(p, IO_PHYS, IO_SIZE)) return XLATE(p, IO_PHYS, IO_VIRT); - return __arm_ioremap(p, size, type); + return __arm_ioremap_caller(p, size, type, __builtin_return_address(0)); } EXPORT_SYMBOL(davinci_ioremap); diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c new file mode 100644 index 000000000000..fab953b43dea --- /dev/null +++ b/arch/arm/mach-davinci/pm.c @@ -0,0 +1,158 @@ +/* + * DaVinci Power Management Routines + * + * Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/spinlock.h> + +#include <asm/cacheflush.h> +#include <asm/delay.h> + +#include <mach/da8xx.h> +#include <mach/sram.h> +#include <mach/pm.h> + +#include "clock.h" + +#define DEEPSLEEP_SLEEPCOUNT_MASK 0xFFFF + +static void (*davinci_sram_suspend) (struct davinci_pm_config *); +static struct davinci_pm_config *pdata; + +static void davinci_sram_push(void *dest, void *src, unsigned int size) +{ + memcpy(dest, src, size); + flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); +} + +static void davinci_pm_suspend(void) +{ + unsigned val; + + if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) { + + /* Switch CPU PLL to bypass mode */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + + udelay(PLL_BYPASS_TIME); + + /* Powerdown CPU PLL */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val |= PLLCTL_PLLPWRDN; + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + } + + /* Configure sleep count in deep sleep register */ + val = __raw_readl(pdata->deepsleep_reg); + val &= ~DEEPSLEEP_SLEEPCOUNT_MASK, + val |= pdata->sleepcount; + __raw_writel(val, pdata->deepsleep_reg); + + /* System goes to sleep in this call */ + davinci_sram_suspend(pdata); + + if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) { + + /* put CPU PLL in reset */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val &= ~PLLCTL_PLLRST; + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + + /* put CPU PLL in power down */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val &= ~PLLCTL_PLLPWRDN; + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + + /* wait for CPU PLL reset */ + udelay(PLL_RESET_TIME); + + /* bring CPU PLL out of reset */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val |= PLLCTL_PLLRST; + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + + /* Wait for CPU PLL to lock */ + udelay(PLL_LOCK_TIME); + + /* Remove CPU PLL from bypass mode */ + val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); + val &= ~PLLCTL_PLLENSRC; + val |= PLLCTL_PLLEN; + __raw_writel(val, pdata->cpupll_reg_base + PLLCTL); + } +} + +static int davinci_pm_enter(suspend_state_t state) +{ + int ret = 0; + + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + davinci_pm_suspend(); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static struct platform_suspend_ops davinci_pm_ops = { + .enter = davinci_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static int __init davinci_pm_probe(struct platform_device *pdev) +{ + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "cannot get platform data\n"); + return -ENOENT; + } + + davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); + if (!davinci_sram_suspend) { + dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); + return -ENOMEM; + } + + davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, + davinci_cpu_suspend_sz); + + suspend_set_ops(&davinci_pm_ops); + + return 0; +} + +static int __exit davinci_pm_remove(struct platform_device *pdev) +{ + sram_free(davinci_sram_suspend, davinci_cpu_suspend_sz); + return 0; +} + +static struct platform_driver davinci_pm_driver = { + .driver = { + .name = "pm-davinci", + .owner = THIS_MODULE, + }, + .remove = __exit_p(davinci_pm_remove), +}; + +static int __init davinci_pm_init(void) +{ + return platform_driver_probe(&davinci_pm_driver, davinci_pm_probe); +} +late_initcall(davinci_pm_init); diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index 04a3cb72c5ab..adf6b5c7f1e5 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -25,17 +25,6 @@ #include <mach/cputype.h> #include <mach/psc.h> -/* PSC register offsets */ -#define EPCPR 0x070 -#define PTCMD 0x120 -#define PTSTAT 0x128 -#define PDSTAT 0x200 -#define PDCTL1 0x304 -#define MDSTAT 0x800 -#define MDCTL 0xA00 - -#define MDSTAT_STATE_MASK 0x1f - /* Return nonzero iff the domain's clock is active */ int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id) { diff --git a/arch/arm/mach-davinci/sleep.S b/arch/arm/mach-davinci/sleep.S new file mode 100644 index 000000000000..fb5e72b532b0 --- /dev/null +++ b/arch/arm/mach-davinci/sleep.S @@ -0,0 +1,224 @@ +/* + * (C) Copyright 2009, Texas Instruments, Inc. http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR /PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* replicated define because linux/bitops.h cannot be included in assembly */ +#define BIT(nr) (1 << (nr)) + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/psc.h> +#include <mach/memory.h> + +#include "clock.h" + +/* Arbitrary, hardware currently does not update PHYRDY correctly */ +#define PHYRDY_CYCLES 0x1000 + +/* Assume 25 MHz speed for the cycle conversions since PLLs are bypassed */ +#define PLL_BYPASS_CYCLES (PLL_BYPASS_TIME * 25) +#define PLL_RESET_CYCLES (PLL_RESET_TIME * 25) +#define PLL_LOCK_CYCLES (PLL_LOCK_TIME * 25) + +#define DEEPSLEEP_SLEEPENABLE_BIT BIT(31) + + .text +/* + * Move DaVinci into deep sleep state + * + * Note: This code is copied to internal SRAM by PM code. When the DaVinci + * wakes up it continues execution at the point it went to sleep. + * Register Usage: + * r0: contains virtual base for DDR2 controller + * r1: contains virtual base for DDR2 Power and Sleep controller (PSC) + * r2: contains PSC number for DDR2 + * r3: contains virtual base DDR2 PLL controller + * r4: contains virtual address of the DEEPSLEEP register + */ +ENTRY(davinci_cpu_suspend) + stmfd sp!, {r0-r12, lr} @ save registers on stack + + ldr ip, CACHE_FLUSH + blx ip + + ldmia r0, {r0-r4} + + /* + * Switch DDR to self-refresh mode. + */ + + /* calculate SDRCR address */ + ldr ip, [r0, #DDR2_SDRCR_OFFSET] + bic ip, ip, #DDR2_SRPD_BIT + orr ip, ip, #DDR2_LPMODEN_BIT + str ip, [r0, #DDR2_SDRCR_OFFSET] + + ldr ip, [r0, #DDR2_SDRCR_OFFSET] + orr ip, ip, #DDR2_MCLKSTOPEN_BIT + str ip, [r0, #DDR2_SDRCR_OFFSET] + + mov ip, #PHYRDY_CYCLES +1: subs ip, ip, #0x1 + bne 1b + + /* Disable DDR2 LPSC */ + mov r7, r0 + mov r0, #0x2 + bl davinci_ddr_psc_config + mov r0, r7 + + /* Disable clock to DDR PHY */ + ldr ip, [r3, #PLLDIV1] + bic ip, ip, #PLLDIV_EN + str ip, [r3, #PLLDIV1] + + /* Put the DDR PLL in bypass and power down */ + ldr ip, [r3, #PLLCTL] + bic ip, ip, #PLLCTL_PLLENSRC + bic ip, ip, #PLLCTL_PLLEN + str ip, [r3, #PLLCTL] + + /* Wait for PLL to switch to bypass */ + mov ip, #PLL_BYPASS_CYCLES +2: subs ip, ip, #0x1 + bne 2b + + /* Power down the PLL */ + ldr ip, [r3, #PLLCTL] + orr ip, ip, #PLLCTL_PLLPWRDN + str ip, [r3, #PLLCTL] + + /* Go to deep sleep */ + ldr ip, [r4] + orr ip, ip, #DEEPSLEEP_SLEEPENABLE_BIT + /* System goes to sleep beyond after this instruction */ + str ip, [r4] + + /* Wake up from sleep */ + + /* Clear sleep enable */ + ldr ip, [r4] + bic ip, ip, #DEEPSLEEP_SLEEPENABLE_BIT + str ip, [r4] + + /* initialize the DDR PLL controller */ + + /* Put PLL in reset */ + ldr ip, [r3, #PLLCTL] + bic ip, ip, #PLLCTL_PLLRST + str ip, [r3, #PLLCTL] + + /* Clear PLL power down */ + ldr ip, [r3, #PLLCTL] + bic ip, ip, #PLLCTL_PLLPWRDN + str ip, [r3, #PLLCTL] + + mov ip, #PLL_RESET_CYCLES +3: subs ip, ip, #0x1 + bne 3b + + /* Bring PLL out of reset */ + ldr ip, [r3, #PLLCTL] + orr ip, ip, #PLLCTL_PLLRST + str ip, [r3, #PLLCTL] + + /* Wait for PLL to lock (assume prediv = 1, 25MHz OSCIN) */ + mov ip, #PLL_LOCK_CYCLES +4: subs ip, ip, #0x1 + bne 4b + + /* Remove PLL from bypass mode */ + ldr ip, [r3, #PLLCTL] + bic ip, ip, #PLLCTL_PLLENSRC + orr ip, ip, #PLLCTL_PLLEN + str ip, [r3, #PLLCTL] + + /* Start 2x clock to DDR2 */ + + ldr ip, [r3, #PLLDIV1] + orr ip, ip, #PLLDIV_EN + str ip, [r3, #PLLDIV1] + + /* Enable VCLK */ + + /* Enable DDR2 LPSC */ + mov r7, r0 + mov r0, #0x3 + bl davinci_ddr_psc_config + mov r0, r7 + + /* clear MCLKSTOPEN */ + + ldr ip, [r0, #DDR2_SDRCR_OFFSET] + bic ip, ip, #DDR2_MCLKSTOPEN_BIT + str ip, [r0, #DDR2_SDRCR_OFFSET] + + ldr ip, [r0, #DDR2_SDRCR_OFFSET] + bic ip, ip, #DDR2_LPMODEN_BIT + str ip, [r0, #DDR2_SDRCR_OFFSET] + + /* Restore registers and return */ + ldmfd sp!, {r0-r12, pc} + +ENDPROC(davinci_cpu_suspend) + +/* + * Disables or Enables DDR2 LPSC + * Register Usage: + * r0: Enable or Disable LPSC r0 = 0x3 => Enable, r0 = 0x2 => Disable LPSC + * r1: contains virtual base for DDR2 Power and Sleep controller (PSC) + * r2: contains PSC number for DDR2 + */ +ENTRY(davinci_ddr_psc_config) + /* Set next state in mdctl for DDR2 */ + mov r6, #MDCTL + add r6, r6, r2, lsl #2 + ldr ip, [r1, r6] + bic ip, ip, #MDSTAT_STATE_MASK + orr ip, ip, r0 + str ip, [r1, r6] + + /* Enable the Power Domain Transition Command */ + ldr ip, [r1, #PTCMD] + orr ip, ip, #0x1 + str ip, [r1, #PTCMD] + + /* Check for Transition Complete (PTSTAT) */ +ptstat_done: + ldr ip, [r1, #PTSTAT] + and ip, ip, #0x1 + cmp ip, #0x0 + bne ptstat_done + + /* Check for DDR2 clock disable completion; */ + mov r6, #MDSTAT + add r6, r6, r2, lsl #2 +ddr2clk_stop_done: + ldr ip, [r1, r6] + and ip, ip, #MDSTAT_STATE_MASK + cmp ip, r0 + bne ddr2clk_stop_done + + mov pc, lr +ENDPROC(davinci_ddr_psc_config) + +CACHE_FLUSH: + .word arm926_flush_kern_cache_all + +ENTRY(davinci_cpu_suspend_sz) + .word . - davinci_cpu_suspend +ENDPROC(davinci_cpu_suspend_sz) diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c index 1d0f9d8aff2e..e894ee0d603d 100644 --- a/arch/arm/mach-ep93xx/clock.c +++ b/arch/arm/mach-ep93xx/clock.c @@ -10,6 +10,8 @@ * your option) any later version. */ +#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/clk.h> #include <linux/err.h> @@ -445,37 +447,39 @@ static void __init ep93xx_dma_clock_init(void) static int __init ep93xx_clock_init(void) { u32 value; - int i; - value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1); - if (!(value & 0x00800000)) { /* PLL1 bypassed? */ + /* Determine the bootloader configured pll1 rate */ + value = __raw_readl(EP93XX_SYSCON_CLKSET1); + if (!(value & EP93XX_SYSCON_CLKSET1_NBYP1)) clk_pll1.rate = clk_xtali.rate; - } else { + else clk_pll1.rate = calc_pll_rate(value); - } + + /* Initialize the pll1 derived clocks */ clk_f.rate = clk_pll1.rate / fclk_divisors[(value >> 25) & 0x7]; clk_h.rate = clk_pll1.rate / hclk_divisors[(value >> 20) & 0x7]; clk_p.rate = clk_h.rate / pclk_divisors[(value >> 18) & 0x3]; ep93xx_dma_clock_init(); + /* Determine the bootloader configured pll2 rate */ value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2); - if (!(value & 0x00080000)) { /* PLL2 bypassed? */ + if (!(value & EP93XX_SYSCON_CLKSET2_NBYP2)) clk_pll2.rate = clk_xtali.rate; - } else if (value & 0x00040000) { /* PLL2 enabled? */ + else if (value & EP93XX_SYSCON_CLKSET2_PLL2_EN) clk_pll2.rate = calc_pll_rate(value); - } else { + else clk_pll2.rate = 0; - } + + /* Initialize the pll2 derived clocks */ clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1); - printk(KERN_INFO "ep93xx: PLL1 running at %ld MHz, PLL2 at %ld MHz\n", + pr_info("PLL1 running at %ld MHz, PLL2 at %ld MHz\n", clk_pll1.rate / 1000000, clk_pll2.rate / 1000000); - printk(KERN_INFO "ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n", + pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n", clk_f.rate / 1000000, clk_h.rate / 1000000, clk_p.rate / 1000000); - for (i = 0; i < ARRAY_SIZE(clocks); i++) - clkdev_add(&clocks[i]); + clkdev_add_table(clocks, ARRAY_SIZE(clocks)); return 0; } arch_initcall(ep93xx_clock_init); diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 1f0d66561bbe..41064bd63e38 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -14,6 +14,8 @@ * your option) any later version. */ +#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> @@ -318,8 +320,7 @@ static int ep93xx_gpio_irq_type(unsigned int irq, unsigned int type) desc->handle_irq = handle_edge_irq; break; default: - pr_err("ep93xx: failed to set irq type %d for gpio %d\n", - type, gpio); + pr_err("failed to set irq type %d for gpio %d\n", type, gpio); return -EINVAL; } @@ -572,9 +573,9 @@ void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, * CMOS driver. */ if (data->sda_is_open_drain && data->sda_pin != EP93XX_GPIO_LINE_EEDAT) - pr_warning("ep93xx: sda != EEDAT, open drain has no effect\n"); + pr_warning("sda != EEDAT, open drain has no effect\n"); if (data->scl_is_open_drain && data->scl_pin != EP93XX_GPIO_LINE_EECLK) - pr_warning("ep93xx: scl != EECLK, open drain has no effect\n"); + pr_warning("scl != EECLK, open drain has no effect\n"); __raw_writel((data->sda_is_open_drain << 1) | (data->scl_is_open_drain << 0), diff --git a/arch/arm/mach-ep93xx/dma-m2p.c b/arch/arm/mach-ep93xx/dma-m2p.c index dbcac9c40a28..8904ca4e2e24 100644 --- a/arch/arm/mach-ep93xx/dma-m2p.c +++ b/arch/arm/mach-ep93xx/dma-m2p.c @@ -28,6 +28,8 @@ * with this implementation. */ +#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/clk.h> #include <linux/err.h> @@ -173,7 +175,7 @@ static irqreturn_t m2p_irq(int irq, void *dev_id) switch (m2p_channel_state(ch)) { case STATE_IDLE: - pr_crit("m2p_irq: dma interrupt without a dma buffer\n"); + pr_crit("dma interrupt without a dma buffer\n"); BUG(); break; @@ -197,7 +199,7 @@ static irqreturn_t m2p_irq(int irq, void *dev_id) break; case STATE_NEXT: - pr_crit("m2p_irq: dma interrupt while next\n"); + pr_crit("dma interrupt while next\n"); BUG(); break; } diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c index a4a7be308000..d22d67ac8b99 100644 --- a/arch/arm/mach-ep93xx/edb93xx.c +++ b/arch/arm/mach-ep93xx/edb93xx.c @@ -118,12 +118,33 @@ static void __init edb93xx_register_i2c(void) } } + +/************************************************************************* + * EDB93xx pwm + *************************************************************************/ +static void __init edb93xx_register_pwm(void) +{ + if (machine_is_edb9301() || + machine_is_edb9302() || machine_is_edb9302a()) { + /* EP9301 and EP9302 only have pwm.1 (EGPIO14) */ + ep93xx_register_pwm(0, 1); + } else if (machine_is_edb9307() || machine_is_edb9307a()) { + /* EP9307 only has pwm.0 (PWMOUT) */ + ep93xx_register_pwm(1, 0); + } else { + /* EP9312 and EP9315 have both */ + ep93xx_register_pwm(1, 1); + } +} + + static void __init edb93xx_init_machine(void) { ep93xx_init_devices(); edb93xx_register_flash(); ep93xx_register_eth(&edb93xx_eth_data, 1); edb93xx_register_i2c(); + edb93xx_register_pwm(); } diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index d55194a4c093..cd359120c1f5 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -167,8 +167,11 @@ #define EP93XX_SYSCON_PWRCNT_DMA_M2P1 (1<<16) #define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08) #define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c) -#define EP93XX_SYSCON_CLOCK_SET1 EP93XX_SYSCON_REG(0x20) -#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24) +#define EP93XX_SYSCON_CLKSET1 EP93XX_SYSCON_REG(0x20) +#define EP93XX_SYSCON_CLKSET1_NBYP1 (1<<23) +#define EP93XX_SYSCON_CLKSET2 EP93XX_SYSCON_REG(0x24) +#define EP93XX_SYSCON_CLKSET2_NBYP2 (1<<19) +#define EP93XX_SYSCON_CLKSET2_PLL2_EN (1<<18) #define EP93XX_SYSCON_DEVCFG EP93XX_SYSCON_REG(0x80) #define EP93XX_SYSCON_DEVCFG_SWRST (1<<31) #define EP93XX_SYSCON_DEVCFG_D1ONG (1<<30) diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h index 3bd934e9a7f1..61c0e132c63e 100644 --- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h +++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h @@ -9,9 +9,6 @@ * febff000 22000000 4K model number register * febfe000 22400000 4K options register * febfd000 22800000 4K options register #2 - * febfc000 [67]0000000 4K NAND data register - * febfb000 [67]0400000 4K NAND control register - * febfa000 [67]0800000 4K NAND busy register * febf9000 10800000 4K TS-5620 RTC index register * febf8000 11700000 4K TS-5620 RTC data register */ @@ -41,22 +38,6 @@ #define TS72XX_OPTIONS2_TS9420_BOOT 0x02 -#define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000 -#define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000 -#define TS72XX_NAND_DATA_VIRT_BASE 0xfebfc000 -#define TS72XX_NAND_DATA_SIZE 0x00001000 - -#define TS72XX_NAND1_CONTROL_PHYS_BASE 0x60400000 -#define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000 -#define TS72XX_NAND_CONTROL_VIRT_BASE 0xfebfb000 -#define TS72XX_NAND_CONTROL_SIZE 0x00001000 - -#define TS72XX_NAND1_BUSY_PHYS_BASE 0x60800000 -#define TS72XX_NAND2_BUSY_PHYS_BASE 0x70800000 -#define TS72XX_NAND_BUSY_VIRT_BASE 0xfebfa000 -#define TS72XX_NAND_BUSY_SIZE 0x00001000 - - #define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000 #define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000 #define TS72XX_RTC_INDEX_SIZE 0x00001000 diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index 259f7822ba52..47a86f07831d 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -10,12 +10,16 @@ * your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/m48t86.h> #include <linux/mtd/physmap.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> #include <mach/hardware.h> #include <mach/ts72xx.h> @@ -54,92 +58,162 @@ static struct map_desc ts72xx_io_desc[] __initdata = { } }; -static struct map_desc ts72xx_nand_io_desc[] __initdata = { - { - .virtual = TS72XX_NAND_DATA_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_DATA_PHYS_BASE), - .length = TS72XX_NAND_DATA_SIZE, - .type = MT_DEVICE, - }, { - .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_CONTROL_PHYS_BASE), - .length = TS72XX_NAND_CONTROL_SIZE, - .type = MT_DEVICE, - }, { - .virtual = TS72XX_NAND_BUSY_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_BUSY_PHYS_BASE), - .length = TS72XX_NAND_BUSY_SIZE, - .type = MT_DEVICE, +static void __init ts72xx_map_io(void) +{ + ep93xx_map_io(); + iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); +} + + +/************************************************************************* + * NAND flash + *************************************************************************/ +#define TS72XX_NAND_CONTROL_ADDR_LINE 22 /* 0xN0400000 */ +#define TS72XX_NAND_BUSY_ADDR_LINE 23 /* 0xN0800000 */ + +static void ts72xx_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + void __iomem *addr = chip->IO_ADDR_R; + unsigned char bits; + + addr += (1 << TS72XX_NAND_CONTROL_ADDR_LINE); + + bits = __raw_readb(addr) & ~0x07; + bits |= (ctrl & NAND_NCE) << 2; /* bit 0 -> bit 2 */ + bits |= (ctrl & NAND_CLE); /* bit 1 -> bit 1 */ + bits |= (ctrl & NAND_ALE) >> 2; /* bit 2 -> bit 0 */ + + __raw_writeb(bits, addr); } -}; -static struct map_desc ts72xx_alternate_nand_io_desc[] __initdata = { + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, chip->IO_ADDR_W); +} + +static int ts72xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + void __iomem *addr = chip->IO_ADDR_R; + + addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE); + + return !!(__raw_readb(addr) & 0x20); +} + +static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL }; + +#define TS72XX_BOOTROM_PART_SIZE (SZ_16K) +#define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M) + +static struct mtd_partition ts72xx_nand_parts[] = { { - .virtual = TS72XX_NAND_DATA_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_DATA_PHYS_BASE), - .length = TS72XX_NAND_DATA_SIZE, - .type = MT_DEVICE, + .name = "TS-BOOTROM", + .offset = 0, + .size = TS72XX_BOOTROM_PART_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_CONTROL_PHYS_BASE), - .length = TS72XX_NAND_CONTROL_SIZE, - .type = MT_DEVICE, + .name = "Linux", + .offset = MTDPART_OFS_APPEND, + .size = 0, /* filled in later */ }, { - .virtual = TS72XX_NAND_BUSY_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_BUSY_PHYS_BASE), - .length = TS72XX_NAND_BUSY_SIZE, - .type = MT_DEVICE, - } + .name = "RedBoot", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, }; -static void __init ts72xx_map_io(void) +static void ts72xx_nand_set_parts(uint64_t size, + struct platform_nand_chip *chip) { - ep93xx_map_io(); - iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); + /* Factory TS-72xx boards only come with 32MiB or 128MiB NAND options */ + if (size == SZ_32M || size == SZ_128M) { + /* Set the "Linux" partition size */ + ts72xx_nand_parts[1].size = size - TS72XX_REDBOOT_PART_SIZE; - /* - * The TS-7200 has NOR flash, the other models have NAND flash. - */ - if (!board_is_ts7200()) { - if (is_ts9420_installed()) { - iotable_init(ts72xx_alternate_nand_io_desc, - ARRAY_SIZE(ts72xx_alternate_nand_io_desc)); - } else { - iotable_init(ts72xx_nand_io_desc, - ARRAY_SIZE(ts72xx_nand_io_desc)); - } + chip->partitions = ts72xx_nand_parts; + chip->nr_partitions = ARRAY_SIZE(ts72xx_nand_parts); + } else { + pr_warning("Unknown nand disk size:%lluMiB\n", size >> 20); } } +static struct platform_nand_data ts72xx_nand_data = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .chip_delay = 15, + .part_probe_types = ts72xx_nand_part_probes, + .set_parts = ts72xx_nand_set_parts, + }, + .ctrl = { + .cmd_ctrl = ts72xx_nand_hwcontrol, + .dev_ready = ts72xx_nand_device_ready, + }, +}; + +static struct resource ts72xx_nand_resource[] = { + { + .start = 0, /* filled in later */ + .end = 0, /* filled in later */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ts72xx_nand_flash = { + .name = "gen_nand", + .id = -1, + .dev.platform_data = &ts72xx_nand_data, + .resource = ts72xx_nand_resource, + .num_resources = ARRAY_SIZE(ts72xx_nand_resource), +}; + + /************************************************************************* * NOR flash (TS-7200 only) *************************************************************************/ -static struct physmap_flash_data ts72xx_flash_data = { +static struct physmap_flash_data ts72xx_nor_data = { .width = 2, }; -static struct resource ts72xx_flash_resource = { +static struct resource ts72xx_nor_resource = { .start = EP93XX_CS6_PHYS_BASE, .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1, .flags = IORESOURCE_MEM, }; -static struct platform_device ts72xx_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &ts72xx_flash_data, - }, - .num_resources = 1, - .resource = &ts72xx_flash_resource, +static struct platform_device ts72xx_nor_flash = { + .name = "physmap-flash", + .id = 0, + .dev.platform_data = &ts72xx_nor_data, + .resource = &ts72xx_nor_resource, + .num_resources = 1, }; static void __init ts72xx_register_flash(void) { - if (board_is_ts7200()) - platform_device_register(&ts72xx_flash); + if (board_is_ts7200()) { + platform_device_register(&ts72xx_nor_flash); + } else { + resource_size_t start; + + if (is_ts9420_installed()) + start = EP93XX_CS7_PHYS_BASE; + else + start = EP93XX_CS6_PHYS_BASE; + + ts72xx_nand_resource[0].start = start; + ts72xx_nand_resource[0].end = start + SZ_16M - 1; + + platform_device_register(&ts72xx_nand_flash); + } } + static unsigned char ts72xx_rtc_readbyte(unsigned long addr) { __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); diff --git a/arch/arm/mach-footbridge/common.c b/arch/arm/mach-footbridge/common.c index 41febc796b1c..e3bc3f6f6b10 100644 --- a/arch/arm/mach-footbridge/common.c +++ b/arch/arm/mach-footbridge/common.c @@ -32,12 +32,13 @@ unsigned int mem_fclk_21285 = 50000000; EXPORT_SYMBOL(mem_fclk_21285); -static void __init early_fclk(char **arg) +static int __init early_fclk(char *arg) { - mem_fclk_21285 = simple_strtoul(*arg, arg, 0); + mem_fclk_21285 = simple_strtoul(arg, NULL, 0); + return 0; } -__early_param("mem_fclk_21285=", early_fclk); +early_param("mem_fclk_21285", early_fclk); static int __init parse_tag_memclk(const struct tag *tag) { diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c index a0f60e55da6a..8b390e36ba69 100644 --- a/arch/arm/mach-integrator/core.c +++ b/arch/arm/mach-integrator/core.c @@ -144,8 +144,7 @@ static int __init integrator_init(void) { int i; - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 3f35293d457a..66ef86d6d9e3 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -558,9 +558,7 @@ static void __init intcp_init(void) { int i; - for (i = 0; i < ARRAY_SIZE(cp_lookups); i++) - clkdev_add(&cp_lookups[i]); - + clkdev_add_table(cp_lookups, ARRAY_SIZE(cp_lookups)); platform_add_devices(intcp_devs, ARRAY_SIZE(intcp_devs)); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { diff --git a/arch/arm/mach-iop13xx/io.c b/arch/arm/mach-iop13xx/io.c index 529580997814..48642e66c566 100644 --- a/arch/arm/mach-iop13xx/io.c +++ b/arch/arm/mach-iop13xx/io.c @@ -61,9 +61,9 @@ void * __iomem __iop13xx_ioremap(unsigned long cookie, size_t size, (cookie - IOP13XX_PCIE_LOWER_MEM_RA)); break; case IOP13XX_PBI_LOWER_MEM_RA ... IOP13XX_PBI_UPPER_MEM_RA: - retval = __arm_ioremap(IOP13XX_PBI_LOWER_MEM_PA + + retval = __arm_ioremap_caller(IOP13XX_PBI_LOWER_MEM_PA + (cookie - IOP13XX_PBI_LOWER_MEM_RA), - size, mtype); + size, mtype, __builtin_return_address(0)); break; case IOP13XX_PCIE_LOWER_IO_PA ... IOP13XX_PCIE_UPPER_IO_PA: retval = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(cookie); @@ -75,7 +75,8 @@ void * __iomem __iop13xx_ioremap(unsigned long cookie, size_t size, retval = (void *) IOP13XX_PMMR_PHYS_TO_VIRT(cookie); break; default: - retval = __arm_ioremap(cookie, size, mtype); + retval = __arm_ioremap_caller(cookie, size, mtype, + __builtin_return_address(0)); } return retval; diff --git a/arch/arm/mach-lh7a40x/clocks.c b/arch/arm/mach-lh7a40x/clocks.c index fcaf876f19b6..0651f96653f9 100644 --- a/arch/arm/mach-lh7a40x/clocks.c +++ b/arch/arm/mach-lh7a40x/clocks.c @@ -10,6 +10,8 @@ #include <mach/hardware.h> #include <mach/clocks.h> #include <linux/err.h> +#include <linux/device.h> +#include <linux/string.h> struct module; diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c index 2a46ed5cc2a2..886e05648f08 100644 --- a/arch/arm/mach-mmp/clock.c +++ b/arch/arm/mach-mmp/clock.c @@ -88,11 +88,3 @@ unsigned long clk_get_rate(struct clk *clk) return rate; } EXPORT_SYMBOL(clk_get_rate); - -void clks_register(struct clk_lookup *clks, size_t num) -{ - int i; - - for (i = 0; i < num; i++) - clkdev_add(&clks[i]); -} diff --git a/arch/arm/mach-mmp/clock.h b/arch/arm/mach-mmp/clock.h index eefffbe683b0..016ae94691c0 100644 --- a/arch/arm/mach-mmp/clock.h +++ b/arch/arm/mach-mmp/clock.h @@ -68,5 +68,3 @@ struct clk clk_##_name = { \ extern struct clk clk_pxa168_gpio; extern struct clk clk_pxa168_timers; - -extern void clks_register(struct clk_lookup *, size_t); diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c index 37dbdde17fac..1873c821df90 100644 --- a/arch/arm/mach-mmp/pxa168.c +++ b/arch/arm/mach-mmp/pxa168.c @@ -94,7 +94,7 @@ static int __init pxa168_init(void) mfp_init_base(MFPR_VIRT_BASE); mfp_init_addr(pxa168_mfp_addr_map); pxa_init_dma(IRQ_PXA168_DMA_INT0, 32); - clks_register(ARRAY_AND_SIZE(pxa168_clkregs)); + clkdev_add_table(ARRAY_AND_SIZE(pxa168_clkregs)); } return 0; diff --git a/arch/arm/mach-mmp/pxa910.c b/arch/arm/mach-mmp/pxa910.c index d4049508a4df..46f2d69bef3c 100644 --- a/arch/arm/mach-mmp/pxa910.c +++ b/arch/arm/mach-mmp/pxa910.c @@ -131,7 +131,7 @@ static int __init pxa910_init(void) mfp_init_base(MFPR_VIRT_BASE); mfp_init_addr(pxa910_mfp_addr_map); pxa_init_dma(IRQ_PXA910_DMA_INT0, 32); - clks_register(ARRAY_AND_SIZE(pxa910_clkregs)); + clkdev_add_table(ARRAY_AND_SIZE(pxa910_clkregs)); } return 0; diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c index 8f49b2b12608..b22dec4abf78 100644 --- a/arch/arm/mach-mmp/ttc_dkb.c +++ b/arch/arm/mach-mmp/ttc_dkb.c @@ -24,8 +24,6 @@ #include "common.h" -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - static unsigned long ttc_dkb_pin_config[] __initdata = { /* UART2 */ GPIO47_UART2_RXD, 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..6d7937fa50ce 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,9 +1,78 @@ -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_MACH_TROUT) += board-trout-rfkill.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_ARCH_MSM7X01A) += board-halibut.o +obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o +obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o board-trout-panel.o +obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o +obj-$(CONFIG_MACH_TROUT) += board-trout-mmc.o board-trout-wifi.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..bcc8491ab342 --- /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 <linux/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..9d8e290e8930 --- /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 <asm/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-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..537516723f16 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 <asm/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..75e59be7fd4b --- /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 <asm/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..f417fa4f4152 --- /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 <asm/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..637253ec3f78 --- /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 <asm/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..a64578f1a032 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 <asm/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..8d8a5ddb8a4e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h @@ -0,0 +1,115 @@ +/* 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> + +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 */ + +#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 1c5e7dac086f..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) @@ -76,5 +192,7 @@ __msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) mtype = MT_DEVICE_NONSHARED; } - return __arm_ioremap(phys_addr, size, 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..e6ae979d8df8 --- /dev/null +++ b/arch/arm/mach-msm/smd_qmi.c @@ -0,0 +1,844 @@ +/* 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..64e0bca35fcf --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -0,0 +1,2199 @@ +/* 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 int rpcrouter_need_len; + +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; + } + rpcrouter_need_len = len; + 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..d4272faff974 --- /dev/null +++ b/arch/arm/mach-msm/smd_tty.c @@ -0,0 +1,284 @@ +/* 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..c2809109fc62 --- /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/remote_spinlock.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/string.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; } diff --git a/arch/arm/mach-mx1/clock.c b/arch/arm/mach-mx1/clock.c index d1b588519ad2..6cf2d4a7511d 100644 --- a/arch/arm/mach-mx1/clock.c +++ b/arch/arm/mach-mx1/clock.c @@ -570,7 +570,6 @@ static struct clk_lookup lookups[] __initdata = { int __init mx1_clocks_init(unsigned long fref) { unsigned int reg; - int i; /* disable clocks we are able to */ __raw_writel(0, SCM_GCCR); @@ -592,8 +591,7 @@ int __init mx1_clocks_init(unsigned long fref) reg = (reg & CCM_CSCR_CLKO_MASK) >> CCM_CSCR_CLKO_OFFSET; clko_clk.parent = (struct clk *)clko_clocks[reg]; - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); clk_enable(&hclk); clk_enable(&fclk); diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index 91901b5d56c2..e82b489d1215 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -968,7 +968,6 @@ static struct clk_lookup lookups[] = { */ int __init mx21_clocks_init(unsigned long lref, unsigned long href) { - int i; u32 cscr; external_low_reference = lref; @@ -986,8 +985,7 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href) else spll_clk.parent = &fpm_clk; - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); /* Turn off all clock gates */ __raw_writel(0, CCM_PCCR0); diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c index b010bf9ceaab..18c53a6487fa 100644 --- a/arch/arm/mach-mx2/clock_imx27.c +++ b/arch/arm/mach-mx2/clock_imx27.c @@ -719,7 +719,6 @@ static void __init to2_adjust_clocks(void) int __init mx27_clocks_init(unsigned long fref) { u32 cscr = __raw_readl(CCM_CSCR); - int i; external_high_reference = fref; @@ -736,8 +735,7 @@ int __init mx27_clocks_init(unsigned long fref) to2_adjust_clocks(); - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); /* Turn off all clocks we do not need */ __raw_writel(0, CCM_PCCR0); diff --git a/arch/arm/mach-mx2/mxt_td60.c b/arch/arm/mach-mx2/mxt_td60.c index 03dbbdc98955..8bcc1a5b8829 100644 --- a/arch/arm/mach-mx2/mxt_td60.c +++ b/arch/arm/mach-mx2/mxt_td60.c @@ -58,21 +58,6 @@ static unsigned int mxt_td60_pins[] __initdata = { PE9_PF_UART3_RXD, PE10_PF_UART3_CTS, PE11_PF_UART3_RTS, - /* UART3 */ - PB26_AF_UART4_RTS, - PB28_AF_UART4_TXD, - PB29_AF_UART4_CTS, - PB31_AF_UART4_RXD, - /* UART4 */ - PB18_AF_UART5_TXD, - PB19_AF_UART5_RXD, - PB20_AF_UART5_CTS, - PB21_AF_UART5_RTS, - /* UART5 */ - PB10_AF_UART6_TXD, - PB12_AF_UART6_CTS, - PB11_AF_UART6_RXD, - PB13_AF_UART6_RTS, /* FEC */ PD0_AIN_FEC_TXD0, PD1_AIN_FEC_TXD1, @@ -261,12 +246,6 @@ static struct imxuart_platform_data uart_pdata[] = { .flags = IMXUART_HAVE_RTSCTS, }, { .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, }, }; @@ -278,9 +257,6 @@ static void __init mxt_td60_board_init(void) mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); - mxc_register_device(&mxc_uart_device3, &uart_pdata[3]); - mxc_register_device(&mxc_uart_device4, &uart_pdata[4]); - mxc_register_device(&mxc_uart_device5, &uart_pdata[5]); mxc_register_device(&mxc_nand_device, &mxt_td60_nand_board_info); i2c_register_board_info(0, mxt_td60_i2c_devices, diff --git a/arch/arm/mach-mx25/clock.c b/arch/arm/mach-mx25/clock.c index ef26951a5275..66916f104812 100644 --- a/arch/arm/mach-mx25/clock.c +++ b/arch/arm/mach-mx25/clock.c @@ -173,6 +173,7 @@ DEFINE_CLOCK(pwm4_clk, 0, CCM_CGCR2, 2, get_rate_ipg, NULL); DEFINE_CLOCK(kpp_clk, 0, CCM_CGCR1, 28, get_rate_ipg, NULL); DEFINE_CLOCK(tsc_clk, 0, CCM_CGCR2, 13, get_rate_ipg, NULL); DEFINE_CLOCK(i2c_clk, 0, CCM_CGCR0, 6, get_rate_i2c, NULL); +DEFINE_CLOCK(fec_clk, 0, CCM_CGCR0, 23, get_rate_ipg, NULL); #define _REGISTER_CLOCK(d, n, c) \ { \ @@ -204,15 +205,12 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk) _REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk) _REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk) + _REGISTER_CLOCK("fec.0", NULL, fec_clk) }; int __init mx25_clocks_init(unsigned long fref) { - int i; - - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); - + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); mxc_timer_init(&gpt_clk, MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54); return 0; diff --git a/arch/arm/mach-mx25/devices.c b/arch/arm/mach-mx25/devices.c index 63511de3a559..9fdeea1c083b 100644 --- a/arch/arm/mach-mx25/devices.c +++ b/arch/arm/mach-mx25/devices.c @@ -419,3 +419,22 @@ int __init mxc_register_gpios(void) return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports)); } +static struct resource mx25_fec_resources[] = { + { + .start = MX25_FEC_BASE_ADDR, + .end = MX25_FEC_BASE_ADDR + 0xfff, + .flags = IORESOURCE_MEM, + }, + { + .start = MX25_INT_FEC, + .end = MX25_INT_FEC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device mx25_fec_device = { + .name = "fec", + .id = 0, + .num_resources = ARRAY_SIZE(mx25_fec_resources), + .resource = mx25_fec_resources, +}; diff --git a/arch/arm/mach-mx25/devices.h b/arch/arm/mach-mx25/devices.h index fe6bf88ad1dd..fe5420fcd11f 100644 --- a/arch/arm/mach-mx25/devices.h +++ b/arch/arm/mach-mx25/devices.h @@ -17,3 +17,4 @@ extern struct platform_device mxc_keypad_device; extern struct platform_device mxc_i2c_device0; extern struct platform_device mxc_i2c_device1; extern struct platform_device mxc_i2c_device2; +extern struct platform_device mx25_fec_device; diff --git a/arch/arm/mach-mx25/mx25pdk.c b/arch/arm/mach-mx25/mx25pdk.c index d23ae571c03f..921bc99ea231 100644 --- a/arch/arm/mach-mx25/mx25pdk.c +++ b/arch/arm/mach-mx25/mx25pdk.c @@ -18,10 +18,11 @@ #include <linux/types.h> #include <linux/init.h> +#include <linux/delay.h> #include <linux/clk.h> #include <linux/irq.h> #include <linux/gpio.h> -#include <linux/smsc911x.h> +#include <linux/fec.h> #include <linux/platform_device.h> #include <mach/hardware.h> @@ -35,16 +36,57 @@ #include <mach/mx25.h> #include <mach/mxc_nand.h> #include "devices.h" -#include <mach/iomux-v3.h> +#include <mach/iomux.h> static struct imxuart_platform_data uart_pdata = { .flags = IMXUART_HAVE_RTSCTS, }; +static struct pad_desc mx25pdk_pads[] = { + MX25_PAD_FEC_MDC__FEC_MDC, + MX25_PAD_FEC_MDIO__FEC_MDIO, + MX25_PAD_FEC_TDATA0__FEC_TDATA0, + MX25_PAD_FEC_TDATA1__FEC_TDATA1, + MX25_PAD_FEC_TX_EN__FEC_TX_EN, + MX25_PAD_FEC_RDATA0__FEC_RDATA0, + MX25_PAD_FEC_RDATA1__FEC_RDATA1, + MX25_PAD_FEC_RX_DV__FEC_RX_DV, + MX25_PAD_FEC_TX_CLK__FEC_TX_CLK, + MX25_PAD_A17__GPIO_2_3, /* FEC_EN, GPIO 35 */ + MX25_PAD_D12__GPIO_4_8, /* FEC_RESET_B, GPIO 104 */ +}; + +static struct fec_platform_data mx25_fec_pdata = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +#define FEC_ENABLE_GPIO 35 +#define FEC_RESET_B_GPIO 104 + +static void __init mx25pdk_fec_reset(void) +{ + gpio_request(FEC_ENABLE_GPIO, "FEC PHY enable"); + gpio_request(FEC_RESET_B_GPIO, "FEC PHY reset"); + + gpio_direction_output(FEC_ENABLE_GPIO, 0); /* drop PHY power */ + gpio_direction_output(FEC_RESET_B_GPIO, 0); /* assert reset */ + udelay(2); + + /* turn on PHY power and lift reset */ + gpio_set_value(FEC_ENABLE_GPIO, 1); + gpio_set_value(FEC_RESET_B_GPIO, 1); +} + static void __init mx25pdk_init(void) { + mxc_iomux_v3_setup_multiple_pads(mx25pdk_pads, + ARRAY_SIZE(mx25pdk_pads)); + mxc_register_device(&mxc_uart_device0, &uart_pdata); mxc_register_device(&mxc_usbh2, NULL); + + mx25pdk_fec_reset(); + mxc_register_device(&mx25_fec_device, &mx25_fec_pdata); } static void __init mx25pdk_timer_init(void) diff --git a/arch/arm/mach-mx3/Kconfig b/arch/arm/mach-mx3/Kconfig index ea8ed109a7c2..28294416b0af 100644 --- a/arch/arm/mach-mx3/Kconfig +++ b/arch/arm/mach-mx3/Kconfig @@ -49,6 +49,7 @@ config MACH_PCM037_EET config MACH_MX31LITE bool "Support MX31 LITEKIT (LogicPD)" select ARCH_MX31 + select MXC_ULPI if USB_ULPI help Include support for MX31 LITEKIT platform. This includes specific configurations for the board and its peripherals. @@ -63,7 +64,7 @@ config MACH_MX31_3DS config MACH_MX31MOBOARD bool "Support mx31moboard platforms (EPFL Mobots group)" select ARCH_MX31 - select MXC_ULPI + select MXC_ULPI if USB_ULPI help Include support for mx31moboard platform. This includes specific configurations for the board and its peripherals. diff --git a/arch/arm/mach-mx3/clock-imx35.c b/arch/arm/mach-mx3/clock-imx35.c index 7584b4c6c556..f3f41fa4f21b 100644 --- a/arch/arm/mach-mx3/clock-imx35.c +++ b/arch/arm/mach-mx3/clock-imx35.c @@ -485,15 +485,13 @@ static struct clk_lookup lookups[] = { int __init mx35_clocks_init() { - int i; unsigned int ll = 0; #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_ICEDCC) ll = (3 << 16); #endif - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); /* Turn off all clocks except the ones we need to survive, namely: * EMI, GPIO1/2/3, GPT, IOMUX, MAX and eventually uart diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c index 27a318af0d20..b5c39a016db7 100644 --- a/arch/arm/mach-mx3/clock.c +++ b/arch/arm/mach-mx3/clock.c @@ -578,12 +578,10 @@ static struct clk_lookup lookups[] = { int __init mx31_clocks_init(unsigned long fref) { u32 reg; - int i; ckih_rate = fref; - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); /* change the csi_clk parent if necessary */ reg = __raw_readl(MXC_CCM_CCMR); diff --git a/arch/arm/mach-mx3/mm.c b/arch/arm/mach-mx3/mm.c index bedf5b8d976a..6858a4f9806c 100644 --- a/arch/arm/mach-mx3/mm.c +++ b/arch/arm/mach-mx3/mm.c @@ -65,6 +65,11 @@ static struct map_desc mxc_io_desc[] __initdata = { .pfn = __phys_to_pfn(AIPS2_BASE_ADDR), .length = AIPS2_SIZE, .type = MT_DEVICE_NONSHARED + }, { + .virtual = SPBA0_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(SPBA0_BASE_ADDR), + .length = SPBA0_SIZE, + .type = MT_DEVICE_NONSHARED }, }; diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c index 0497c152be18..3e7bafa2ddbb 100644 --- a/arch/arm/mach-mx3/mx31ads.c +++ b/arch/arm/mach-mx3/mx31ads.c @@ -494,11 +494,6 @@ static void mxc_init_i2c(void) */ static struct map_desc mx31ads_io_desc[] __initdata = { { - .virtual = SPBA0_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(SPBA0_BASE_ADDR), - .length = SPBA0_SIZE, - .type = MT_DEVICE_NONSHARED - }, { .virtual = CS4_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(CS4_BASE_ADDR), .length = CS4_SIZE / 2, diff --git a/arch/arm/mach-mx3/mx31lite.c b/arch/arm/mach-mx3/mx31lite.c index def6b6736594..789b20d1730f 100644 --- a/arch/arm/mach-mx3/mx31lite.c +++ b/arch/arm/mach-mx3/mx31lite.c @@ -135,6 +135,7 @@ static struct spi_board_info mc13783_spi_dev __initdata = { * USB */ +#if defined(CONFIG_USB_ULPI) #define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \ PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU) @@ -180,6 +181,7 @@ static struct mxc_usbh_platform_data usbh2_pdata = { .portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT, .flags = MXC_EHCI_POWER_PINS_ENABLED, }; +#endif /* * NOR flash @@ -212,11 +214,6 @@ static struct platform_device physmap_flash_device = { */ static struct map_desc mx31lite_io_desc[] __initdata = { { - .virtual = SPBA0_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(SPBA0_BASE_ADDR), - .length = SPBA0_SIZE, - .type = MT_DEVICE_NONSHARED - }, { .virtual = CS4_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(CS4_BASE_ADDR), .length = CS4_SIZE, @@ -261,11 +258,13 @@ static void __init mxc_board_init(void) mxc_register_device(&mxc_spi_device1, &spi1_pdata); spi_register_board_info(&mc13783_spi_dev, 1); +#if defined(CONFIG_USB_ULPI) /* USB */ usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); mxc_register_device(&mxc_usbh2, &usbh2_pdata); +#endif /* SMSC9117 IRQ pin */ ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_SFS6), "sms9117-irq"); diff --git a/arch/arm/mach-mx3/mx31moboard-devboard.c b/arch/arm/mach-mx3/mx31moboard-devboard.c index 8fc624f141cb..438428eaf769 100644 --- a/arch/arm/mach-mx3/mx31moboard-devboard.c +++ b/arch/arm/mach-mx3/mx31moboard-devboard.c @@ -179,7 +179,7 @@ static int __init devboard_usbh1_init(void) usbh1_pdata.otg = otg; - return mxc_register_device(&mx31_usbh1, &usbh1_pdata); + return mxc_register_device(&mxc_usbh1, &usbh1_pdata); } /* diff --git a/arch/arm/mach-mx3/mx31moboard-marxbot.c b/arch/arm/mach-mx3/mx31moboard-marxbot.c index 85184a35e674..1f44b9ccbb0f 100644 --- a/arch/arm/mach-mx3/mx31moboard-marxbot.c +++ b/arch/arm/mach-mx3/mx31moboard-marxbot.c @@ -294,7 +294,7 @@ static int __init marxbot_usbh1_init(void) usbh1_pdata.otg = otg; - return mxc_register_device(&mx31_usbh1, &usbh1_pdata); + return mxc_register_device(&mxc_usbh1, &usbh1_pdata); } /* diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c index b70529145936..cfd605d078ec 100644 --- a/arch/arm/mach-mx3/mx31moboard.c +++ b/arch/arm/mach-mx3/mx31moboard.c @@ -346,6 +346,8 @@ static struct fsl_usb2_platform_data usb_pdata = { .phy_mode = FSL_USB2_PHY_ULPI, }; +#if defined(CONFIG_USB_ULPI) + #define USBH2_EN_B IOMUX_TO_GPIO(MX31_PIN_SCK6) static int moboard_usbh2_hw_init(struct platform_device *pdev) @@ -392,8 +394,11 @@ static int __init moboard_usbh2_init(void) usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); - return mxc_register_device(&mx31_usbh2, &usbh2_pdata); + return mxc_register_device(&mxc_usbh2, &usbh2_pdata); } +#else +static inline int moboard_usbh2_init(void) { return 0; } +#endif static struct gpio_led mx31moboard_leds[] = { diff --git a/arch/arm/mach-mx3/mx31pdk.c b/arch/arm/mach-mx3/mx31pdk.c index 0f7a2f06bc2d..18715f1aa7eb 100644 --- a/arch/arm/mach-mx3/mx31pdk.c +++ b/arch/arm/mach-mx3/mx31pdk.c @@ -211,11 +211,6 @@ static int __init mx31pdk_init_expio(void) */ static struct map_desc mx31pdk_io_desc[] __initdata = { { - .virtual = SPBA0_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(SPBA0_BASE_ADDR), - .length = SPBA0_SIZE, - .type = MT_DEVICE_NONSHARED, - }, { .virtual = CS5_BASE_ADDR_VIRT, .pfn = __phys_to_pfn(CS5_BASE_ADDR), .length = CS5_SIZE, diff --git a/arch/arm/mach-mx3/pcm037.c b/arch/arm/mach-mx3/pcm037.c index 6cbaabedf386..5be396917c99 100644 --- a/arch/arm/mach-mx3/pcm037.c +++ b/arch/arm/mach-mx3/pcm037.c @@ -322,16 +322,25 @@ static int pcm037_camera_power(struct device *dev, int on) return 0; } -static struct i2c_board_info pcm037_i2c_2_devices[] = { +static struct i2c_board_info pcm037_i2c_camera[] = { { I2C_BOARD_INFO("mt9t031", 0x5d), + }, { + I2C_BOARD_INFO("mt9v022", 0x48), }, }; -static struct soc_camera_link iclink = { +static struct soc_camera_link iclink_mt9v022 = { + .bus_id = 0, /* Must match with the camera ID */ + .board_info = &pcm037_i2c_camera[1], + .i2c_adapter_id = 2, + .module_name = "mt9v022", +}; + +static struct soc_camera_link iclink_mt9t031 = { .bus_id = 0, /* Must match with the camera ID */ .power = pcm037_camera_power, - .board_info = &pcm037_i2c_2_devices[0], + .board_info = &pcm037_i2c_camera[0], .i2c_adapter_id = 2, .module_name = "mt9t031", }; @@ -345,11 +354,19 @@ static struct i2c_board_info pcm037_i2c_devices[] = { } }; -static struct platform_device pcm037_camera = { +static struct platform_device pcm037_mt9t031 = { .name = "soc-camera-pdrv", .id = 0, .dev = { - .platform_data = &iclink, + .platform_data = &iclink_mt9t031, + }, +}; + +static struct platform_device pcm037_mt9v022 = { + .name = "soc-camera-pdrv", + .id = 1, + .dev = { + .platform_data = &iclink_mt9v022, }, }; @@ -449,7 +466,8 @@ static int __init pcm037_camera_alloc_dma(const size_t buf_size) static struct platform_device *devices[] __initdata = { &pcm037_flash, &pcm037_sram_device, - &pcm037_camera, + &pcm037_mt9t031, + &pcm037_mt9v022, }; static struct ipu_platform_data mx3_ipu_data = { @@ -599,7 +617,7 @@ static void __init mxc_board_init(void) if (!ret) gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), 1); else - iclink.power = NULL; + iclink_mt9t031.power = NULL; if (!pcm037_camera_alloc_dma(4 * 1024 * 1024)) mxc_register_device(&mx3_camera, &camera_pdata); diff --git a/arch/arm/mach-mxc91231/clock.c b/arch/arm/mach-mxc91231/clock.c index ecfa37fef8ad..5c85075d8a56 100644 --- a/arch/arm/mach-mxc91231/clock.c +++ b/arch/arm/mach-mxc91231/clock.c @@ -624,7 +624,6 @@ static struct clk_lookup lookups[] = { int __init mxc91231_clocks_init(unsigned long fref) { void __iomem *gpt_base; - int i; ckih_rate = fref; @@ -632,8 +631,7 @@ int __init mxc91231_clocks_init(unsigned long fref) sdhc_clk[0].parent = clk_sdhc_parent(&sdhc_clk[0]); sdhc_clk[1].parent = clk_sdhc_parent(&sdhc_clk[1]); - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); gpt_base = MXC91231_IO_ADDRESS(MXC91231_GPT1_BASE_ADDR); mxc_timer_init(&gpt_clk, gpt_base, MXC91231_INT_GPT); diff --git a/arch/arm/mach-nomadik/cpu-8815.c b/arch/arm/mach-nomadik/cpu-8815.c index f93c59634191..9bf33b30a025 100644 --- a/arch/arm/mach-nomadik/cpu-8815.c +++ b/arch/arm/mach-nomadik/cpu-8815.c @@ -86,11 +86,19 @@ static struct amba_device cpu8815_amba_gpio[] = { }, }; +static struct amba_device cpu8815_amba_rng = { + .dev = { + .init_name = "rng", + }, + __MEM_4K_RESOURCE(NOMADIK_RNG_BASE), +}; + static struct amba_device *amba_devs[] __initdata = { cpu8815_amba_gpio + 0, cpu8815_amba_gpio + 1, cpu8815_amba_gpio + 2, cpu8815_amba_gpio + 3, + &cpu8815_amba_rng }; static int __init cpu8815_init(void) diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 9ce17f13d3f1..b6a537c875b8 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -3,7 +3,7 @@ # # Common support -obj-y := io.o id.o sram.o irq.o mux.o serial.o devices.o +obj-y := io.o id.o sram.o irq.o mux.o flash.o serial.o devices.o obj-y += clock.o clock_data.o opp_data.o obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index 7e70c3c08da6..096f2ed102cb 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -18,18 +18,19 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/input.h> #include <linux/smc91x.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <plat/tc.h> #include <mach/gpio.h> #include <plat/mux.h> +#include <plat/flash.h> #include <plat/fpga.h> #include <plat/keypad.h> #include <plat/common.h> @@ -150,9 +151,9 @@ static struct mtd_partition nor_partitions[] = { }, }; -static struct flash_platform_data nor_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data nor_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = nor_partitions, .nr_parts = ARRAY_SIZE(nor_partitions), }; @@ -164,7 +165,7 @@ static struct resource nor_resource = { }; static struct platform_device nor_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &nor_data, diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index fa7cecea19f9..d1100e4f65ac 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -26,6 +26,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/input.h> #include <linux/i2c/tps65010.h> #include <linux/smc91x.h> @@ -35,7 +36,6 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <plat/mux.h> @@ -45,6 +45,7 @@ #include <plat/usb.h> #include <plat/keypad.h> #include <plat/common.h> +#include <plat/flash.h> #include "board-h2.h" @@ -121,9 +122,9 @@ static struct mtd_partition h2_nor_partitions[] = { } }; -static struct flash_platform_data h2_nor_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data h2_nor_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = h2_nor_partitions, .nr_parts = ARRAY_SIZE(h2_nor_partitions), }; @@ -134,7 +135,7 @@ static struct resource h2_nor_resource = { }; static struct platform_device h2_nor_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &h2_nor_data, diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 6a7f9c391cf1..a53ab8297d25 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -25,6 +25,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/input.h> #include <linux/spi/spi.h> #include <linux/i2c/tps65010.h> @@ -37,7 +38,6 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <mach/irqs.h> @@ -47,6 +47,7 @@ #include <plat/keypad.h> #include <plat/dma.h> #include <plat/common.h> +#include <plat/flash.h> #include "board-h3.h" @@ -126,9 +127,9 @@ static struct mtd_partition nor_partitions[] = { } }; -static struct flash_platform_data nor_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data nor_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = nor_partitions, .nr_parts = ARRAY_SIZE(nor_partitions), }; @@ -139,7 +140,7 @@ static struct resource nor_resource = { }; static struct platform_device nor_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &nor_data, diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index 2133b006f6a3..5d12fd35681b 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -22,16 +22,17 @@ #include <linux/delay.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/input.h> #include <linux/smc91x.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <plat/mux.h> +#include <plat/flash.h> #include <plat/fpga.h> #include <mach/gpio.h> #include <plat/tc.h> @@ -94,9 +95,9 @@ static struct mtd_partition innovator_partitions[] = { } }; -static struct flash_platform_data innovator_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data innovator_flash_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = innovator_partitions, .nr_parts = ARRAY_SIZE(innovator_partitions), }; @@ -108,7 +109,7 @@ static struct resource innovator_flash_resource = { }; static struct platform_device innovator_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &innovator_flash_data, diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index ccea4f448e9a..80d862001def 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -37,6 +37,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/i2c/tps65010.h> @@ -46,8 +47,8 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> -#include <asm/mach/flash.h> +#include <plat/flash.h> #include <plat/usb.h> #include <plat/mux.h> #include <plat/tc.h> @@ -94,9 +95,9 @@ static struct mtd_partition osk_partitions[] = { } }; -static struct flash_platform_data osk_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data osk_flash_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = osk_partitions, .nr_parts = ARRAY_SIZE(osk_partitions), }; @@ -107,7 +108,7 @@ static struct resource osk_flash_resource = { }; static struct platform_device osk5912_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &osk_flash_data, diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index 9fe887262bdf..569b4c9085cd 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/spi/spi.h> #include <linux/interrupt.h> #include <linux/apm-emulation.h> @@ -31,9 +32,9 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> -#include <asm/mach/flash.h> #include <mach/gpio.h> +#include <plat/flash.h> #include <plat/mux.h> #include <plat/usb.h> #include <plat/tc.h> @@ -126,9 +127,9 @@ static struct mtd_partition palmte_rom_partitions[] = { }, }; -static struct flash_platform_data palmte_rom_data = { - .map_name = "map_rom", +static struct physmap_flash_data palmte_rom_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = palmte_rom_partitions, .nr_parts = ARRAY_SIZE(palmte_rom_partitions), }; @@ -140,7 +141,7 @@ static struct resource palmte_rom_resource = { }; static struct platform_device palmte_rom_device = { - .name = "omapflash", + .name = "physmap-flash", .id = -1, .dev = { .platform_data = &palmte_rom_data, diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c index af068e3e0fe7..6ad49a2cc1a0 100644 --- a/arch/arm/mach-omap1/board-palmtt.c +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -21,16 +21,17 @@ #include <linux/interrupt.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/leds.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 <plat/led.h> #include <mach/gpio.h> +#include <plat/flash.h> #include <plat/mux.h> #include <plat/usb.h> #include <plat/dma.h> @@ -104,9 +105,9 @@ static struct mtd_partition palmtt_partitions[] = { } }; -static struct flash_platform_data palmtt_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data palmtt_flash_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = palmtt_partitions, .nr_parts = ARRAY_SIZE(palmtt_partitions), }; @@ -118,7 +119,7 @@ static struct resource palmtt_flash_resource = { }; static struct platform_device palmtt_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &palmtt_flash_data, diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index c7a3b6f36500..6641de9257ef 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -25,14 +25,15 @@ #include <linux/interrupt.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.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 <mach/gpio.h> +#include <plat/flash.h> #include <plat/mux.h> #include <plat/usb.h> #include <plat/dma.h> @@ -126,10 +127,9 @@ static struct mtd_partition palmz71_rom_partitions[] = { }, }; -static struct flash_platform_data palmz71_rom_data = { - .map_name = "map_rom", - .name = "onboardrom", +static struct physmap_flash_data palmz71_rom_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = palmz71_rom_partitions, .nr_parts = ARRAY_SIZE(palmz71_rom_partitions), }; @@ -141,7 +141,7 @@ static struct resource palmz71_rom_resource = { }; static struct platform_device palmz71_rom_device = { - .name = "omapflash", + .name = "physmap-flash", .id = -1, .dev = { .platform_data = &palmz71_rom_data, diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 1387a4f15da9..e854d5741c88 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -18,19 +18,20 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/input.h> #include <linux/smc91x.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <plat/tc.h> #include <mach/gpio.h> #include <plat/mux.h> #include <plat/fpga.h> +#include <plat/flash.h> #include <plat/keypad.h> #include <plat/common.h> #include <plat/board.h> @@ -117,9 +118,9 @@ static struct mtd_partition nor_partitions[] = { }, }; -static struct flash_platform_data nor_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data nor_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = nor_partitions, .nr_parts = ARRAY_SIZE(nor_partitions), }; @@ -131,7 +132,7 @@ static struct resource nor_resource = { }; static struct platform_device nor_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &nor_data, diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c index 7a97fac83d8d..2fb1e5f8e2ec 100644 --- a/arch/arm/mach-omap1/board-sx1.c +++ b/arch/arm/mach-omap1/board-sx1.c @@ -22,6 +22,7 @@ #include <linux/notifier.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/types.h> #include <linux/i2c.h> #include <linux/errno.h> @@ -29,10 +30,10 @@ #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <mach/gpio.h> +#include <plat/flash.h> #include <plat/mux.h> #include <plat/dma.h> #include <plat/irda.h> @@ -287,9 +288,9 @@ static struct mtd_partition sx1_partitions[] = { } }; -static struct flash_platform_data sx1_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data sx1_flash_data = { .width = 2, + .set_vpp = omap1_set_vpp, .parts = sx1_partitions, .nr_parts = ARRAY_SIZE(sx1_partitions), }; @@ -310,7 +311,7 @@ static struct resource sx1_old_flash_resource[] = { }; static struct platform_device sx1_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &sx1_flash_data, @@ -327,7 +328,7 @@ static struct resource sx1_new_flash_resource = { }; static struct platform_device sx1_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &sx1_flash_data, diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index 169183537997..87b9436fe7c0 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c @@ -18,6 +18,7 @@ #include <linux/irq.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/mtd/physmap.h> #include <linux/notifier.h> #include <linux/reboot.h> #include <linux/serial_8250.h> @@ -27,11 +28,11 @@ #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> -#include <asm/mach/flash.h> #include <asm/mach/map.h> #include <plat/common.h> #include <mach/gpio.h> +#include <plat/flash.h> #include <plat/mux.h> #include <plat/tc.h> #include <plat/usb.h> @@ -86,9 +87,9 @@ static int __init ext_uart_init(void) } arch_initcall(ext_uart_init); -static struct flash_platform_data voiceblue_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data voiceblue_flash_data = { .width = 2, + .set_vpp = omap1_set_vpp, }; static struct resource voiceblue_flash_resource = { @@ -98,7 +99,7 @@ static struct resource voiceblue_flash_resource = { }; static struct platform_device voiceblue_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &voiceblue_flash_data, diff --git a/arch/arm/mach-omap1/clock_data.c b/arch/arm/mach-omap1/clock_data.c index ab995a9c606c..65e7b5b85d83 100644 --- a/arch/arm/mach-omap1/clock_data.c +++ b/arch/arm/mach-omap1/clock_data.c @@ -599,7 +599,7 @@ static struct clk i2c_ick = { static struct omap_clk omap_clks[] = { /* non-ULPD clocks */ CLK(NULL, "ck_ref", &ck_ref, CK_16XX | CK_1510 | CK_310 | CK_7XX), - CLK(NULL, "ck_dpll1", &ck_dpll1, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "ck_dpll1", &ck_dpll1, CK_16XX | CK_1510 | CK_310 | CK_7XX), /* CK_GEN1 clocks */ CLK(NULL, "ck_dpll1out", &ck_dpll1out.clk, CK_16XX), CLK(NULL, "ck_sossi", &sossi_ck, CK_16XX), @@ -627,7 +627,7 @@ static struct omap_clk omap_clks[] = { CLK(NULL, "tc2_ck", &tc2_ck, CK_16XX), CLK(NULL, "dma_ck", &dma_ck, CK_16XX | CK_1510 | CK_310), CLK(NULL, "dma_lcdfree_ck", &dma_lcdfree_ck, CK_16XX), - CLK(NULL, "api_ck", &api_ck.clk, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "api_ck", &api_ck.clk, CK_16XX | CK_1510 | CK_310 | CK_7XX), CLK(NULL, "lb_ck", &lb_ck.clk, CK_1510 | CK_310), CLK(NULL, "rhea1_ck", &rhea1_ck, CK_16XX), CLK(NULL, "rhea2_ck", &rhea2_ck, CK_16XX), @@ -658,6 +658,10 @@ static struct omap_clk omap_clks[] = { CLK("i2c_omap.1", "fck", &i2c_fck, CK_16XX | CK_1510 | CK_310 | CK_7XX), CLK("i2c_omap.1", "ick", &i2c_ick, CK_16XX), CLK("i2c_omap.1", "ick", &dummy_ck, CK_1510 | CK_310 | CK_7XX), + CLK("omap1_spi100k.1", "fck", &dummy_ck, CK_7XX), + CLK("omap1_spi100k.1", "ick", &dummy_ck, CK_7XX), + CLK("omap1_spi100k.2", "fck", &dummy_ck, CK_7XX), + CLK("omap1_spi100k.2", "ick", &dummy_ck, CK_7XX), CLK("omap_uwire", "fck", &armxor_ck.clk, CK_16XX | CK_1510 | CK_310), CLK("omap-mcbsp.1", "ick", &dspper_ck, CK_16XX), CLK("omap-mcbsp.1", "ick", &dummy_ck, CK_1510 | CK_310), @@ -674,7 +678,7 @@ static struct omap_clk omap_clks[] = { * init */ -static struct clk_functions omap1_clk_functions __initdata = { +static struct clk_functions omap1_clk_functions = { .clk_enable = omap1_clk_enable, .clk_disable = omap1_clk_disable, .clk_round_rate = omap1_clk_round_rate, diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index 23ded2d49600..a2d07aa75c9e 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/spi/spi.h> #include <mach/hardware.h> #include <asm/mach/map.h> @@ -23,6 +24,7 @@ #include <plat/mux.h> #include <mach/gpio.h> #include <plat/mmc.h> +#include <plat/omap7xx.h> /*-------------------------------------------------------------------------*/ @@ -196,6 +198,38 @@ void __init omap1_init_mmc(struct omap_mmc_platform_data **mmc_data, /*-------------------------------------------------------------------------*/ +/* OMAP7xx SPI support */ +#if defined(CONFIG_SPI_OMAP_100K) || defined(CONFIG_SPI_OMAP_100K_MODULE) + +struct platform_device omap_spi1 = { + .name = "omap1_spi100k", + .id = 1, +}; + +struct platform_device omap_spi2 = { + .name = "omap1_spi100k", + .id = 2, +}; + +static void omap_init_spi100k(void) +{ + omap_spi1.dev.platform_data = ioremap(OMAP7XX_SPI1_BASE, 0x7ff); + if (omap_spi1.dev.platform_data) + platform_device_register(&omap_spi1); + + omap_spi2.dev.platform_data = ioremap(OMAP7XX_SPI2_BASE, 0x7ff); + if (omap_spi2.dev.platform_data) + platform_device_register(&omap_spi2); +} + +#else +static inline void omap_init_spi100k(void) +{ +} +#endif + +/*-------------------------------------------------------------------------*/ + #if defined(CONFIG_OMAP_STI) #define OMAP1_STI_BASE 0xfffea000 @@ -263,6 +297,7 @@ static int __init omap1_init_devices(void) omap_init_mbox(); omap_init_rtc(); + omap_init_spi100k(); omap_init_sti(); return 0; diff --git a/arch/arm/mach-omap1/flash.c b/arch/arm/mach-omap1/flash.c new file mode 100644 index 000000000000..0b07a78eeaa7 --- /dev/null +++ b/arch/arm/mach-omap1/flash.c @@ -0,0 +1,33 @@ +/* + * Flash support for OMAP1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> + +#include <plat/io.h> +#include <plat/tc.h> + +void omap1_set_vpp(struct map_info *map, int enable) +{ + static int count; + u32 l; + + if (enable) { + if (count++ == 0) { + l = omap_readl(EMIFS_CONFIG); + l |= OMAP_EMIFS_CONFIG_WP; + omap_writel(l, EMIFS_CONFIG); + } + } else { + if (count && (--count == 0)) { + l = omap_readl(EMIFS_CONFIG); + l &= ~OMAP_EMIFS_CONFIG_WP; + omap_writel(l, EMIFS_CONFIG); + } + } +} diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index 07212cc621ae..84341377232d 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -62,6 +62,14 @@ MUX_CFG_7XX("MMC_7XX_DAT0", 2, 17, 0, 16, 1, 0) /* I2C interface */ MUX_CFG_7XX("I2C_7XX_SCL", 5, 1, 0, 0, 1, 0) MUX_CFG_7XX("I2C_7XX_SDA", 5, 5, 0, 0, 1, 0) + +/* SPI pins */ +MUX_CFG_7XX("SPI_7XX_1", 6, 5, 4, 4, 1, 0) +MUX_CFG_7XX("SPI_7XX_2", 6, 9, 4, 8, 1, 0) +MUX_CFG_7XX("SPI_7XX_3", 6, 13, 4, 12, 1, 0) +MUX_CFG_7XX("SPI_7XX_4", 6, 17, 4, 16, 1, 0) +MUX_CFG_7XX("SPI_7XX_5", 8, 25, 0, 24, 0, 0) +MUX_CFG_7XX("SPI_7XX_6", 9, 5, 0, 4, 0, 0) }; #define OMAP7XX_PINS_SZ ARRAY_SIZE(omap7xx_pins) #else diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 10eafa70a909..606bf04f51b6 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -80,6 +80,7 @@ config MACH_OVERO config MACH_OMAP3EVM bool "OMAP 3530 EVM board" depends on ARCH_OMAP3 && ARCH_OMAP34XX + select OMAP_PACKAGE_CBB config MACH_OMAP3517EVM bool "OMAP3517/ AM3517 EVM board" diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c index e508904fb67e..31042ee7e772 100644 --- a/arch/arm/mach-omap2/board-2430sdp.c +++ b/arch/arm/mach-omap2/board-2430sdp.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/delay.h> #include <linux/i2c/twl.h> #include <linux/err.h> @@ -28,7 +29,6 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> -#include <asm/mach/flash.h> #include <mach/gpio.h> #include <plat/mux.h> @@ -74,8 +74,7 @@ static struct mtd_partition sdp2430_partitions[] = { } }; -static struct flash_platform_data sdp2430_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data sdp2430_flash_data = { .width = 2, .parts = sdp2430_partitions, .nr_parts = ARRAY_SIZE(sdp2430_partitions), @@ -88,7 +87,7 @@ static struct resource sdp2430_flash_resource = { }; static struct platform_device sdp2430_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &sdp2430_flash_data, diff --git a/arch/arm/mach-omap2/board-3630sdp.c b/arch/arm/mach-omap2/board-3630sdp.c index 739059632811..739059632811 100755..100644 --- a/arch/arm/mach-omap2/board-3630sdp.c +++ b/arch/arm/mach-omap2/board-3630sdp.c diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index cfb7f1257d20..ffb005101fa8 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/i2c.h> @@ -29,7 +30,6 @@ #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> -#include <asm/mach/flash.h> #include <plat/control.h> #include <mach/gpio.h> @@ -115,8 +115,7 @@ static struct mtd_partition h4_partitions[] = { } }; -static struct flash_platform_data h4_flash_data = { - .map_name = "cfi_probe", +static struct physmap_flash_data h4_flash_data = { .width = 2, .parts = h4_partitions, .nr_parts = ARRAY_SIZE(h4_partitions), @@ -127,7 +126,7 @@ static struct resource h4_flash_resource = { }; static struct platform_device h4_flash_device = { - .name = "omapflash", + .name = "physmap-flash", .id = 0, .dev = { .platform_data = &h4_flash_data, diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index 8dd277c36661..5c8474c75a34 100755..100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -24,6 +24,7 @@ #include <plat/common.h> #include <plat/usb.h> +#include "mux.h" #include "mmc-twl4030.h" /* Zoom2 has Qwerty keyboard*/ @@ -63,21 +64,21 @@ static int board_keymap[] = { KEY(5, 1, KEY_H), KEY(5, 2, KEY_J), KEY(5, 3, KEY_F3), + KEY(5, 4, KEY_UNKNOWN), KEY(5, 5, KEY_VOLUMEDOWN), KEY(5, 6, KEY_M), - KEY(5, 7, KEY_ENTER), + KEY(5, 7, KEY_RIGHT), KEY(6, 0, KEY_Q), KEY(6, 1, KEY_A), KEY(6, 2, KEY_N), KEY(6, 3, KEY_BACKSPACE), KEY(6, 6, KEY_P), - KEY(6, 7, KEY_SELECT), + KEY(6, 7, KEY_UP), KEY(7, 0, KEY_PROG1), /*MACRO 1 <User defined> */ KEY(7, 1, KEY_PROG2), /*MACRO 2 <User defined> */ KEY(7, 2, KEY_PROG3), /*MACRO 3 <User defined> */ KEY(7, 3, KEY_PROG4), /*MACRO 4 <User defined> */ - KEY(7, 5, KEY_RIGHT), - KEY(7, 6, KEY_UP), + KEY(7, 6, KEY_SELECT), KEY(7, 7, KEY_DOWN) }; @@ -263,9 +264,17 @@ static int __init omap_i2c_init(void) return 0; } +static void enable_board_wakeup_source(void) +{ + /* T2 interrupt line (keypad) */ + omap_mux_init_signal("sys_nirq", + OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP); +} + void __init zoom_peripherals_init(void) { omap_i2c_init(); omap_serial_init(); usb_musb_init(); + enable_board_wakeup_source(); } diff --git a/arch/arm/mach-omap2/board-zoom3.c b/arch/arm/mach-omap2/board-zoom3.c index a9fe9181b010..6512b2143f32 100644 --- a/arch/arm/mach-omap2/board-zoom3.c +++ b/arch/arm/mach-omap2/board-zoom3.c @@ -20,6 +20,7 @@ #include <plat/common.h> #include <plat/board.h> +#include <plat/usb.h> #include "mux.h" #include "sdram-hynix-h8mbx00u0mer-0em.h" @@ -51,11 +52,24 @@ static struct omap_board_mux board_mux[] __initdata = { #define board_mux NULL #endif +static struct ehci_hcd_omap_platform_data ehci_pdata __initconst = { + .port_mode[0] = EHCI_HCD_OMAP_MODE_UNKNOWN, + .port_mode[1] = EHCI_HCD_OMAP_MODE_PHY, + .port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN, + .phy_reset = true, + .reset_gpio_port[0] = -EINVAL, + .reset_gpio_port[1] = 64, + .reset_gpio_port[2] = -EINVAL, +}; + static void __init omap_zoom_init(void) { omap3_mux_init(board_mux, OMAP_PACKAGE_CBP); zoom_peripherals_init(); zoom_debugboard_init(); + + omap_mux_init_gpio(64, OMAP_PIN_OUTPUT); + usb_ehci_init(&ehci_pdata); } MACHINE_START(OMAP_ZOOM3, "OMAP Zoom3 board") diff --git a/arch/arm/mach-omap2/clock2xxx.c b/arch/arm/mach-omap2/clock2xxx.c index d0e3fb7f9298..5420356eb407 100644 --- a/arch/arm/mach-omap2/clock2xxx.c +++ b/arch/arm/mach-omap2/clock2xxx.c @@ -449,40 +449,78 @@ int omap2_select_table_rate(struct clk *clk, unsigned long rate) #ifdef CONFIG_CPU_FREQ /* * Walk PRCM rate table and fillout cpufreq freq_table + * XXX This should be replaced by an OPP layer in the near future */ -static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(rate_table)]; +static struct cpufreq_frequency_table *freq_table; void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table) { - struct prcm_config *prcm; + const struct prcm_config *prcm; + long sys_ck_rate; int i = 0; + int tbl_sz = 0; + + sys_ck_rate = clk_get_rate(sclk); for (prcm = rate_table; prcm->mpu_speed; prcm++) { if (!(prcm->flags & cpu_mask)) continue; - if (prcm->xtal_speed != sys_ck.rate) + if (prcm->xtal_speed != sys_ck_rate) continue; /* don't put bypass rates in table */ if (prcm->dpll_speed == prcm->xtal_speed) continue; - freq_table[i].index = i; - freq_table[i].frequency = prcm->mpu_speed / 1000; - i++; + tbl_sz++; } - if (i == 0) { - printk(KERN_WARNING "%s: failed to initialize frequency " - "table\n", __func__); + /* + * XXX Ensure that we're doing what CPUFreq expects for this error + * case and the following one + */ + if (tbl_sz == 0) { + pr_warning("%s: no matching entries in rate_table\n", + __func__); + return; + } + + /* Include the CPUFREQ_TABLE_END terminator entry */ + tbl_sz++; + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * tbl_sz, + GFP_ATOMIC); + if (!freq_table) { + pr_err("%s: could not kzalloc frequency table\n", __func__); return; } + for (prcm = rate_table; prcm->mpu_speed; prcm++) { + if (!(prcm->flags & cpu_mask)) + continue; + if (prcm->xtal_speed != sys_ck_rate) + continue; + + /* don't put bypass rates in table */ + if (prcm->dpll_speed == prcm->xtal_speed) + continue; + + freq_table[i].index = i; + freq_table[i].frequency = prcm->mpu_speed / 1000; + i++; + } + freq_table[i].index = i; freq_table[i].frequency = CPUFREQ_TABLE_END; *table = &freq_table[0]; } + +void omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table **table) +{ + kfree(freq_table); +} + #endif struct clk_functions omap2_clk_functions = { @@ -494,6 +532,7 @@ struct clk_functions omap2_clk_functions = { .clk_disable_unused = omap2_clk_disable_unused, #ifdef CONFIG_CPU_FREQ .clk_init_cpufreq_table = omap2_clk_init_cpufreq_table, + .clk_exit_cpufreq_table = omap2_clk_exit_cpufreq_table, #endif }; diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c index ded32364f32b..d4217b93e10b 100644 --- a/arch/arm/mach-omap2/clock34xx.c +++ b/arch/arm/mach-omap2/clock34xx.c @@ -34,7 +34,6 @@ #include <asm/div64.h> #include <asm/clkdev.h> -#include <plat/sdrc.h> #include "clock.h" #include "clock34xx.h" #include "sdrc.h" diff --git a/arch/arm/mach-omap2/clock34xx_data.c b/arch/arm/mach-omap2/clock34xx_data.c index 8bdcc9cc7f9a..c6031d74d6f6 100644 --- a/arch/arm/mach-omap2/clock34xx_data.c +++ b/arch/arm/mach-omap2/clock34xx_data.c @@ -776,6 +776,8 @@ static struct clk dpll4_m5_ck = { .clksel_mask = OMAP3430_CLKSEL_CAM_MASK, .clksel = div16_dpll4_clksel, .clkdm_name = "dpll4_clkdm", + .set_rate = &omap2_clksel_set_rate, + .round_rate = &omap2_clksel_round_rate, .recalc = &omap2_clksel_recalc, }; @@ -1500,6 +1502,7 @@ static struct clk uart2_fck = { .parent = &core_48m_fck, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430_EN_UART2_SHIFT, + .clkdm_name = "core_l4_clkdm", .recalc = &followparent_recalc, }; @@ -1509,6 +1512,7 @@ static struct clk uart1_fck = { .parent = &core_48m_fck, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430_EN_UART1_SHIFT, + .clkdm_name = "core_l4_clkdm", .recalc = &followparent_recalc, }; @@ -2745,7 +2749,7 @@ static struct clk mcbsp4_ick = { }; static const struct clksel mcbsp_234_clksel[] = { - { .parent = &core_96m_fck, .rates = common_mcbsp_96m_rates }, + { .parent = &per_96m_fck, .rates = common_mcbsp_96m_rates }, { .parent = &mcbsp_clks, .rates = common_mcbsp_mcbsp_rates }, { .parent = NULL } }; diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 1a45ed1e8ba1..dd285f001467 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c @@ -559,7 +559,7 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) * downstream clocks for debugging purposes? */ - if (!clkdm || !clk) + if (!clkdm || !clk || !clkdm->clktrctrl_mask) return -EINVAL; if (atomic_inc_return(&clkdm->usecount) > 1) @@ -610,7 +610,7 @@ int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) * downstream clocks for debugging purposes? */ - if (!clkdm || !clk) + if (!clkdm || !clk || !clkdm->clktrctrl_mask) return -EINVAL; #ifdef DEBUG diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index a8749e8017b9..5a7996402c53 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -33,7 +33,6 @@ #include <plat/sdrc.h> #include <plat/gpmc.h> #include <plat/serial.h> -#include <plat/mux.h> #include <plat/vram.h> #include "clock.h" @@ -73,21 +72,21 @@ static struct map_desc omap24xx_io_desc[] __initdata = { #ifdef CONFIG_ARCH_OMAP2420 static struct map_desc omap242x_io_desc[] __initdata = { { - .virtual = DSP_MEM_24XX_VIRT, - .pfn = __phys_to_pfn(DSP_MEM_24XX_PHYS), - .length = DSP_MEM_24XX_SIZE, + .virtual = DSP_MEM_2420_VIRT, + .pfn = __phys_to_pfn(DSP_MEM_2420_PHYS), + .length = DSP_MEM_2420_SIZE, .type = MT_DEVICE }, { - .virtual = DSP_IPI_24XX_VIRT, - .pfn = __phys_to_pfn(DSP_IPI_24XX_PHYS), - .length = DSP_IPI_24XX_SIZE, + .virtual = DSP_IPI_2420_VIRT, + .pfn = __phys_to_pfn(DSP_IPI_2420_PHYS), + .length = DSP_IPI_2420_SIZE, .type = MT_DEVICE }, { - .virtual = DSP_MMU_24XX_VIRT, - .pfn = __phys_to_pfn(DSP_MMU_24XX_PHYS), - .length = DSP_MMU_24XX_SIZE, + .virtual = DSP_MMU_2420_VIRT, + .pfn = __phys_to_pfn(DSP_MMU_2420_PHYS), + .length = DSP_MMU_2420_SIZE, .type = MT_DEVICE }, }; diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index e071b3fd1878..a8febd3cea17 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -977,6 +977,38 @@ static void __init omap_mux_init_list(struct omap_mux *superset) } } +#ifdef CONFIG_OMAP_MUX + +static void omap_mux_init_package(struct omap_mux *superset, + struct omap_mux *package_subset, + struct omap_ball *package_balls) +{ + if (package_subset) + omap_mux_package_fixup(package_subset, superset); + if (package_balls) + omap_mux_package_init_balls(package_balls, superset); +} + +static void omap_mux_init_signals(struct omap_board_mux *board_mux) +{ + omap_mux_set_cmdline_signals(); + omap_mux_set_board_signals(board_mux); +} + +#else + +static void omap_mux_init_package(struct omap_mux *superset, + struct omap_mux *package_subset, + struct omap_ball *package_balls) +{ +} + +static void omap_mux_init_signals(struct omap_board_mux *board_mux) +{ +} + +#endif + int __init omap_mux_init(u32 mux_pbase, u32 mux_size, struct omap_mux *superset, struct omap_mux *package_subset, @@ -993,14 +1025,9 @@ int __init omap_mux_init(u32 mux_pbase, u32 mux_size, return -ENODEV; } -#ifdef CONFIG_OMAP_MUX - omap_mux_package_fixup(package_subset, superset); - omap_mux_package_init_balls(package_balls, superset); - omap_mux_set_cmdline_signals(); - omap_mux_set_board_signals(board_mux); -#endif - + omap_mux_init_package(superset, package_subset, package_balls); omap_mux_init_list(superset); + omap_mux_init_signals(board_mux); return 0; } diff --git a/arch/arm/mach-omap2/opp2420_data.c b/arch/arm/mach-omap2/opp2420_data.c index 126a9396b3a8..e6dda694fd5c 100644 --- a/arch/arm/mach-omap2/opp2420_data.c +++ b/arch/arm/mach-omap2/opp2420_data.c @@ -9,45 +9,47 @@ * The OMAP2 processor can be run at several discrete 'PRCM configurations'. * These configurations are characterized by voltage and speed for clocks. * The device is only validated for certain combinations. One way to express - * these combinations is via the 'ratio's' which the clocks operate with + * these combinations is via the 'ratios' which the clocks operate with * respect to each other. These ratio sets are for a given voltage/DPLL - * setting. All configurations can be described by a DPLL setting and a ratio - * There are 3 ratio sets for the 2430 and X ratio sets for 2420. - * - * 2430 differs from 2420 in that there are no more phase synchronizers used. - * They both have a slightly different clock domain setup. 2420(iva1,dsp) vs - * 2430 (iva2.1, NOdsp, mdm) + * setting. All configurations can be described by a DPLL setting and a ratio. * * XXX Missing voltage data. + * XXX Missing 19.2MHz sys_clk rate sets (needed for N800/N810) * * THe format described in this file is deprecated. Once a reasonable * OPP API exists, the data in this file should be converted to use it. * * This is technically part of the OMAP2xxx clock code. + * + * Considerable work is still needed to fully support dynamic frequency + * changes on OMAP2xxx-series chips. Readers interested in such a + * project are encouraged to review the Maemo Diablo RX-34 and RX-44 + * kernel source at: + * http://repository.maemo.org/pool/diablo/free/k/kernel-source-diablo/ */ #include "opp2xxx.h" #include "sdrc.h" #include "clock.h" -/*------------------------------------------------------------------------- - * Key dividers which make up a PRCM set. Ratio's for a PRCM are mandated. +/* + * Key dividers which make up a PRCM set. Ratios for a PRCM are mandated. * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU, * CM_CLKSEL_DSP, CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL, * CM_CLKSEL2_PLL, CM_CLKSEL_MDM * - * Filling in table based on H4 boards and 2430-SDPs variants available. - * There are quite a few more rates combinations which could be defined. + * Filling in table based on H4 boards available. There are quite a + * few more rate combinations which could be defined. * - * When multiple values are defined the start up will try and choose the - * fastest one. If a 'fast' value is defined, then automatically, the /2 - * one should be included as it can be used. Generally having more that - * one fast set does not make sense, as static timings need to be changed - * to change the set. The exception is the bypass setting which is - * availble for low power bypass. + * When multiple values are defined the start up will try and choose + * the fastest one. If a 'fast' value is defined, then automatically, + * the /2 one should be included as it can be used. Generally having + * more than one fast set does not make sense, as static timings need + * to be changed to change the set. The exception is the bypass + * setting which is available for low power bypass. * * Note: This table needs to be sorted, fastest to slowest. - *-------------------------------------------------------------------------*/ + **/ const struct prcm_config omap2420_rate_table[] = { /* PRCM I - FAST */ {S12M, S660M, S330M, RI_CM_CLKSEL_MPU_VAL, /* 330MHz ARM */ diff --git a/arch/arm/mach-omap2/opp2430_data.c b/arch/arm/mach-omap2/opp2430_data.c index edb81672c844..1b9596ae201e 100644 --- a/arch/arm/mach-omap2/opp2430_data.c +++ b/arch/arm/mach-omap2/opp2430_data.c @@ -1,5 +1,5 @@ /* - * opp2420_data.c - old-style "OPP" table for OMAP2420 + * opp2430_data.c - old-style "OPP" table for OMAP2430 * * Copyright (C) 2005-2009 Texas Instruments, Inc. * Copyright (C) 2004-2009 Nokia Corporation @@ -9,16 +9,16 @@ * The OMAP2 processor can be run at several discrete 'PRCM configurations'. * These configurations are characterized by voltage and speed for clocks. * The device is only validated for certain combinations. One way to express - * these combinations is via the 'ratio's' which the clocks operate with + * these combinations is via the 'ratios' which the clocks operate with * respect to each other. These ratio sets are for a given voltage/DPLL - * setting. All configurations can be described by a DPLL setting and a ratio - * There are 3 ratio sets for the 2430 and X ratio sets for 2420. + * setting. All configurations can be described by a DPLL setting and a ratio. * * 2430 differs from 2420 in that there are no more phase synchronizers used. * They both have a slightly different clock domain setup. 2420(iva1,dsp) vs * 2430 (iva2.1, NOdsp, mdm) * * XXX Missing voltage data. + * XXX Missing 19.2MHz sys_clk rate sets. * * THe format described in this file is deprecated. Once a reasonable * OPP API exists, the data in this file should be converted to use it. @@ -30,24 +30,24 @@ #include "sdrc.h" #include "clock.h" -/*------------------------------------------------------------------------- - * Key dividers which make up a PRCM set. Ratio's for a PRCM are mandated. +/* + * Key dividers which make up a PRCM set. Ratios for a PRCM are mandated. * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU, * CM_CLKSEL_DSP, CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL, * CM_CLKSEL2_PLL, CM_CLKSEL_MDM * - * Filling in table based on H4 boards and 2430-SDPs variants available. - * There are quite a few more rates combinations which could be defined. + * Filling in table based on 2430-SDPs variants available. There are + * quite a few more rate combinations which could be defined. * - * When multiple values are defined the start up will try and choose the - * fastest one. If a 'fast' value is defined, then automatically, the /2 - * one should be included as it can be used. Generally having more that - * one fast set does not make sense, as static timings need to be changed - * to change the set. The exception is the bypass setting which is - * availble for low power bypass. + * When multiple values are defined the start up will try and choose + * the fastest one. If a 'fast' value is defined, then automatically, + * the /2 one should be included as it can be used. Generally having + * more than one fast set does not make sense, as static timings need + * to be changed to change the set. The exception is the bypass + * setting which is available for low power bypass. * * Note: This table needs to be sorted, fastest to slowest. - *-------------------------------------------------------------------------*/ + */ const struct prcm_config omap2430_rate_table[] = { /* PRCM #4 - ratio2 (ES2.1) - FAST */ {S13M, S798M, S399M, R2_CM_CLKSEL_MPU_VAL, /* 399MHz ARM */ diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 81ed252a0f8a..c6cc809afb79 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -124,8 +124,8 @@ static void omap3_core_save_context(void) control_padconf_off |= START_PADCONF_SAVE; omap_ctrl_writel(control_padconf_off, OMAP343X_CONTROL_PADCONF_OFF); /* wait for the save to complete */ - while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) - & PADCONF_SAVE_DONE) + while (!(omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) + & PADCONF_SAVE_DONE)) ; /* Save the Interrupt controller context */ omap_intc_save_context(); diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 19805a7de06c..837b34757ffc 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -125,6 +125,13 @@ static struct plat_serial8250_port serial_platform_data3[] = { } }; #endif +static inline unsigned int __serial_read_reg(struct uart_port *up, + int offset) +{ + offset <<= up->regshift; + return (unsigned int)__raw_readb(up->membase + offset); +} + static inline unsigned int serial_read_reg(struct plat_serial8250_port *up, int offset) { @@ -583,11 +590,12 @@ static unsigned int serial_in_override(struct uart_port *up, int offset) { if (UART_RX == offset) { unsigned int lsr; - lsr = serial_read_reg(omap_uart[up->line].p, UART_LSR); + lsr = __serial_read_reg(up, UART_LSR); if (!(lsr & UART_LSR_DR)) return -EPERM; } - return serial_read_reg(omap_uart[up->line].p, offset); + + return __serial_read_reg(up, offset); } void __init omap_serial_early_init(void) @@ -686,15 +694,16 @@ void __init omap_serial_init_port(int port) DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout); } - /* omap44xx: Never read empty UART fifo - * omap3xxx: Never read empty UART fifo on UARTs - * with IP rev >=0x52 - */ - if (cpu_is_omap44xx()) - uart->p->serial_in = serial_in_override; - else if ((serial_read_reg(uart->p, UART_OMAP_MVER) & 0xFF) - >= UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV) - uart->p->serial_in = serial_in_override; + /* + * omap44xx: Never read empty UART fifo + * omap3xxx: Never read empty UART fifo on UARTs + * with IP rev >=0x52 + */ + if (cpu_is_omap44xx()) + uart->p->serial_in = serial_in_override; + else if ((serial_read_reg(uart->p, UART_OMAP_MVER) & 0xFF) + >= UART_OMAP_NO_EMPTY_FIFO_READ_IP_REV) + uart->p->serial_in = serial_in_override; } /** diff --git a/arch/arm/mach-pnx4008/clock.c b/arch/arm/mach-pnx4008/clock.c index 898c0e88acbc..9d1975fa4d9f 100644 --- a/arch/arm/mach-pnx4008/clock.c +++ b/arch/arm/mach-pnx4008/clock.c @@ -22,8 +22,9 @@ #include <linux/delay.h> #include <linux/io.h> -#include <mach/hardware.h> +#include <asm/clkdev.h> +#include <mach/hardware.h> #include <mach/clock.h> #include "clock.h" @@ -56,18 +57,19 @@ static void propagate_rate(struct clk *clk) } } -static inline void clk_reg_disable(struct clk *clk) +static void clk_reg_disable(struct clk *clk) { if (clk->enable_reg) __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_shift), clk->enable_reg); } -static inline void clk_reg_enable(struct clk *clk) +static int clk_reg_enable(struct clk *clk) { if (clk->enable_reg) __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_shift), clk->enable_reg); + return 0; } static inline void clk_reg_disable1(struct clk *clk) @@ -636,31 +638,34 @@ static struct clk flash_ck = { static struct clk i2c0_ck = { .name = "i2c0_ck", .parent = &per_ck, - .flags = NEEDS_INITIALIZATION, - .round_rate = &on_off_round_rate, - .set_rate = &on_off_set_rate, + .flags = NEEDS_INITIALIZATION | FIXED_RATE, .enable_shift = 0, .enable_reg = I2CCLKCTRL_REG, + .rate = 13000000, + .enable = clk_reg_enable, + .disable = clk_reg_disable, }; static struct clk i2c1_ck = { .name = "i2c1_ck", .parent = &per_ck, - .flags = NEEDS_INITIALIZATION, - .round_rate = &on_off_round_rate, - .set_rate = &on_off_set_rate, + .flags = NEEDS_INITIALIZATION | FIXED_RATE, .enable_shift = 1, .enable_reg = I2CCLKCTRL_REG, + .rate = 13000000, + .enable = clk_reg_enable, + .disable = clk_reg_disable, }; static struct clk i2c2_ck = { .name = "i2c2_ck", .parent = &per_ck, - .flags = NEEDS_INITIALIZATION, - .round_rate = &on_off_round_rate, - .set_rate = &on_off_set_rate, + .flags = NEEDS_INITIALIZATION | FIXED_RATE, .enable_shift = 2, .enable_reg = USB_OTG_CLKCTRL_REG, + .rate = 13000000, + .enable = clk_reg_enable, + .disable = clk_reg_disable, }; static struct clk spi0_ck = { @@ -738,16 +743,16 @@ static struct clk wdt_ck = { .name = "wdt_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, - .round_rate = &on_off_round_rate, - .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = TIMCLKCTRL_REG, + .enable = clk_reg_enable, + .disable = clk_reg_disable, }; /* These clocks are visible outside this module * and can be initialized */ -static struct clk *onchip_clks[] = { +static struct clk *onchip_clks[] __initdata = { &ck_13MHz, &ck_pll1, &ck_pll4, @@ -777,49 +782,74 @@ static struct clk *onchip_clks[] = { &wdt_ck, }; -static int local_clk_enable(struct clk *clk) -{ - int ret = 0; - - if (!(clk->flags & FIXED_RATE) && !clk->rate && clk->set_rate - && clk->user_rate) - ret = clk->set_rate(clk, clk->user_rate); - return ret; -} +static struct clk_lookup onchip_clkreg[] = { + { .clk = &ck_13MHz, .con_id = "ck_13MHz" }, + { .clk = &ck_pll1, .con_id = "ck_pll1" }, + { .clk = &ck_pll4, .con_id = "ck_pll4" }, + { .clk = &ck_pll5, .con_id = "ck_pll5" }, + { .clk = &ck_pll3, .con_id = "ck_pll3" }, + { .clk = &vfp9_ck, .con_id = "vfp9_ck" }, + { .clk = &m2hclk_ck, .con_id = "m2hclk_ck" }, + { .clk = &hclk_ck, .con_id = "hclk_ck" }, + { .clk = &dma_ck, .con_id = "dma_ck" }, + { .clk = &flash_ck, .con_id = "flash_ck" }, + { .clk = &dum_ck, .con_id = "dum_ck" }, + { .clk = &keyscan_ck, .con_id = "keyscan_ck" }, + { .clk = &pwm1_ck, .con_id = "pwm1_ck" }, + { .clk = &pwm2_ck, .con_id = "pwm2_ck" }, + { .clk = &jpeg_ck, .con_id = "jpeg_ck" }, + { .clk = &ms_ck, .con_id = "ms_ck" }, + { .clk = &touch_ck, .con_id = "touch_ck" }, + { .clk = &i2c0_ck, .dev_id = "pnx-i2c.0" }, + { .clk = &i2c1_ck, .dev_id = "pnx-i2c.1" }, + { .clk = &i2c2_ck, .dev_id = "pnx-i2c.2" }, + { .clk = &spi0_ck, .con_id = "spi0_ck" }, + { .clk = &spi1_ck, .con_id = "spi1_ck" }, + { .clk = &uart3_ck, .con_id = "uart3_ck" }, + { .clk = &uart4_ck, .con_id = "uart4_ck" }, + { .clk = &uart5_ck, .con_id = "uart5_ck" }, + { .clk = &uart6_ck, .con_id = "uart6_ck" }, + { .clk = &wdt_ck, .dev_id = "pnx4008-watchdog" }, +}; static void local_clk_disable(struct clk *clk) { - if (!(clk->flags & FIXED_RATE) && clk->rate && clk->set_rate) - clk->set_rate(clk, 0); -} + if (WARN_ON(clk->usecount == 0)) + return; -static void local_clk_unuse(struct clk *clk) -{ - if (clk->usecount > 0 && !(--clk->usecount)) { - local_clk_disable(clk); + if (!(--clk->usecount)) { + if (clk->disable) + clk->disable(clk); + else if (!(clk->flags & FIXED_RATE) && clk->rate && clk->set_rate) + clk->set_rate(clk, 0); if (clk->parent) - local_clk_unuse(clk->parent); + local_clk_disable(clk->parent); } } -static int local_clk_use(struct clk *clk) +static int local_clk_enable(struct clk *clk) { int ret = 0; - if (clk->usecount++ == 0) { - if (clk->parent) - ret = local_clk_use(clk->parent); - if (ret != 0) { - clk->usecount--; - goto out; + if (clk->usecount == 0) { + if (clk->parent) { + ret = local_clk_enable(clk->parent); + if (ret != 0) + goto out; } - ret = local_clk_enable(clk); + if (clk->enable) + ret = clk->enable(clk); + else if (!(clk->flags & FIXED_RATE) && !clk->rate && clk->set_rate + && clk->user_rate) + ret = clk->set_rate(clk, clk->user_rate); if (ret != 0 && clk->parent) { - local_clk_unuse(clk->parent); - clk->usecount--; + local_clk_disable(clk->parent); + goto out; } + + clk->usecount++; } out: return ret; @@ -866,35 +896,6 @@ out: EXPORT_SYMBOL(clk_set_rate); -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *clk = ERR_PTR(-ENOENT); - struct clk **clkp; - - clock_lock(); - for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); - clkp++) { - if (strcmp(id, (*clkp)->name) == 0 - && try_module_get((*clkp)->owner)) { - clk = (*clkp); - break; - } - } - clock_unlock(); - - return clk; -} -EXPORT_SYMBOL(clk_get); - -void clk_put(struct clk *clk) -{ - clock_lock(); - if (clk && !IS_ERR(clk)) - module_put(clk->owner); - clock_unlock(); -} -EXPORT_SYMBOL(clk_put); - unsigned long clk_get_rate(struct clk *clk) { unsigned long ret; @@ -907,10 +908,10 @@ EXPORT_SYMBOL(clk_get_rate); int clk_enable(struct clk *clk) { - int ret = 0; + int ret; clock_lock(); - ret = local_clk_use(clk); + ret = local_clk_enable(clk); clock_unlock(); return ret; } @@ -920,7 +921,7 @@ EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { clock_lock(); - local_clk_unuse(clk); + local_clk_disable(clk); clock_unlock(); } @@ -967,18 +968,24 @@ static int __init clk_init(void) for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); clkp++) { - if (((*clkp)->flags & NEEDS_INITIALIZATION) - && ((*clkp)->set_rate)) { - (*clkp)->user_rate = (*clkp)->rate; - local_set_rate((*clkp), (*clkp)->user_rate); - if ((*clkp)->set_parent) - (*clkp)->set_parent((*clkp), (*clkp)->parent); + struct clk *clk = *clkp; + if (clk->flags & NEEDS_INITIALIZATION) { + if (clk->set_rate) { + clk->user_rate = clk->rate; + local_set_rate(clk, clk->user_rate); + if (clk->set_parent) + clk->set_parent(clk, clk->parent); + } + if (clk->enable && clk->usecount) + clk->enable(clk); + if (clk->disable && !clk->usecount) + clk->disable(clk); } pr_debug("%s: clock %s, rate %ld\n", - __func__, (*clkp)->name, (*clkp)->rate); + __func__, clk->name, clk->rate); } - local_clk_use(&ck_pll4); + local_clk_enable(&ck_pll4); /* if ck_13MHz is not used, disable it. */ if (ck_13MHz.usecount == 0) @@ -987,6 +994,8 @@ static int __init clk_init(void) /* Disable autoclocking */ __raw_writeb(0xff, AUTOCLK_CTRL); + clkdev_add_table(onchip_clkreg, ARRAY_SIZE(onchip_clkreg)); + return 0; } diff --git a/arch/arm/mach-pnx4008/clock.h b/arch/arm/mach-pnx4008/clock.h index cd58f372cfd0..39720d6c0d01 100644 --- a/arch/arm/mach-pnx4008/clock.h +++ b/arch/arm/mach-pnx4008/clock.h @@ -14,8 +14,6 @@ #define __ARCH_ARM_PNX4008_CLOCK_H__ struct clk { - struct list_head node; - struct module *owner; const char *name; struct clk *parent; struct clk *propagate_next; @@ -29,9 +27,11 @@ struct clk { u8 enable_shift1; u32 enable_reg1; u32 parent_switch_reg; - u32(*round_rate) (struct clk *, u32); + u32(*round_rate) (struct clk *, u32); int (*set_rate) (struct clk *, u32); int (*set_parent) (struct clk * clk, struct clk * parent); + int (*enable)(struct clk *); + void (*disable)(struct clk *); }; /* Flags */ diff --git a/arch/arm/mach-pnx4008/i2c.c b/arch/arm/mach-pnx4008/i2c.c index f3fea29c00d3..8103f9644e2d 100644 --- a/arch/arm/mach-pnx4008/i2c.c +++ b/arch/arm/mach-pnx4008/i2c.c @@ -18,120 +18,24 @@ #include <mach/irqs.h> #include <mach/i2c.h> -static int set_clock_run(struct platform_device *pdev) -{ - struct clk *clk; - char name[10]; - int retval = 0; - - snprintf(name, 10, "i2c%d_ck", pdev->id); - clk = clk_get(&pdev->dev, name); - if (!IS_ERR(clk)) { - clk_set_rate(clk, 1); - clk_put(clk); - } else - retval = -ENOENT; - - return retval; -} - -static int set_clock_stop(struct platform_device *pdev) -{ - struct clk *clk; - char name[10]; - int retval = 0; - - snprintf(name, 10, "i2c%d_ck", pdev->id); - clk = clk_get(&pdev->dev, name); - if (!IS_ERR(clk)) { - clk_set_rate(clk, 0); - clk_put(clk); - } else - retval = -ENOENT; - - return retval; -} - -static int i2c_pnx_suspend(struct platform_device *pdev, pm_message_t state) -{ - int retval = 0; -#ifdef CONFIG_PM - retval = set_clock_run(pdev); -#endif - return retval; -} - -static int i2c_pnx_resume(struct platform_device *pdev) -{ - int retval = 0; -#ifdef CONFIG_PM - retval = set_clock_run(pdev); -#endif - return retval; -} - -static u32 calculate_input_freq(struct platform_device *pdev) -{ - return HCLK_MHZ; -} - - -static struct i2c_pnx_algo_data pnx_algo_data0 = { +static struct i2c_pnx_data i2c0_data = { + .name = I2C_CHIP_NAME "0", .base = PNX4008_I2C1_BASE, .irq = I2C_1_INT, }; -static struct i2c_pnx_algo_data pnx_algo_data1 = { +static struct i2c_pnx_data i2c1_data = { + .name = I2C_CHIP_NAME "1", .base = PNX4008_I2C2_BASE, .irq = I2C_2_INT, }; -static struct i2c_pnx_algo_data pnx_algo_data2 = { +static struct i2c_pnx_data i2c2_data = { + .name = "USB-I2C", .base = (PNX4008_USB_CONFIG_BASE + 0x300), .irq = USB_I2C_INT, }; -static struct i2c_adapter pnx_adapter0 = { - .name = I2C_CHIP_NAME "0", - .algo_data = &pnx_algo_data0, -}; -static struct i2c_adapter pnx_adapter1 = { - .name = I2C_CHIP_NAME "1", - .algo_data = &pnx_algo_data1, -}; - -static struct i2c_adapter pnx_adapter2 = { - .name = "USB-I2C", - .algo_data = &pnx_algo_data2, -}; - -static struct i2c_pnx_data i2c0_data = { - .suspend = i2c_pnx_suspend, - .resume = i2c_pnx_resume, - .calculate_input_freq = calculate_input_freq, - .set_clock_run = set_clock_run, - .set_clock_stop = set_clock_stop, - .adapter = &pnx_adapter0, -}; - -static struct i2c_pnx_data i2c1_data = { - .suspend = i2c_pnx_suspend, - .resume = i2c_pnx_resume, - .calculate_input_freq = calculate_input_freq, - .set_clock_run = set_clock_run, - .set_clock_stop = set_clock_stop, - .adapter = &pnx_adapter1, -}; - -static struct i2c_pnx_data i2c2_data = { - .suspend = i2c_pnx_suspend, - .resume = i2c_pnx_resume, - .calculate_input_freq = calculate_input_freq, - .set_clock_run = set_clock_run, - .set_clock_stop = set_clock_stop, - .adapter = &pnx_adapter2, -}; - static struct platform_device i2c0_device = { .name = "pnx-i2c", .id = 0, diff --git a/arch/arm/mach-pnx4008/include/mach/clkdev.h b/arch/arm/mach-pnx4008/include/mach/clkdev.h new file mode 100644 index 000000000000..04b37a89801c --- /dev/null +++ b/arch/arm/mach-pnx4008/include/mach/clkdev.h @@ -0,0 +1,7 @@ +#ifndef __ASM_MACH_CLKDEV_H +#define __ASM_MACH_CLKDEV_H + +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) + +#endif diff --git a/arch/arm/mach-pnx4008/include/mach/timex.h b/arch/arm/mach-pnx4008/include/mach/timex.h index 5ff0196c0f16..b383c7de7ab4 100644 --- a/arch/arm/mach-pnx4008/include/mach/timex.h +++ b/arch/arm/mach-pnx4008/include/mach/timex.h @@ -14,60 +14,6 @@ #ifndef __PNX4008_TIMEX_H #define __PNX4008_TIMEX_H -#include <linux/io.h> -#include <mach/hardware.h> - #define CLOCK_TICK_RATE 1000000 -#define TICKS2USECS(x) (x) - -/* MilliSecond Timer - Chapter 21 Page 202 */ - -#define MSTIM_INT IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x0)) -#define MSTIM_CTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x4)) -#define MSTIM_COUNTER IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x8)) -#define MSTIM_MCTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x14)) -#define MSTIM_MATCH0 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x18)) -#define MSTIM_MATCH1 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x1c)) - -/* High Speed Timer - Chpater 22, Page 205 */ - -#define HSTIM_INT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x0)) -#define HSTIM_CTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x4)) -#define HSTIM_COUNTER IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x8)) -#define HSTIM_PMATCH IO_ADDRESS((PNX4008_HSTIMER_BASE + 0xC)) -#define HSTIM_PCOUNT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x10)) -#define HSTIM_MCTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x14)) -#define HSTIM_MATCH0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x18)) -#define HSTIM_MATCH1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x1c)) -#define HSTIM_MATCH2 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x20)) -#define HSTIM_CCR IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x28)) -#define HSTIM_CR0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x2C)) -#define HSTIM_CR1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x30)) - -/* IMPORTANT: both timers are UPCOUNTING */ - -/* xSTIM_MCTRL bit definitions */ -#define MR0_INT 1 -#define RESET_COUNT0 (1<<1) -#define STOP_COUNT0 (1<<2) -#define MR1_INT (1<<3) -#define RESET_COUNT1 (1<<4) -#define STOP_COUNT1 (1<<5) -#define MR2_INT (1<<6) -#define RESET_COUNT2 (1<<7) -#define STOP_COUNT2 (1<<8) - -/* xSTIM_CTRL bit definitions */ -#define COUNT_ENAB 1 -#define RESET_COUNT (1<<1) -#define DEBUG_EN (1<<2) - -/* xSTIM_INT bit definitions */ -#define MATCH0_INT 1 -#define MATCH1_INT (1<<1) -#define MATCH2_INT (1<<2) -#define RTC_TICK0 (1<<4) -#define RTC_TICK1 (1<<5) - #endif diff --git a/arch/arm/mach-pnx4008/pm.c b/arch/arm/mach-pnx4008/pm.c index b3d8d53e32ef..1f0585329be4 100644 --- a/arch/arm/mach-pnx4008/pm.c +++ b/arch/arm/mach-pnx4008/pm.c @@ -21,6 +21,8 @@ #include <linux/io.h> #include <asm/cacheflush.h> + +#include <mach/hardware.h> #include <mach/pm.h> #include <mach/clock.h> diff --git a/arch/arm/mach-pnx4008/time.c b/arch/arm/mach-pnx4008/time.c index fc0ba183fe12..0c8aad4bb0dc 100644 --- a/arch/arm/mach-pnx4008/time.c +++ b/arch/arm/mach-pnx4008/time.c @@ -30,6 +30,8 @@ #include <asm/mach/time.h> #include <asm/errno.h> +#include "time.h" + /*! Note: all timers are UPCOUNTING */ /*! diff --git a/arch/arm/mach-pnx4008/time.h b/arch/arm/mach-pnx4008/time.h new file mode 100644 index 000000000000..75e88c570aa7 --- /dev/null +++ b/arch/arm/mach-pnx4008/time.h @@ -0,0 +1,70 @@ +/* + * arch/arm/mach-pnx4008/include/mach/timex.h + * + * PNX4008 timers header file + * + * Author: Dmitry Chigirev <source@mvista.com> + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef PNX_TIME_H +#define PNX_TIME_H + +#include <linux/io.h> +#include <mach/hardware.h> + +#define TICKS2USECS(x) (x) + +/* MilliSecond Timer - Chapter 21 Page 202 */ + +#define MSTIM_INT IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x0)) +#define MSTIM_CTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x4)) +#define MSTIM_COUNTER IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x8)) +#define MSTIM_MCTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x14)) +#define MSTIM_MATCH0 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x18)) +#define MSTIM_MATCH1 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x1c)) + +/* High Speed Timer - Chpater 22, Page 205 */ + +#define HSTIM_INT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x0)) +#define HSTIM_CTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x4)) +#define HSTIM_COUNTER IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x8)) +#define HSTIM_PMATCH IO_ADDRESS((PNX4008_HSTIMER_BASE + 0xC)) +#define HSTIM_PCOUNT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x10)) +#define HSTIM_MCTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x14)) +#define HSTIM_MATCH0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x18)) +#define HSTIM_MATCH1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x1c)) +#define HSTIM_MATCH2 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x20)) +#define HSTIM_CCR IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x28)) +#define HSTIM_CR0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x2C)) +#define HSTIM_CR1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x30)) + +/* IMPORTANT: both timers are UPCOUNTING */ + +/* xSTIM_MCTRL bit definitions */ +#define MR0_INT 1 +#define RESET_COUNT0 (1<<1) +#define STOP_COUNT0 (1<<2) +#define MR1_INT (1<<3) +#define RESET_COUNT1 (1<<4) +#define STOP_COUNT1 (1<<5) +#define MR2_INT (1<<6) +#define RESET_COUNT2 (1<<7) +#define STOP_COUNT2 (1<<8) + +/* xSTIM_CTRL bit definitions */ +#define COUNT_ENAB 1 +#define RESET_COUNT (1<<1) +#define DEBUG_EN (1<<2) + +/* xSTIM_INT bit definitions */ +#define MATCH0_INT 1 +#define MATCH1_INT (1<<1) +#define MATCH2_INT (1<<2) +#define RTC_TICK0 (1<<4) +#define RTC_TICK1 (1<<5) + +#endif diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 8a0837ea0294..385c30ee3f23 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -37,6 +37,8 @@ config MACH_ZYLONITE320 config MACH_LITTLETON bool "PXA3xx Form Factor Platform (aka Littleton)" select PXA3xx + select CPU_PXA300 + select CPU_PXA310 select PXA_SSP config MACH_TAVOREVB @@ -415,6 +417,24 @@ config MACH_TREO680 Say Y here if you intend to run this kernel on Palm Treo 680 smartphone. +config MACH_RAUMFELD_RC + bool "Raumfeld Controller" + select PXA3xx + select CPU_PXA300 + select HAVE_PWM + +config MACH_RAUMFELD_CONNECTOR + bool "Raumfeld Connector" + select PXA3xx + select CPU_PXA300 + select PXA_SSP + +config MACH_RAUMFELD_SPEAKER + bool "Raumfeld Speaker" + select PXA3xx + select CPU_PXA300 + select PXA_SSP + config PXA_SHARPSL bool "SHARP Zaurus SL-5600, SL-C7xx and SL-Cxx00 Models" select SHARP_SCOOP diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile index f64afda7e6f6..9d831939b3c5 100644 --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile @@ -89,6 +89,9 @@ obj-$(CONFIG_MACH_E740) += e740.o obj-$(CONFIG_MACH_E750) += e750.o obj-$(CONFIG_MACH_E400) += e400.o obj-$(CONFIG_MACH_E800) += e800.o +obj-$(CONFIG_MACH_RAUMFELD_RC) += raumfeld.o +obj-$(CONFIG_MACH_RAUMFELD_CONNECTOR) += raumfeld.o +obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o # Support for blinky lights led-y := leds.o diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c index 49ae38292310..abba0089a2ae 100644 --- a/arch/arm/mach-pxa/clock.c +++ b/arch/arm/mach-pxa/clock.c @@ -78,11 +78,3 @@ const struct clkops clk_cken_ops = { .enable = clk_cken_enable, .disable = clk_cken_disable, }; - -void clks_register(struct clk_lookup *clks, size_t num) -{ - int i; - - for (i = 0; i < num; i++) - clkdev_add(&clks[i]); -} diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h index 978a3667e90d..d8488742b807 100644 --- a/arch/arm/mach-pxa/clock.h +++ b/arch/arm/mach-pxa/clock.h @@ -67,7 +67,3 @@ extern void clk_pxa3xx_cken_enable(struct clk *); extern void clk_pxa3xx_cken_disable(struct clk *); #endif -void clks_register(struct clk_lookup *clks, size_t num); -int clk_add_alias(const char *alias, const char *alias_name, char *id, - struct device *dev); - diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c index 91417f035069..96ed13081639 100644 --- a/arch/arm/mach-pxa/eseries.c +++ b/arch/arm/mach-pxa/eseries.c @@ -128,6 +128,6 @@ static struct clk_lookup eseries_clkregs[] = { void eseries_register_clks(void) { - clks_register(eseries_clkregs, ARRAY_SIZE(eseries_clkregs)); + clkdev_add_table(eseries_clkregs, ARRAY_SIZE(eseries_clkregs)); } diff --git a/arch/arm/mach-pxa/include/mach/hardware.h b/arch/arm/mach-pxa/include/mach/hardware.h index 50f1297bf5ac..e741bf1bfb2d 100644 --- a/arch/arm/mach-pxa/include/mach/hardware.h +++ b/arch/arm/mach-pxa/include/mach/hardware.h @@ -250,20 +250,17 @@ #define cpu_is_pxa930() \ ({ \ - unsigned int id = read_cpuid(CPUID_ID); \ - __cpu_is_pxa930(id); \ + __cpu_is_pxa930(read_cpuid_id()); \ }) #define cpu_is_pxa935() \ ({ \ - unsigned int id = read_cpuid(CPUID_ID); \ - __cpu_is_pxa935(id); \ + __cpu_is_pxa935(read_cpuid_id()); \ }) #define cpu_is_pxa950() \ ({ \ - unsigned int id = read_cpuid(CPUID_ID); \ - __cpu_is_pxa950(id); \ + __cpu_is_pxa950(read_cpuid_id()); \ }) diff --git a/arch/arm/mach-pxa/include/mach/zylonite.h b/arch/arm/mach-pxa/include/mach/zylonite.h index bf6785adccf4..9edf645368d6 100644 --- a/arch/arm/mach-pxa/include/mach/zylonite.h +++ b/arch/arm/mach-pxa/include/mach/zylonite.h @@ -8,13 +8,6 @@ /* the following variables are processor specific and initialized * by the corresponding zylonite_pxa3xx_init() */ -struct platform_mmc_slot { - int gpio_cd; - int gpio_wp; -}; - -extern struct platform_mmc_slot zylonite_mmc_slot[]; - extern int gpio_eth_irq; extern int gpio_debug_led1; extern int gpio_debug_led2; diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c index f28c1715b910..fa527b258d61 100644 --- a/arch/arm/mach-pxa/littleton.c +++ b/arch/arm/mach-pxa/littleton.c @@ -110,6 +110,12 @@ static mfp_cfg_t littleton_mfp_cfg[] __initdata = { GPIO7_MMC1_CLK, GPIO8_MMC1_CMD, GPIO15_GPIO, /* card detect */ + + /* UART3 */ + GPIO107_UART3_CTS, + GPIO108_UART3_RTS, + GPIO109_UART3_TXD, + GPIO110_UART3_RXD, }; static struct resource smc91x_resources[] = { diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c index 8a38d604dc77..189f330719a2 100644 --- a/arch/arm/mach-pxa/magician.c +++ b/arch/arm/mach-pxa/magician.c @@ -381,7 +381,7 @@ err: return ret; } -static int magician_backlight_notify(int brightness) +static int magician_backlight_notify(struct device *dev, int brightness) { gpio_set_value(EGPIO_MAGICIAN_BL_POWER, brightness); if (brightness >= 200) { diff --git a/arch/arm/mach-pxa/palmld.c b/arch/arm/mach-pxa/palmld.c index 59140217890a..e100af78b166 100644 --- a/arch/arm/mach-pxa/palmld.c +++ b/arch/arm/mach-pxa/palmld.c @@ -270,7 +270,7 @@ err: return ret; } -static int palmld_backlight_notify(int brightness) +static int palmld_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_PALMLD_BL_POWER, brightness); gpio_set_value(GPIO_NR_PALMLD_LCD_POWER, brightness); diff --git a/arch/arm/mach-pxa/palmt5.c b/arch/arm/mach-pxa/palmt5.c index 7f89ca20f13a..8fe3ec27568f 100644 --- a/arch/arm/mach-pxa/palmt5.c +++ b/arch/arm/mach-pxa/palmt5.c @@ -209,7 +209,7 @@ err: return ret; } -static int palmt5_backlight_notify(int brightness) +static int palmt5_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_PALMT5_BL_POWER, brightness); gpio_set_value(GPIO_NR_PALMT5_LCD_POWER, brightness); diff --git a/arch/arm/mach-pxa/palmtc.c b/arch/arm/mach-pxa/palmtc.c index 308417592007..b992f07ece21 100644 --- a/arch/arm/mach-pxa/palmtc.c +++ b/arch/arm/mach-pxa/palmtc.c @@ -185,7 +185,7 @@ err: return ret; } -static int palmtc_backlight_notify(int brightness) +static int palmtc_backlight_notify(struct device *dev, int brightness) { /* backlight is on when GPIO16 AF0 is high */ gpio_set_value(GPIO_NR_PALMTC_BL_POWER, brightness); diff --git a/arch/arm/mach-pxa/palmte2.c b/arch/arm/mach-pxa/palmte2.c index 265d62bae7de..dc728d6ab94e 100644 --- a/arch/arm/mach-pxa/palmte2.c +++ b/arch/arm/mach-pxa/palmte2.c @@ -181,7 +181,7 @@ err: return ret; } -static int palmte2_backlight_notify(int brightness) +static int palmte2_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_PALMTE2_BL_POWER, brightness); gpio_set_value(GPIO_NR_PALMTE2_LCD_POWER, brightness); diff --git a/arch/arm/mach-pxa/palmtreo.c b/arch/arm/mach-pxa/palmtreo.c index 606eb7e8a17e..b433bb496711 100644 --- a/arch/arm/mach-pxa/palmtreo.c +++ b/arch/arm/mach-pxa/palmtreo.c @@ -375,7 +375,7 @@ err: return ret; } -static int treo_backlight_notify(int brightness) +static int treo_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_TREO_BL_POWER, brightness); return TREO_MAX_INTENSITY - brightness; diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index 7bf18c2f002f..b37a025c0b7b 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -269,7 +269,7 @@ err: return ret; } -static int palmtx_backlight_notify(int brightness) +static int palmtx_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_PALMTX_BL_POWER, brightness); gpio_set_value(GPIO_NR_PALMTX_LCD_POWER, brightness); diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index d787ac7cfdd8..1c5d68a94511 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -196,7 +196,7 @@ err: return ret; } -static int palmz72_backlight_notify(int brightness) +static int palmz72_backlight_notify(struct device *dev, int brightness) { gpio_set_value(GPIO_NR_PALMZ72_BL_POWER, brightness); gpio_set_value(GPIO_NR_PALMZ72_LCD_POWER, brightness); diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index e5eeb3a62d01..c2b938a4d5c9 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c @@ -293,7 +293,7 @@ static struct pxamci_platform_data poodle_mci_platform_data = { .init = poodle_mci_init, .setpower = poodle_mci_setpower, .exit = poodle_mci_exit, - .gpio_card_detect = POODLE_IRQ_GPIO_nSD_DETECT, + .gpio_card_detect = POODLE_GPIO_nSD_DETECT, .gpio_card_ro = POODLE_GPIO_nSD_WP, .gpio_power = -1, }; diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index 2c1b0b70d01d..0b9ad30bfd51 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -349,7 +349,7 @@ static int __init pxa25x_init(void) reset_status = RCSR; - clks_register(pxa25x_clkregs, ARRAY_SIZE(pxa25x_clkregs)); + clkdev_add_table(pxa25x_clkregs, ARRAY_SIZE(pxa25x_clkregs)); if ((ret = pxa_init_dma(IRQ_DMA, 16))) return ret; @@ -370,7 +370,7 @@ static int __init pxa25x_init(void) /* Only add HWUART for PXA255/26x; PXA210/250 do not have it. */ if (cpu_is_pxa255()) - clks_register(&pxa25x_hwuart_clkreg, 1); + clkdev_add(&pxa25x_hwuart_clkreg); return ret; } diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 6a0b73167e03..d783123e2d48 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -392,7 +392,7 @@ static int __init pxa27x_init(void) reset_status = RCSR; - clks_register(pxa27x_clkregs, ARRAY_SIZE(pxa27x_clkregs)); + clkdev_add_table(pxa27x_clkregs, ARRAY_SIZE(pxa27x_clkregs)); if ((ret = pxa_init_dma(IRQ_DMA, 32))) return ret; diff --git a/arch/arm/mach-pxa/pxa300.c b/arch/arm/mach-pxa/pxa300.c index f4af6e2bef89..40bb16501d86 100644 --- a/arch/arm/mach-pxa/pxa300.c +++ b/arch/arm/mach-pxa/pxa300.c @@ -102,12 +102,12 @@ static int __init pxa300_init(void) if (cpu_is_pxa300() || cpu_is_pxa310()) { mfp_init_base(io_p2v(MFPR_BASE)); mfp_init_addr(pxa300_mfp_addr_map); - clks_register(ARRAY_AND_SIZE(common_clkregs)); + clkdev_add_table(ARRAY_AND_SIZE(common_clkregs)); } if (cpu_is_pxa310()) { mfp_init_addr(pxa310_mfp_addr_map); - clks_register(ARRAY_AND_SIZE(pxa310_clkregs)); + clkdev_add_table(ARRAY_AND_SIZE(pxa310_clkregs)); } return 0; diff --git a/arch/arm/mach-pxa/pxa320.c b/arch/arm/mach-pxa/pxa320.c index c7373e74a109..8d614ecd8e99 100644 --- a/arch/arm/mach-pxa/pxa320.c +++ b/arch/arm/mach-pxa/pxa320.c @@ -90,7 +90,7 @@ static int __init pxa320_init(void) if (cpu_is_pxa320()) { mfp_init_base(io_p2v(MFPR_BASE)); mfp_init_addr(pxa320_mfp_addr_map); - clks_register(ARRAY_AND_SIZE(pxa320_clkregs)); + clkdev_add_table(ARRAY_AND_SIZE(pxa320_clkregs)); } return 0; diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index fcb0721f4669..4d7c03e72504 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -634,7 +634,7 @@ static int __init pxa3xx_init(void) */ ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S); - clks_register(pxa3xx_clkregs, ARRAY_SIZE(pxa3xx_clkregs)); + clkdev_add_table(pxa3xx_clkregs, ARRAY_SIZE(pxa3xx_clkregs)); if ((ret = pxa_init_dma(IRQ_DMA, 32))) return ret; diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c new file mode 100644 index 000000000000..06717d7995cb --- /dev/null +++ b/arch/arm/mach-pxa/raumfeld.c @@ -0,0 +1,1100 @@ +/* + * arch/arm/mach-pxa/raumfeld.c + * + * Support for the following Raumfeld devices: + * + * * Controller + * * Connector + * * Speaker S/M + * + * See http://www.raumfeld.com for details. + * + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sysdev.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/smsc911x.h> +#include <linux/input.h> +#include <linux/rotary_encoder.h> +#include <linux/gpio_keys.h> +#include <linux/input/eeti_ts.h> +#include <linux/leds.h> +#include <linux/w1-gpio.h> +#include <linux/sched.h> +#include <linux/pwm_backlight.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_gpio.h> +#include <linux/lis3lv02d.h> +#include <linux/pda_power.h> +#include <linux/power_supply.h> +#include <linux/pda_power.h> +#include <linux/power_supply.h> +#include <linux/regulator/max8660.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include <mach/hardware.h> +#include <mach/pxa3xx-regs.h> +#include <mach/mfp-pxa3xx.h> +#include <mach/mfp-pxa300.h> +#include <mach/ohci.h> +#include <mach/pxafb.h> +#include <mach/mmc.h> +#include <plat/i2c.h> +#include <plat/pxa3xx_nand.h> + +#include "generic.h" +#include "devices.h" +#include "clock.h" + +/* common GPIO definitions */ + +/* inputs */ +#define GPIO_ON_OFF (14) +#define GPIO_VOLENC_A (19) +#define GPIO_VOLENC_B (20) +#define GPIO_CHARGE_DONE (23) +#define GPIO_CHARGE_IND (27) +#define GPIO_TOUCH_IRQ (32) +#define GPIO_ETH_IRQ (40) +#define GPIO_SPI_MISO (98) +#define GPIO_ACCEL_IRQ (104) +#define GPIO_RESCUE_BOOT (115) +#define GPIO_DOCK_DETECT (116) +#define GPIO_KEY1 (117) +#define GPIO_KEY2 (118) +#define GPIO_KEY3 (119) +#define GPIO_CHARGE_USB_OK (112) +#define GPIO_CHARGE_DC_OK (101) +#define GPIO_CHARGE_USB_SUSP (102) + +/* outputs */ +#define GPIO_SHUTDOWN_SUPPLY (16) +#define GPIO_SHUTDOWN_BATT (18) +#define GPIO_CHRG_PEN2 (31) +#define GPIO_TFT_VA_EN (33) +#define GPIO_SPDIF_CS (34) +#define GPIO_LED2 (35) +#define GPIO_LED1 (36) +#define GPIO_SPDIF_RESET (38) +#define GPIO_SPI_CLK (95) +#define GPIO_MCLK_DAC_CS (96) +#define GPIO_SPI_MOSI (97) +#define GPIO_W1_PULLUP_ENABLE (105) +#define GPIO_DISPLAY_ENABLE (106) +#define GPIO_MCLK_RESET (111) +#define GPIO_W2W_RESET (113) +#define GPIO_W2W_PDN (114) +#define GPIO_CODEC_RESET (120) +#define GPIO_AUDIO_VA_ENABLE (124) +#define GPIO_ACCEL_CS (125) +#define GPIO_ONE_WIRE (126) + +/* + * GPIO configurations + */ +static mfp_cfg_t raumfeld_controller_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* One-wire */ + GPIO126_GPIO | MFP_LPM_FLOAT, + GPIO105_GPIO | MFP_PULL_LOW | MFP_LPM_PULL_LOW, + + /* CHRG_USB_OK */ + GPIO101_GPIO | MFP_PULL_HIGH, + /* CHRG_USB_OK */ + GPIO112_GPIO | MFP_PULL_HIGH, + /* CHRG_USB_SUSP */ + GPIO102_GPIO, + /* DISPLAY_ENABLE */ + GPIO106_GPIO, + /* DOCK_DETECT */ + GPIO116_GPIO | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* LCD */ + GPIO54_LCD_LDD_0, + GPIO55_LCD_LDD_1, + GPIO56_LCD_LDD_2, + GPIO57_LCD_LDD_3, + GPIO58_LCD_LDD_4, + GPIO59_LCD_LDD_5, + GPIO60_LCD_LDD_6, + GPIO61_LCD_LDD_7, + GPIO62_LCD_LDD_8, + GPIO63_LCD_LDD_9, + GPIO64_LCD_LDD_10, + GPIO65_LCD_LDD_11, + GPIO66_LCD_LDD_12, + GPIO67_LCD_LDD_13, + GPIO68_LCD_LDD_14, + GPIO69_LCD_LDD_15, + GPIO70_LCD_LDD_16, + GPIO71_LCD_LDD_17, + GPIO72_LCD_FCLK, + GPIO73_LCD_LCLK, + GPIO74_LCD_PCLK, + GPIO75_LCD_BIAS, +}; + +static mfp_cfg_t raumfeld_connector_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* Ethernet */ + GPIO1_nCS2, /* CS */ + GPIO40_GPIO | MFP_PULL_HIGH, /* IRQ */ + + /* SSP for I2S */ + GPIO85_SSP1_SCLK, + GPIO89_SSP1_EXTCLK, + GPIO86_SSP1_FRM, + GPIO87_SSP1_TXD, + GPIO88_SSP1_RXD, + GPIO90_SSP1_SYSCLK, + + /* SSP2 for S/PDIF */ + GPIO25_SSP2_SCLK, + GPIO26_SSP2_FRM, + GPIO27_SSP2_TXD, + GPIO29_SSP2_EXTCLK, +}; + +static mfp_cfg_t raumfeld_speaker_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* Ethernet */ + GPIO1_nCS2, /* CS */ + GPIO40_GPIO | MFP_PULL_HIGH, /* IRQ */ + + /* SSP for I2S */ + GPIO85_SSP1_SCLK, + GPIO89_SSP1_EXTCLK, + GPIO86_SSP1_FRM, + GPIO87_SSP1_TXD, + GPIO88_SSP1_RXD, + GPIO90_SSP1_SYSCLK, +}; + +/* + * SMSC LAN9220 Ethernet + */ + +static struct resource smc91x_resources[] = { + { + .start = PXA3xx_CS2_PHYS, + .end = PXA3xx_CS2_PHYS + 0xfffff, + .flags = IORESOURCE_MEM, + }, + { + .start = gpio_to_irq(GPIO_ETH_IRQ), + .end = gpio_to_irq(GPIO_ETH_IRQ), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_FALLING, + } +}; + +static struct smsc911x_platform_config raumfeld_smsc911x_config = { + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, +}; + +static struct platform_device smc91x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, + .dev = { + .platform_data = &raumfeld_smsc911x_config, + } +}; + +/** + * NAND + */ + +static struct mtd_partition raumfeld_nand_partitions[] = { + { + .name = "Bootloader", + .offset = 0, + .size = 0xa0000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "BootloaderEnvironment", + .offset = 0xa0000, + .size = 0x20000, + }, + { + .name = "BootloaderSplashScreen", + .offset = 0xc0000, + .size = 0x60000, + }, + { + .name = "UBI", + .offset = 0x120000, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct pxa3xx_nand_platform_data raumfeld_nand_info = { + .enable_arbiter = 1, + .keep_config = 1, + .parts = raumfeld_nand_partitions, + .nr_parts = ARRAY_SIZE(raumfeld_nand_partitions), +}; + +/** + * USB (OHCI) support + */ + +static struct pxaohci_platform_data raumfeld_ohci_info = { + .port_mode = PMM_GLOBAL_MODE, + .flags = ENABLE_PORT1, +}; + +/** + * Rotary encoder input device + */ + +static struct rotary_encoder_platform_data raumfeld_rotary_encoder_info = { + .steps = 24, + .axis = REL_X, + .relative_axis = 1, + .gpio_a = GPIO_VOLENC_A, + .gpio_b = GPIO_VOLENC_B, + .inverted_a = 1, + .inverted_b = 0, +}; + +static struct platform_device rotary_encoder_device = { + .name = "rotary-encoder", + .id = 0, + .dev = { + .platform_data = &raumfeld_rotary_encoder_info, + } +}; + +/** + * GPIO buttons + */ + +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_F1, + .type = EV_KEY, + .gpio = GPIO_KEY1, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 1", + }, + { + .code = KEY_F2, + .type = EV_KEY, + .gpio = GPIO_KEY2, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 2", + }, + { + .code = KEY_F3, + .type = EV_KEY, + .gpio = GPIO_KEY3, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 3", + }, + { + .code = KEY_F4, + .type = EV_KEY, + .gpio = GPIO_RESCUE_BOOT, + .active_low = 0, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "rescue boot button", + }, + { + .code = KEY_F5, + .type = EV_KEY, + .gpio = GPIO_DOCK_DETECT, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "dock detect", + }, + { + .code = KEY_F6, + .type = EV_KEY, + .gpio = GPIO_ON_OFF, + .active_low = 0, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "on/off button", + }, +}; + +static struct gpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +static struct platform_device raumfeld_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_keys_platform_data, + } +}; + +/** + * GPIO LEDs + */ + +static struct gpio_led raumfeld_leds[] = { + { + .name = "raumfeld:1", + .gpio = GPIO_LED1, + .active_low = 1, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "raumfeld:2", + .gpio = GPIO_LED2, + .active_low = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + } +}; + +static struct gpio_led_platform_data raumfeld_led_platform_data = { + .leds = raumfeld_leds, + .num_leds = ARRAY_SIZE(raumfeld_leds), +}; + +static struct platform_device raumfeld_led_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &raumfeld_led_platform_data, + }, +}; + +/** + * One-wire (W1 bus) support + */ + +static void w1_enable_external_pullup(int enable) +{ + gpio_set_value(GPIO_W1_PULLUP_ENABLE, enable); + msleep(100); +} + +static struct w1_gpio_platform_data w1_gpio_platform_data = { + .pin = GPIO_ONE_WIRE, + .is_open_drain = 0, + .enable_external_pullup = w1_enable_external_pullup, +}; + +struct platform_device raumfeld_w1_gpio_device = { + .name = "w1-gpio", + .dev = { + .platform_data = &w1_gpio_platform_data + } +}; + +static void __init raumfeld_w1_init(void) +{ + int ret = gpio_request(GPIO_W1_PULLUP_ENABLE, + "W1 external pullup enable"); + + if (ret < 0) + pr_warning("Unable to request GPIO_W1_PULLUP_ENABLE\n"); + else + gpio_direction_output(GPIO_W1_PULLUP_ENABLE, 0); + + platform_device_register(&raumfeld_w1_gpio_device); +} + +/** + * Framebuffer device + */ + +/* PWM controlled backlight */ +static struct platform_pwm_backlight_data raumfeld_pwm_backlight_data = { + .pwm_id = 0, + .max_brightness = 100, + .dft_brightness = 100, + /* 10000 ns = 10 ms ^= 100 kHz */ + .pwm_period_ns = 10000, +}; + +static struct platform_device raumfeld_pwm_backlight_device = { + .name = "pwm-backlight", + .dev = { + .parent = &pxa27x_device_pwm0.dev, + .platform_data = &raumfeld_pwm_backlight_data, + } +}; + +/* LT3593 controlled backlight */ +static struct gpio_led raumfeld_lt3593_led = { + .name = "backlight", + .gpio = mfp_to_gpio(MFP_PIN_GPIO17), + .default_state = LEDS_GPIO_DEFSTATE_ON, +}; + +static struct gpio_led_platform_data raumfeld_lt3593_platform_data = { + .leds = &raumfeld_lt3593_led, + .num_leds = 1, +}; + +static struct platform_device raumfeld_lt3593_device = { + .name = "leds-lt3593", + .id = -1, + .dev = { + .platform_data = &raumfeld_lt3593_platform_data, + }, +}; + +static struct pxafb_mode_info sharp_lq043t3dx02_mode = { + .pixclock = 111000, + .xres = 480, + .yres = 272, + .bpp = 16, + .hsync_len = 4, + .left_margin = 2, + .right_margin = 1, + .vsync_len = 1, + .upper_margin = 3, + .lower_margin = 1, + .sync = 0, +}; + +static struct pxafb_mach_info raumfeld_sharp_lcd_info = { + .modes = &sharp_lq043t3dx02_mode, + .num_modes = 1, + .video_mem_size = 0x400000, + .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL, +}; + +static void __init raumfeld_lcd_init(void) +{ + int ret; + + set_pxa_fb_info(&raumfeld_sharp_lcd_info); + + /* Earlier devices had the backlight regulator controlled + * via PWM, later versions use another controller for that */ + if ((system_rev & 0xff) < 2) { + mfp_cfg_t raumfeld_pwm_pin_config = GPIO17_PWM0_OUT; + pxa3xx_mfp_config(&raumfeld_pwm_pin_config, 1); + platform_device_register(&raumfeld_pwm_backlight_device); + } else + platform_device_register(&raumfeld_lt3593_device); + + ret = gpio_request(GPIO_TFT_VA_EN, "display VA enable"); + if (ret < 0) + pr_warning("Unable to request GPIO_TFT_VA_EN\n"); + else + gpio_direction_output(GPIO_TFT_VA_EN, 1); + + ret = gpio_request(GPIO_DISPLAY_ENABLE, "display enable"); + if (ret < 0) + pr_warning("Unable to request GPIO_DISPLAY_ENABLE\n"); + else + gpio_direction_output(GPIO_DISPLAY_ENABLE, 1); +} + +/** + * SPI devices + */ + +struct spi_gpio_platform_data raumfeld_spi_platform_data = { + .sck = GPIO_SPI_CLK, + .mosi = GPIO_SPI_MOSI, + .miso = GPIO_SPI_MISO, + .num_chipselect = 3, +}; + +static struct platform_device raumfeld_spi_device = { + .name = "spi_gpio", + .id = 0, + .dev = { + .platform_data = &raumfeld_spi_platform_data, + } +}; + +static struct lis3lv02d_platform_data lis3_pdata = { + .click_flags = LIS3_CLICK_SINGLE_X | + LIS3_CLICK_SINGLE_Y | + LIS3_CLICK_SINGLE_Z, + .irq_cfg = LIS3_IRQ1_CLICK | LIS3_IRQ2_CLICK, + .wakeup_flags = LIS3_WAKEUP_X_LO | LIS3_WAKEUP_X_HI | + LIS3_WAKEUP_Y_LO | LIS3_WAKEUP_Y_HI | + LIS3_WAKEUP_Z_LO | LIS3_WAKEUP_Z_HI, + .wakeup_thresh = 10, + .click_thresh_x = 10, + .click_thresh_y = 10, + .click_thresh_z = 10, +}; + +#define SPI_AK4104 \ +{ \ + .modalias = "ak4104", \ + .max_speed_hz = 10000, \ + .bus_num = 0, \ + .chip_select = 0, \ + .controller_data = (void *) GPIO_SPDIF_CS, \ +} + +#define SPI_LIS3 \ +{ \ + .modalias = "lis3lv02d_spi", \ + .max_speed_hz = 1000000, \ + .bus_num = 0, \ + .chip_select = 1, \ + .controller_data = (void *) GPIO_ACCEL_CS, \ + .platform_data = &lis3_pdata, \ + .irq = gpio_to_irq(GPIO_ACCEL_IRQ), \ +} + +#define SPI_DAC7512 \ +{ \ + .modalias = "dac7512", \ + .max_speed_hz = 1000000, \ + .bus_num = 0, \ + .chip_select = 2, \ + .controller_data = (void *) GPIO_MCLK_DAC_CS, \ +} + +static struct spi_board_info connector_spi_devices[] __initdata = { + SPI_AK4104, + SPI_DAC7512, +}; + +static struct spi_board_info speaker_spi_devices[] __initdata = { + SPI_DAC7512, +}; + +static struct spi_board_info controller_spi_devices[] __initdata = { + SPI_LIS3, +}; + +/** + * MMC for Marvell Libertas 8688 via SDIO + */ + +static int raumfeld_mci_init(struct device *dev, irq_handler_t isr, void *data) +{ + gpio_set_value(GPIO_W2W_RESET, 1); + gpio_set_value(GPIO_W2W_PDN, 1); + + return 0; +} + +static void raumfeld_mci_exit(struct device *dev, void *data) +{ + gpio_set_value(GPIO_W2W_RESET, 0); + gpio_set_value(GPIO_W2W_PDN, 0); +} + +static struct pxamci_platform_data raumfeld_mci_platform_data = { + .init = raumfeld_mci_init, + .exit = raumfeld_mci_exit, + .detect_delay = 20, + .gpio_card_detect = -1, + .gpio_card_ro = -1, + .gpio_power = -1, +}; + +/* + * External power / charge logic + */ + +static int power_supply_init(struct device *dev) +{ + return 0; +} + +static void power_supply_exit(struct device *dev) +{ +} + +static int raumfeld_is_ac_online(void) +{ + return !gpio_get_value(GPIO_CHARGE_DC_OK); +} + +static int raumfeld_is_usb_online(void) +{ + return 0; +} + +static char *raumfeld_power_supplicants[] = { "ds2760-battery.0" }; + +static struct pda_power_pdata power_supply_info = { + .init = power_supply_init, + .is_ac_online = raumfeld_is_ac_online, + .is_usb_online = raumfeld_is_usb_online, + .exit = power_supply_exit, + .supplied_to = raumfeld_power_supplicants, + .num_supplicants = ARRAY_SIZE(raumfeld_power_supplicants) +}; + +static struct resource power_supply_resources[] = { + { + .name = "ac", + .flags = IORESOURCE_IRQ | + IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, + .start = GPIO_CHARGE_DC_OK, + .end = GPIO_CHARGE_DC_OK, + }, +}; + +static irqreturn_t charge_done_irq(int irq, void *dev_id) +{ + struct power_supply *psy; + + psy = power_supply_get_by_name("ds2760-battery.0"); + + if (psy) + power_supply_set_battery_charged(psy); + + return IRQ_HANDLED; +} + +static struct platform_device raumfeld_power_supply = { + .name = "pda-power", + .id = -1, + .dev = { + .platform_data = &power_supply_info, + }, + .resource = power_supply_resources, + .num_resources = ARRAY_SIZE(power_supply_resources), +}; + +static void __init raumfeld_power_init(void) +{ + int ret; + + /* Set PEN2 high to enable maximum charge current */ + ret = gpio_request(GPIO_CHRG_PEN2, "CHRG_PEN2"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHRG_PEN2\n"); + else + gpio_direction_output(GPIO_CHRG_PEN2, 1); + + ret = gpio_request(GPIO_CHARGE_DC_OK, "CABLE_DC_OK"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHARGE_DC_OK\n"); + + ret = gpio_request(GPIO_CHARGE_USB_SUSP, "CHARGE_USB_SUSP"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHARGE_USB_SUSP\n"); + else + gpio_direction_output(GPIO_CHARGE_USB_SUSP, 0); + + power_supply_resources[0].start = gpio_to_irq(GPIO_CHARGE_DC_OK); + power_supply_resources[0].end = gpio_to_irq(GPIO_CHARGE_DC_OK); + + ret = request_irq(gpio_to_irq(GPIO_CHARGE_DONE), + &charge_done_irq, IORESOURCE_IRQ_LOWEDGE, + "charge_done", NULL); + + if (ret < 0) + printk(KERN_ERR "%s: unable to register irq %d\n", __func__, + GPIO_CHARGE_DONE); + else + platform_device_register(&raumfeld_power_supply); +} + +/* Fixed regulator for AUDIO_VA, 0-0048 maps to the cs4270 codec device */ + +static struct regulator_consumer_supply audio_va_consumer_supply = + REGULATOR_SUPPLY("va", "0-0048"); + +struct regulator_init_data audio_va_initdata = { + .consumer_supplies = &audio_va_consumer_supply, + .num_consumer_supplies = 1, + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static struct fixed_voltage_config audio_va_config = { + .supply_name = "audio_va", + .microvolts = 5000000, + .gpio = GPIO_AUDIO_VA_ENABLE, + .enable_high = 1, + .enabled_at_boot = 0, + .init_data = &audio_va_initdata, +}; + +static struct platform_device audio_va_device = { + .name = "reg-fixed-voltage", + .id = 0, + .dev = { + .platform_data = &audio_va_config, + }, +}; + +/* Dummy supplies for Codec's VD/VLC */ + +static struct regulator_consumer_supply audio_dummy_supplies[] = { + REGULATOR_SUPPLY("vd", "0-0048"), + REGULATOR_SUPPLY("vlc", "0-0048"), +}; + +struct regulator_init_data audio_dummy_initdata = { + .consumer_supplies = audio_dummy_supplies, + .num_consumer_supplies = ARRAY_SIZE(audio_dummy_supplies), + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static struct fixed_voltage_config audio_dummy_config = { + .supply_name = "audio_vd", + .microvolts = 3300000, + .gpio = -1, + .init_data = &audio_dummy_initdata, +}; + +static struct platform_device audio_supply_dummy_device = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &audio_dummy_config, + }, +}; + +static struct platform_device *audio_regulator_devices[] = { + &audio_va_device, + &audio_supply_dummy_device, +}; + +/** + * Regulator support via MAX8660 + */ + +static struct regulator_consumer_supply vcc_mmc_supply = + REGULATOR_SUPPLY("vmmc", "pxa2xx-mci.0"); + +static struct regulator_init_data vcc_mmc_init_data = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + }, + .consumer_supplies = &vcc_mmc_supply, + .num_consumer_supplies = 1, +}; + +struct max8660_subdev_data max8660_v6_subdev_data = { + .id = MAX8660_V6, + .name = "vmmc", + .platform_data = &vcc_mmc_init_data, +}; + +static struct max8660_platform_data max8660_pdata = { + .subdevs = &max8660_v6_subdev_data, + .num_subdevs = 1, +}; + +/** + * I2C devices + */ + +static struct i2c_board_info raumfeld_pwri2c_board_info = { + .type = "max8660", + .addr = 0x34, + .platform_data = &max8660_pdata, +}; + +static struct i2c_board_info raumfeld_connector_i2c_board_info __initdata = { + .type = "cs4270", + .addr = 0x48, +}; + +static struct eeti_ts_platform_data eeti_ts_pdata = { + .irq_active_high = 1, +}; + +static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = { + .type = "eeti_ts", + .addr = 0x0a, + .irq = gpio_to_irq(GPIO_TOUCH_IRQ), + .platform_data = &eeti_ts_pdata, +}; + +static struct platform_device *raumfeld_common_devices[] = { + &raumfeld_gpio_keys_device, + &raumfeld_led_device, + &raumfeld_spi_device, +}; + +static void __init raumfeld_audio_init(void) +{ + int ret; + + ret = gpio_request(GPIO_CODEC_RESET, "cs4270 reset"); + if (ret < 0) + pr_warning("unable to request GPIO_CODEC_RESET\n"); + else + gpio_direction_output(GPIO_CODEC_RESET, 1); + + ret = gpio_request(GPIO_SPDIF_RESET, "ak4104 s/pdif reset"); + if (ret < 0) + pr_warning("unable to request GPIO_SPDIF_RESET\n"); + else + gpio_direction_output(GPIO_SPDIF_RESET, 1); + + ret = gpio_request(GPIO_MCLK_RESET, "MCLK reset"); + if (ret < 0) + pr_warning("unable to request GPIO_MCLK_RESET\n"); + else + gpio_direction_output(GPIO_MCLK_RESET, 1); + + platform_add_devices(ARRAY_AND_SIZE(audio_regulator_devices)); +} + +static void __init raumfeld_common_init(void) +{ + int ret; + + /* The on/off button polarity has changed after revision 1 */ + if ((system_rev & 0xff) > 1) { + int i; + + for (i = 0; i < ARRAY_SIZE(gpio_keys_button); i++) + if (!strcmp(gpio_keys_button[i].desc, "on/off button")) + gpio_keys_button[i].active_low = 1; + } + + enable_irq_wake(IRQ_WAKEUP0); + + pxa3xx_set_nand_info(&raumfeld_nand_info); + pxa3xx_set_i2c_power_info(NULL); + pxa_set_ohci_info(&raumfeld_ohci_info); + pxa_set_mci_info(&raumfeld_mci_platform_data); + pxa_set_i2c_info(NULL); + pxa_set_ffuart_info(NULL); + + ret = gpio_request(GPIO_W2W_RESET, "Wi2Wi reset"); + if (ret < 0) + pr_warning("Unable to request GPIO_W2W_RESET\n"); + else + gpio_direction_output(GPIO_W2W_RESET, 0); + + ret = gpio_request(GPIO_W2W_PDN, "Wi2Wi powerup"); + if (ret < 0) + pr_warning("Unable to request GPIO_W2W_PDN\n"); + else + gpio_direction_output(GPIO_W2W_PDN, 0); + + /* this can be used to switch off the device */ + ret = gpio_request(GPIO_SHUTDOWN_SUPPLY, + "supply shutdown"); + if (ret < 0) + pr_warning("Unable to request GPIO_SHUTDOWN_SUPPLY\n"); + else + gpio_direction_output(GPIO_SHUTDOWN_SUPPLY, 0); + + platform_add_devices(ARRAY_AND_SIZE(raumfeld_common_devices)); + i2c_register_board_info(1, &raumfeld_pwri2c_board_info, 1); +} + +static void __init raumfeld_controller_init(void) +{ + int ret; + + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_controller_pin_config)); + platform_device_register(&rotary_encoder_device); + spi_register_board_info(ARRAY_AND_SIZE(controller_spi_devices)); + i2c_register_board_info(0, &raumfeld_controller_i2c_board_info, 1); + + ret = gpio_request(GPIO_SHUTDOWN_BATT, "battery shutdown"); + if (ret < 0) + pr_warning("Unable to request GPIO_SHUTDOWN_BATT\n"); + else + gpio_direction_output(GPIO_SHUTDOWN_BATT, 0); + + raumfeld_common_init(); + raumfeld_power_init(); + raumfeld_lcd_init(); + raumfeld_w1_init(); +} + +static void __init raumfeld_connector_init(void) +{ + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_connector_pin_config)); + spi_register_board_info(ARRAY_AND_SIZE(connector_spi_devices)); + i2c_register_board_info(0, &raumfeld_connector_i2c_board_info, 1); + + platform_device_register(&smc91x_device); + + raumfeld_audio_init(); + raumfeld_common_init(); +} + +static void __init raumfeld_speaker_init(void) +{ + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_speaker_pin_config)); + spi_register_board_info(ARRAY_AND_SIZE(speaker_spi_devices)); + i2c_register_board_info(0, &raumfeld_connector_i2c_board_info, 1); + + platform_device_register(&smc91x_device); + platform_device_register(&rotary_encoder_device); + + raumfeld_audio_init(); + raumfeld_common_init(); +} + +/* physical memory regions */ +#define RAUMFELD_SDRAM_BASE 0xa0000000 /* SDRAM region */ + +#ifdef CONFIG_MACH_RAUMFELD_RC +MACHINE_START(RAUMFELD_RC, "Raumfeld Controller") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_controller_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_RAUMFELD_CONNECTOR +MACHINE_START(RAUMFELD_CONNECTOR, "Raumfeld Connector") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_connector_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_RAUMFELD_SPEAKER +MACHINE_START(RAUMFELD_SPEAKER, "Raumfeld Speaker") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_speaker_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 4b50f144fa48..28352c0b8c34 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -389,13 +389,13 @@ static struct gpio_keys_button spitz_gpio_keys[] = { .type = EV_SW, .code = 0, .gpio = SPITZ_GPIO_SWA, - .desc = "Display Down", + .desc = "Display Down", }, { .type = EV_SW, .code = 1, .gpio = SPITZ_GPIO_SWB, - .desc = "Lid Closed", + .desc = "Lid Closed", }, }; diff --git a/arch/arm/mach-pxa/viper.c b/arch/arm/mach-pxa/viper.c index 5352b4e5a7dd..89f258c9e126 100644 --- a/arch/arm/mach-pxa/viper.c +++ b/arch/arm/mach-pxa/viper.c @@ -379,7 +379,7 @@ err_request_bckl: return ret; } -static int viper_backlight_notify(int brightness) +static int viper_backlight_notify(struct device *dev, int brightness) { gpio_set_value(VIPER_LCD_EN_GPIO, !!brightness); gpio_set_value(VIPER_BCKLIGHT_EN_GPIO, !!brightness); diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c index 5b986a8bd9e6..75f2a37f945d 100644 --- a/arch/arm/mach-pxa/zeus.c +++ b/arch/arm/mach-pxa/zeus.c @@ -25,6 +25,7 @@ #include <linux/mtd/physmap.h> #include <linux/i2c.h> #include <linux/i2c/pca953x.h> +#include <linux/apm-emulation.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -626,8 +627,27 @@ static void zeus_power_off(void) pxa27x_cpu_suspend(PWRMODE_DEEPSLEEP); } -int zeus_get_pcb_info(struct i2c_client *client, unsigned gpio, - unsigned ngpio, void *context) +#ifdef CONFIG_APM_EMULATION +static void zeus_get_power_status(struct apm_power_info *info) +{ + /* Power supply is always present */ + info->ac_line_status = APM_AC_ONLINE; + info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT; + info->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT; +} + +static inline void zeus_setup_apm(void) +{ + apm_get_power_status = zeus_get_power_status; +} +#else +static inline void zeus_setup_apm(void) +{ +} +#endif + +static int zeus_get_pcb_info(struct i2c_client *client, unsigned gpio, + unsigned ngpio, void *context) { int i; u8 pcb_info = 0; @@ -726,9 +746,18 @@ static mfp_cfg_t zeus_pin_config[] __initdata = { GPIO99_GPIO, /* CF RDY */ }; +/* + * DM9k MSCx settings: SRAM, 16 bits + * 17 cycles delay first access + * 5 cycles delay next access + * 13 cycles recovery time + * faster device + */ +#define DM9K_MSC_VALUE 0xe4c9 + static void __init zeus_init(void) { - u16 dm9000_msc = 0xe279; + u16 dm9000_msc = DM9K_MSC_VALUE; system_rev = __raw_readw(ZEUS_CPLD_VERSION); pr_info("Zeus CPLD V%dI%d\n", (system_rev & 0xf0) >> 4, (system_rev & 0x0f)); @@ -738,6 +767,7 @@ static void __init zeus_init(void) MSC1 = (MSC1 & 0xffff0000) | dm9000_msc; pm_power_off = zeus_power_off; + zeus_setup_apm(); pxa2xx_mfp_config(ARRAY_AND_SIZE(zeus_pin_config)); diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c index b66e9e2d06e7..2b4043c04d0c 100644 --- a/arch/arm/mach-pxa/zylonite.c +++ b/arch/arm/mach-pxa/zylonite.c @@ -36,9 +36,6 @@ #include "devices.h" #include "generic.h" -#define MAX_SLOTS 3 -struct platform_mmc_slot zylonite_mmc_slot[MAX_SLOTS]; - int gpio_eth_irq; int gpio_debug_led1; int gpio_debug_led2; @@ -220,84 +217,28 @@ static inline void zylonite_init_lcd(void) {} #endif #if defined(CONFIG_MMC) -static int zylonite_mci_ro(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - - return gpio_get_value(zylonite_mmc_slot[pdev->id].gpio_wp); -} - -static int zylonite_mci_init(struct device *dev, - irq_handler_t zylonite_detect_int, - void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int err, cd_irq, gpio_cd, gpio_wp; - - cd_irq = gpio_to_irq(zylonite_mmc_slot[pdev->id].gpio_cd); - gpio_cd = zylonite_mmc_slot[pdev->id].gpio_cd; - gpio_wp = zylonite_mmc_slot[pdev->id].gpio_wp; - - /* - * setup GPIO for Zylonite MMC controller - */ - err = gpio_request(gpio_cd, "mmc card detect"); - if (err) - goto err_request_cd; - gpio_direction_input(gpio_cd); - - err = gpio_request(gpio_wp, "mmc write protect"); - if (err) - goto err_request_wp; - gpio_direction_input(gpio_wp); - - err = request_irq(cd_irq, zylonite_detect_int, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "MMC card detect", data); - if (err) { - printk(KERN_ERR "%s: MMC/SD/SDIO: " - "can't request card detect IRQ\n", __func__); - goto err_request_irq; - } - - return 0; - -err_request_irq: - gpio_free(gpio_wp); -err_request_wp: - gpio_free(gpio_cd); -err_request_cd: - return err; -} - -static void zylonite_mci_exit(struct device *dev, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int cd_irq, gpio_cd, gpio_wp; - - cd_irq = gpio_to_irq(zylonite_mmc_slot[pdev->id].gpio_cd); - gpio_cd = zylonite_mmc_slot[pdev->id].gpio_cd; - gpio_wp = zylonite_mmc_slot[pdev->id].gpio_wp; - - free_irq(cd_irq, data); - gpio_free(gpio_cd); - gpio_free(gpio_wp); -} - static struct pxamci_platform_data zylonite_mci_platform_data = { .detect_delay = 20, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .init = zylonite_mci_init, - .exit = zylonite_mci_exit, - .get_ro = zylonite_mci_ro, - .gpio_card_detect = -1, - .gpio_card_ro = -1, + .gpio_card_detect = EXT_GPIO(0), + .gpio_card_ro = EXT_GPIO(2), .gpio_power = -1, }; static struct pxamci_platform_data zylonite_mci2_platform_data = { .detect_delay = 20, .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .gpio_card_detect = EXT_GPIO(1), + .gpio_card_ro = EXT_GPIO(3), + .gpio_power = -1, +}; + +static struct pxamci_platform_data zylonite_mci3_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .gpio_card_detect = EXT_GPIO(30), + .gpio_card_ro = EXT_GPIO(31), + .gpio_power = -1, }; static void __init zylonite_init_mmc(void) @@ -305,7 +246,7 @@ static void __init zylonite_init_mmc(void) pxa_set_mci_info(&zylonite_mci_platform_data); pxa3xx_set_mci2_info(&zylonite_mci2_platform_data); if (cpu_is_pxa310()) - pxa3xx_set_mci3_info(&zylonite_mci_platform_data); + pxa3xx_set_mci3_info(&zylonite_mci3_platform_data); } #else static inline void zylonite_init_mmc(void) {} diff --git a/arch/arm/mach-pxa/zylonite_pxa300.c b/arch/arm/mach-pxa/zylonite_pxa300.c index 84095440a878..3aa73b3e33f2 100644 --- a/arch/arm/mach-pxa/zylonite_pxa300.c +++ b/arch/arm/mach-pxa/zylonite_pxa300.c @@ -129,8 +129,8 @@ static mfp_cfg_t common_mfp_cfg[] __initdata = { GPIO22_I2C_SDA, /* GPIO */ - GPIO18_GPIO, /* GPIO Expander #0 INT_N */ - GPIO19_GPIO, /* GPIO Expander #1 INT_N */ + GPIO18_GPIO | MFP_PULL_HIGH, /* GPIO Expander #0 INT_N */ + GPIO19_GPIO | MFP_PULL_HIGH, /* GPIO Expander #1 INT_N */ }; static mfp_cfg_t pxa300_mfp_cfg[] __initdata = { @@ -258,10 +258,6 @@ void __init zylonite_pxa300_init(void) /* detect LCD panel */ zylonite_detect_lcd_panel(); - /* MMC card detect & write protect for controller 0 */ - zylonite_mmc_slot[0].gpio_cd = EXT_GPIO(0); - zylonite_mmc_slot[0].gpio_wp = EXT_GPIO(2); - /* WM9713 IRQ */ wm9713_irq = mfp_to_gpio(MFP_PIN_GPIO26); @@ -276,10 +272,6 @@ void __init zylonite_pxa300_init(void) if (cpu_is_pxa310()) { pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa310_mfp_cfg)); gpio_eth_irq = mfp_to_gpio(MFP_PIN_GPIO102); - - /* MMC card detect & write protect for controller 2 */ - zylonite_mmc_slot[2].gpio_cd = EXT_GPIO(30); - zylonite_mmc_slot[2].gpio_wp = EXT_GPIO(31); } /* GPIOs for Debug LEDs */ diff --git a/arch/arm/mach-pxa/zylonite_pxa320.c b/arch/arm/mach-pxa/zylonite_pxa320.c index 60d08f23f5e4..9942bac4cf7d 100644 --- a/arch/arm/mach-pxa/zylonite_pxa320.c +++ b/arch/arm/mach-pxa/zylonite_pxa320.c @@ -209,10 +209,6 @@ void __init zylonite_pxa320_init(void) gpio_debug_led1 = mfp_to_gpio(MFP_PIN_GPIO1_2); gpio_debug_led2 = mfp_to_gpio(MFP_PIN_GPIO4_2); - /* MMC card detect & write protect for controller 0 */ - zylonite_mmc_slot[0].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO1); - zylonite_mmc_slot[0].gpio_wp = mfp_to_gpio(MFP_PIN_GPIO5); - /* WM9713 IRQ */ wm9713_irq = mfp_to_gpio(MFP_PIN_GPIO15); } diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 9f293438e020..90bd4ef71b2c 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -346,10 +346,7 @@ static struct clk_lookup lookups[] = { static int __init clk_init(void) { - int i; - - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); return 0; } arch_initcall(clk_init); diff --git a/arch/arm/mach-realview/include/mach/board-pb1176.h b/arch/arm/mach-realview/include/mach/board-pb1176.h index 34b80b7d40b8..2f5ccb298858 100644 --- a/arch/arm/mach-realview/include/mach/board-pb1176.h +++ b/arch/arm/mach-realview/include/mach/board-pb1176.h @@ -74,8 +74,8 @@ #define REALVIEW_PB1176_L220_BASE 0x10110000 /* L220 registers */ /* - * Control register SYS_RESETCTL is set to 1 to force a soft reset + * Control register SYS_RESETCTL Bit 8 is set to 1 to force a soft reset */ -#define REALVIEW_PB1176_SYS_LOCKVAL_RSTCTL 0x0100 +#define REALVIEW_PB1176_SYS_SOFT_RESET 0x0100 #endif /* __ASM_ARCH_BOARD_PB1176_H */ diff --git a/arch/arm/mach-realview/include/mach/platform.h b/arch/arm/mach-realview/include/mach/platform.h index 4f46bf71e752..86c0c4435a46 100644 --- a/arch/arm/mach-realview/include/mach/platform.h +++ b/arch/arm/mach-realview/include/mach/platform.h @@ -140,7 +140,7 @@ * SYS_CLD, SYS_BOOTCS */ #define REALVIEW_SYS_LOCK_LOCKED (1 << 16) -#define REALVIEW_SYS_LOCKVAL_MASK 0xA05F /* Enable write access */ +#define REALVIEW_SYS_LOCK_VAL 0xA05F /* Enable write access */ /* * REALVIEW_SYS_FLASH diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index 917f8ca3abff..7d857d300558 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -381,6 +381,20 @@ static struct sys_timer realview_eb_timer = { .init = realview_eb_timer_init, }; +static void realview_eb_reset(char mode) +{ + void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL); + void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK); + + /* + * To reset, we hit the on-board reset register + * in the system FPGA + */ + __raw_writel(REALVIEW_SYS_LOCK_VAL, lock_ctrl); + if (core_tile_eb11mp()) + __raw_writel(0x0008, reset_ctrl); +} + static void __init realview_eb_init(void) { int i; @@ -408,6 +422,7 @@ static void __init realview_eb_init(void) #ifdef CONFIG_LEDS leds_event = realview_leds_event; #endif + realview_reset = realview_eb_reset; } MACHINE_START(REALVIEW_EB, "ARM-RealView EB") diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c index 7fb726d5f8b9..44392e51dd50 100644 --- a/arch/arm/mach-realview/realview_pb1176.c +++ b/arch/arm/mach-realview/realview_pb1176.c @@ -292,12 +292,10 @@ static struct sys_timer realview_pb1176_timer = { static void realview_pb1176_reset(char mode) { - void __iomem *hdr_ctrl = __io_address(REALVIEW_SYS_BASE) + - REALVIEW_SYS_RESETCTL_OFFSET; - void __iomem *rst_hdr_ctrl = __io_address(REALVIEW_SYS_BASE) + - REALVIEW_SYS_LOCK_OFFSET; - __raw_writel(REALVIEW_SYS_LOCKVAL_MASK, rst_hdr_ctrl); - __raw_writel(REALVIEW_PB1176_SYS_LOCKVAL_RSTCTL, hdr_ctrl); + void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL); + void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK); + __raw_writel(REALVIEW_SYS_LOCK_VAL, lock_ctrl); + __raw_writel(REALVIEW_PB1176_SYS_SOFT_RESET, reset_ctrl); } static void realview_pb1176_fixup(struct machine_desc *mdesc, diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index 9bbbfc05f225..3e02731af959 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -301,17 +301,16 @@ static struct sys_timer realview_pb11mp_timer = { static void realview_pb11mp_reset(char mode) { - void __iomem *hdr_ctrl = __io_address(REALVIEW_SYS_BASE) + - REALVIEW_SYS_RESETCTL_OFFSET; - unsigned int val; + void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL); + void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK); /* * To reset, we hit the on-board reset register * in the system FPGA */ - val = __raw_readl(hdr_ctrl); - val |= REALVIEW_PB11MP_SYS_CTRL_RESET_CONFIGCLR; - __raw_writel(val, hdr_ctrl); + __raw_writel(REALVIEW_SYS_LOCK_VAL, lock_ctrl); + __raw_writel(0x0000, reset_ctrl); + __raw_writel(0x0004, reset_ctrl); } static void __init realview_pb11mp_init(void) diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c index fe861e96c566..fe4e25c4201a 100644 --- a/arch/arm/mach-realview/realview_pba8.c +++ b/arch/arm/mach-realview/realview_pba8.c @@ -272,6 +272,20 @@ static struct sys_timer realview_pba8_timer = { .init = realview_pba8_timer_init, }; +static void realview_pba8_reset(char mode) +{ + void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL); + void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK); + + /* + * To reset, we hit the on-board reset register + * in the system FPGA + */ + __raw_writel(REALVIEW_SYS_LOCK_VAL, lock_ctrl); + __raw_writel(0x0000, reset_ctrl); + __raw_writel(0x0004, reset_ctrl); +} + static void __init realview_pba8_init(void) { int i; @@ -291,6 +305,7 @@ static void __init realview_pba8_init(void) #ifdef CONFIG_LEDS leds_event = realview_leds_event; #endif + realview_reset = realview_pba8_reset; } MACHINE_START(REALVIEW_PBA8, "ARM-RealView PB-A8") diff --git a/arch/arm/mach-realview/realview_pbx.c b/arch/arm/mach-realview/realview_pbx.c index ec39488e2b42..a21a4b395f73 100644 --- a/arch/arm/mach-realview/realview_pbx.c +++ b/arch/arm/mach-realview/realview_pbx.c @@ -324,6 +324,20 @@ static void realview_pbx_fixup(struct machine_desc *mdesc, struct tag *tags, #endif } +static void realview_pbx_reset(char mode) +{ + void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL); + void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK); + + /* + * To reset, we hit the on-board reset register + * in the system FPGA + */ + __raw_writel(REALVIEW_SYS_LOCK_VAL, lock_ctrl); + __raw_writel(0x0000, reset_ctrl); + __raw_writel(0x0004, reset_ctrl); +} + static void __init realview_pbx_init(void) { int i; @@ -358,6 +372,7 @@ static void __init realview_pbx_init(void) #ifdef CONFIG_LEDS leds_event = realview_leds_event; #endif + realview_reset = realview_pbx_reset; } MACHINE_START(REALVIEW_PBX, "ARM-RealView PBX") diff --git a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h index 980a099e209c..dcef2287cb38 100644 --- a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h +++ b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h @@ -3,7 +3,7 @@ * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C2410 - SPI Controller platfrom_device info + * S3C2410 - SPI Controller platform_device info * * 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 diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c index a037df5e1c2d..0c0505b025cb 100644 --- a/arch/arm/mach-s3c2412/clock.c +++ b/arch/arm/mach-s3c2412/clock.c @@ -124,7 +124,9 @@ static struct clk clk_usysclk = { .name = "usysclk", .id = -1, .parent = &clk_xtal, - .set_parent = s3c2412_setparent_usysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_usysclk, + }, }; static struct clk clk_mrefclk = { @@ -199,10 +201,12 @@ static int s3c2412_setrate_usbsrc(struct clk *clk, unsigned long rate) static struct clk clk_usbsrc = { .name = "usbsrc", .id = -1, - .get_rate = s3c2412_getrate_usbsrc, - .set_rate = s3c2412_setrate_usbsrc, - .round_rate = s3c2412_roundrate_usbsrc, - .set_parent = s3c2412_setparent_usbsrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_usbsrc, + .set_rate = s3c2412_setrate_usbsrc, + .round_rate = s3c2412_roundrate_usbsrc, + .set_parent = s3c2412_setparent_usbsrc, + }, }; static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent) @@ -225,7 +229,9 @@ static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent) static struct clk clk_msysclk = { .name = "msysclk", .id = -1, - .set_parent = s3c2412_setparent_msysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_msysclk, + }, }; static int s3c2412_setparent_armclk(struct clk *clk, struct clk *parent) @@ -264,7 +270,9 @@ static struct clk clk_armclk = { .name = "armclk", .id = -1, .parent = &clk_msysclk, - .set_parent = s3c2412_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_armclk, + }, }; /* these next clocks have an divider immediately after them, @@ -337,10 +345,12 @@ static int s3c2412_setrate_uart(struct clk *clk, unsigned long rate) static struct clk clk_uart = { .name = "uartclk", .id = -1, - .get_rate = s3c2412_getrate_uart, - .set_rate = s3c2412_setrate_uart, - .set_parent = s3c2412_setparent_uart, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_uart, + .set_rate = s3c2412_setrate_uart, + .set_parent = s3c2412_setparent_uart, + .round_rate = s3c2412_roundrate_clksrc, + }, }; static int s3c2412_setparent_i2s(struct clk *clk, struct clk *parent) @@ -388,10 +398,12 @@ static int s3c2412_setrate_i2s(struct clk *clk, unsigned long rate) static struct clk clk_i2s = { .name = "i2sclk", .id = -1, - .get_rate = s3c2412_getrate_i2s, - .set_rate = s3c2412_setrate_i2s, - .set_parent = s3c2412_setparent_i2s, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_i2s, + .set_rate = s3c2412_setrate_i2s, + .set_parent = s3c2412_setparent_i2s, + .round_rate = s3c2412_roundrate_clksrc, + }, }; static int s3c2412_setparent_cam(struct clk *clk, struct clk *parent) @@ -438,10 +450,12 @@ static int s3c2412_setrate_cam(struct clk *clk, unsigned long rate) static struct clk clk_cam = { .name = "camif-upll", /* same as 2440 name */ .id = -1, - .get_rate = s3c2412_getrate_cam, - .set_rate = s3c2412_setrate_cam, - .set_parent = s3c2412_setparent_cam, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_cam, + .set_rate = s3c2412_setrate_cam, + .set_parent = s3c2412_setparent_cam, + .round_rate = s3c2412_roundrate_clksrc, + }, }; /* standard clock definitions */ diff --git a/arch/arm/mach-s3c2440/clock.c b/arch/arm/mach-s3c2440/clock.c index d1c29b2537cd..3dc2426e2345 100644 --- a/arch/arm/mach-s3c2440/clock.c +++ b/arch/arm/mach-s3c2440/clock.c @@ -98,8 +98,10 @@ static struct clk s3c2440_clk_cam = { static struct clk s3c2440_clk_cam_upll = { .name = "camif-upll", .id = -1, - .set_rate = s3c2440_camif_upll_setrate, - .round_rate = s3c2440_camif_upll_round, + .ops = &(struct clk_ops) { + .set_rate = s3c2440_camif_upll_setrate, + .round_rate = s3c2440_camif_upll_round, + }, }; static struct clk s3c2440_clk_ac97 = { diff --git a/arch/arm/mach-s3c2442/clock.c b/arch/arm/mach-s3c2442/clock.c index ea1aa1f5157a..d9b692a12480 100644 --- a/arch/arm/mach-s3c2442/clock.c +++ b/arch/arm/mach-s3c2442/clock.c @@ -109,8 +109,10 @@ static struct clk s3c2442_clk_cam = { static struct clk s3c2442_clk_cam_upll = { .name = "camif-upll", .id = -1, - .set_rate = s3c2442_camif_upll_setrate, - .round_rate = s3c2442_camif_upll_round, + .ops = &(struct clk_ops) { + .set_rate = s3c2442_camif_upll_setrate, + .round_rate = s3c2442_camif_upll_round, + }, }; static int s3c2442_clk_add(struct sys_device *sysdev) diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c index 2785d69c95b0..3eb8b935d64c 100644 --- a/arch/arm/mach-s3c2443/clock.c +++ b/arch/arm/mach-s3c2443/clock.c @@ -187,7 +187,9 @@ static int s3c2443_setparent_epllref(struct clk *clk, struct clk *parent) static struct clk clk_epllref = { .name = "epllref", .id = -1, - .set_parent = s3c2443_setparent_epllref, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_epllref, + }, }; static unsigned long s3c2443_getrate_mdivclk(struct clk *clk) @@ -205,7 +207,9 @@ static struct clk clk_mdivclk = { .name = "mdivclk", .parent = &clk_mpllref, .id = -1, - .get_rate = s3c2443_getrate_mdivclk, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_mdivclk, + }, }; static int s3c2443_setparent_msysclk(struct clk *clk, struct clk *parent) @@ -232,7 +236,9 @@ static struct clk clk_msysclk = { .name = "msysclk", .parent = &clk_xtal, .id = -1, - .set_parent = s3c2443_setparent_msysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_msysclk, + }, }; /* armdiv @@ -273,7 +279,9 @@ static int s3c2443_setparent_armclk(struct clk *clk, struct clk *parent) static struct clk clk_arm = { .name = "armclk", .id = -1, - .set_parent = s3c2443_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_armclk, + }, }; /* esysclk @@ -302,7 +310,9 @@ static struct clk clk_esysclk = { .name = "esysclk", .parent = &clk_epll, .id = -1, - .set_parent = s3c2443_setparent_esysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_esysclk, + }, }; /* uartclk @@ -341,9 +351,11 @@ static struct clk clk_uart = { .name = "uartclk", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_uart, - .set_rate = s3c2443_setrate_uart, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_uart, + .set_rate = s3c2443_setrate_uart, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* hsspi @@ -384,9 +396,11 @@ static struct clk clk_hsspi = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_HSSPICLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_hsspi, - .set_rate = s3c2443_setrate_hsspi, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_hsspi, + .set_rate = s3c2443_setrate_hsspi, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; /* usbhost @@ -426,9 +440,11 @@ static struct clk clk_usb_bus_host = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_USBHOST, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_usbhost, - .set_rate = s3c2443_setrate_usbhost, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_usbhost, + .set_rate = s3c2443_setrate_usbhost, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; /* clk_hsmcc_div @@ -468,9 +484,11 @@ static struct clk clk_hsmmc_div = { .name = "hsmmc-div", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_hsmmc_div, - .set_rate = s3c2443_setrate_hsmmc_div, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_hsmmc_div, + .set_rate = s3c2443_setrate_hsmmc_div, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; static int s3c2443_setparent_hsmmc(struct clk *clk, struct clk *parent) @@ -505,7 +523,9 @@ static struct clk clk_hsmmc = { .id = -1, .parent = &clk_hsmmc_div, .enable = s3c2443_enable_hsmmc, - .set_parent = s3c2443_setparent_hsmmc, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_hsmmc, + }, }; /* i2s_eplldiv @@ -543,9 +563,11 @@ static struct clk clk_i2s_eplldiv = { .name = "i2s-eplldiv", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_i2s_eplldiv, - .set_rate = s3c2443_setrate_i2s_eplldiv, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_i2s_eplldiv, + .set_rate = s3c2443_setrate_i2s_eplldiv, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* i2s-ref @@ -578,7 +600,9 @@ static struct clk clk_i2s = { .parent = &clk_i2s_eplldiv, .ctrlbit = S3C2443_SCLKCON_I2SCLK, .enable = s3c2443_clkcon_enable_s, - .set_parent = s3c2443_setparent_i2s, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_i2s, + }, }; /* cam-if @@ -618,9 +642,11 @@ static struct clk clk_cam = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_CAMCLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_cam, - .set_rate = s3c2443_setrate_cam, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_cam, + .set_rate = s3c2443_setrate_cam, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* display-if @@ -660,9 +686,11 @@ static struct clk clk_display = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_DISPCLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_display, - .set_rate = s3c2443_setrate_display, - .round_rate = s3c2443_roundrate_clksrc256, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_display, + .set_rate = s3c2443_setrate_display, + .round_rate = s3c2443_roundrate_clksrc256, + }, }; /* prediv @@ -685,7 +713,9 @@ static struct clk clk_prediv = { .name = "prediv", .id = -1, .parent = &clk_msysclk, - .get_rate = s3c2443_prediv_getrate, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_prediv_getrate, + }, }; /* standard clock definitions */ @@ -1074,14 +1104,7 @@ void __init s3c2443_init_clocks(int xtal) /* register clocks from clock array */ - clkp = init_clocks; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); /* We must be careful disabling the clocks we are not intending to * be using at boot time, as subsystems such as the LCD which do diff --git a/arch/arm/mach-s3c6400/include/mach/entry-macro.S b/arch/arm/mach-s3c6400/include/mach/entry-macro.S index fbd90d2cf355..33a8fe240882 100644 --- a/arch/arm/mach-s3c6400/include/mach/entry-macro.S +++ b/arch/arm/mach-s3c6400/include/mach/entry-macro.S @@ -12,33 +12,7 @@ * warranty of any kind, whether express or implied. */ -#include <asm/hardware/vic.h> #include <mach/map.h> #include <plat/irqs.h> - .macro disable_fiq - .endm - - .macro get_irqnr_preamble, base, tmp - ldr \base, =S3C_VA_VIC0 - .endm - - .macro arch_ret_to_user, tmp1, tmp2 - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - @ check the vic0 - mov \irqnr, # S3C_IRQ_OFFSET + 31 - ldr \irqstat, [ \base, # VIC_IRQ_STATUS ] - teq \irqstat, #0 - - @ otherwise try vic1 - addeq \tmp, \base, #(S3C_VA_VIC1 - S3C_VA_VIC0) - addeq \irqnr, \irqnr, #32 - ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ] - teqeq \irqstat, #0 - - clzne \irqstat, \irqstat - subne \irqnr, \irqnr, \irqstat - .endm +#include <asm/entry-macro-vic2.S> diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index 106ee13581e2..d4cd3abe3cba 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -70,8 +70,8 @@ #define S3C64XX_VA_USB_HSPHY S3C_ADDR_CPU(0x00200000) /* place VICs close together */ -#define S3C_VA_VIC0 (S3C_VA_IRQ + 0x00) -#define S3C_VA_VIC1 (S3C_VA_IRQ + 0x10000) +#define VA_VIC0 (S3C_VA_IRQ + 0x00) +#define VA_VIC1 (S3C_VA_IRQ + 0x10000) /* compatibiltiy defines. */ #define S3C_PA_TIMER S3C64XX_PA_TIMER diff --git a/arch/arm/mach-s3c6400/include/mach/tick.h b/arch/arm/mach-s3c6400/include/mach/tick.h index d9c0dc7014ec..ebe18a9469b8 100644 --- a/arch/arm/mach-s3c6400/include/mach/tick.h +++ b/arch/arm/mach-s3c6400/include/mach/tick.h @@ -20,7 +20,7 @@ */ static inline u32 s3c24xx_ostimer_pending(void) { - u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS); + u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS); return pend & 1 << (IRQ_TIMER4_VIC - S3C64XX_IRQ_VIC0(0)); } diff --git a/arch/arm/mach-s5pc100/setup-sdhci.c b/arch/arm/mach-s5pc100/setup-sdhci.c index 4385986a3da0..ea7ff19adb95 100644 --- a/arch/arm/mach-s5pc100/setup-sdhci.c +++ b/arch/arm/mach-s5pc100/setup-sdhci.c @@ -28,8 +28,8 @@ char *s5pc100_hsmmc_clksrcs[4] = { [0] = "hsmmc", [1] = "hsmmc", - /* [2] = "mmc_bus", not yet succesfuuly used yet */ - /* [3] = "48m", - note not succesfully used yet */ + /* [2] = "mmc_bus", not yet successfully used yet */ + /* [3] = "48m", - note not successfully used yet */ }; diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c index 111f7ea32b38..c174ed1f3691 100644 --- a/arch/arm/mach-u300/clock.c +++ b/arch/arm/mach-u300/clock.c @@ -1276,11 +1276,8 @@ static struct clk_lookup lookups[] = { static void __init clk_register(void) { - int i; - /* Register the lookups */ - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); } /* diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c index 20b6ebb6783a..8359a73d0041 100644 --- a/arch/arm/mach-ux500/clock.c +++ b/arch/arm/mach-ux500/clock.c @@ -85,11 +85,8 @@ static struct clk_lookup lookups[] = { static int __init clk_init(void) { - int i; - /* register the clock lookups */ - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); return 0; } arch_initcall(clk_init); diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index e13be7c444ca..9ddb49b1cb71 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -851,8 +851,7 @@ void __init versatile_init(void) { int i; - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); platform_device_register(&versatile_flash_device); platform_device_register(&versatile_i2c_device); diff --git a/arch/arm/mach-w90x900/clock.c b/arch/arm/mach-w90x900/clock.c index b785994bab0a..2c371ff22e51 100644 --- a/arch/arm/mach-w90x900/clock.c +++ b/arch/arm/mach-w90x900/clock.c @@ -90,12 +90,3 @@ void nuc900_subclk_enable(struct clk *clk, int enable) __raw_writel(clken, W90X900_VA_CLKPWR + SUBCLK); } - - -void clks_register(struct clk_lookup *clks, size_t num) -{ - int i; - - for (i = 0; i < num; i++) - clkdev_add(&clks[i]); -} diff --git a/arch/arm/mach-w90x900/clock.h b/arch/arm/mach-w90x900/clock.h index f5816a06eed6..c56ddab3d912 100644 --- a/arch/arm/mach-w90x900/clock.h +++ b/arch/arm/mach-w90x900/clock.h @@ -14,7 +14,6 @@ void nuc900_clk_enable(struct clk *clk, int enable); void nuc900_subclk_enable(struct clk *clk, int enable); -void clks_register(struct clk_lookup *clks, size_t num); struct clk { unsigned long cken; diff --git a/arch/arm/mach-w90x900/cpu.c b/arch/arm/mach-w90x900/cpu.c index 20dc0c96214d..6f5ca532883f 100644 --- a/arch/arm/mach-w90x900/cpu.c +++ b/arch/arm/mach-w90x900/cpu.c @@ -208,6 +208,6 @@ void __init nuc900_map_io(struct map_desc *mach_desc, int mach_size) void __init nuc900_init_clocks(void) { - clks_register(nuc900_clkregs, ARRAY_SIZE(nuc900_clkregs)); + clkdev_add_table(nuc900_clkregs, ARRAY_SIZE(nuc900_clkregs)); } diff --git a/arch/arm/mach-w90x900/include/mach/system.h b/arch/arm/mach-w90x900/include/mach/system.h index 940640066857..ce228bdc66dd 100644 --- a/arch/arm/mach-w90x900/include/mach/system.h +++ b/arch/arm/mach-w90x900/include/mach/system.h @@ -15,7 +15,15 @@ * */ +#include <linux/io.h> #include <asm/proc-fns.h> +#include <mach/map.h> +#include <mach/regs-timer.h> + +#define WTCR (TMR_BA + 0x1C) +#define WTCLK (1 << 10) +#define WTE (1 << 7) +#define WTRE (1 << 1) static void arch_idle(void) { @@ -23,6 +31,11 @@ static void arch_idle(void) static void arch_reset(char mode, const char *cmd) { - cpu_reset(0); + if (mode == 's') { + /* Jump into ROM at address 0 */ + cpu_reset(0); + } else { + __raw_writel(WTE | WTRE | WTCLK, WTCR); + } } diff --git a/arch/arm/mach-w90x900/time.c b/arch/arm/mach-w90x900/time.c index 4128af870b41..b80f769bc135 100644 --- a/arch/arm/mach-w90x900/time.c +++ b/arch/arm/mach-w90x900/time.c @@ -42,7 +42,10 @@ #define TICKS_PER_SEC 100 #define PRESCALE 0x63 /* Divider = prescale + 1 */ -unsigned int timer0_load; +#define TDR_SHIFT 24 +#define TDR_MASK ((1 << TDR_SHIFT) - 1) + +static unsigned int timer0_load; static void nuc900_clockevent_setmode(enum clock_event_mode mode, struct clock_event_device *clk) @@ -88,7 +91,7 @@ static int nuc900_clockevent_setnextevent(unsigned long evt, static struct clock_event_device nuc900_clockevent_device = { .name = "nuc900-timer0", .shift = 32, - .features = CLOCK_EVT_MODE_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_mode = nuc900_clockevent_setmode, .set_next_event = nuc900_clockevent_setnextevent, .rating = 300, @@ -112,8 +115,23 @@ static struct irqaction nuc900_timer0_irq = { .handler = nuc900_timer0_interrupt, }; -static void __init nuc900_clockevents_init(unsigned int rate) +static void __init nuc900_clockevents_init(void) { + unsigned int rate; + struct clk *clk = clk_get(NULL, "timer0"); + + BUG_ON(IS_ERR(clk)); + + __raw_writel(0x00, REG_TCSR0); + + clk_enable(clk); + rate = clk_get_rate(clk) / (PRESCALE + 1); + + timer0_load = (rate / TICKS_PER_SEC); + + __raw_writel(RESETINT, REG_TISR); + setup_irq(IRQ_TIMER0, &nuc900_timer0_irq); + nuc900_clockevent_device.mult = div_sc(rate, NSEC_PER_SEC, nuc900_clockevent_device.shift); nuc900_clockevent_device.max_delta_ns = clockevent_delta2ns(0xffffffff, @@ -127,26 +145,35 @@ static void __init nuc900_clockevents_init(unsigned int rate) static cycle_t nuc900_get_cycles(struct clocksource *cs) { - return ~__raw_readl(REG_TDR1); + return (~__raw_readl(REG_TDR1)) & TDR_MASK; } static struct clocksource clocksource_nuc900 = { .name = "nuc900-timer1", .rating = 200, .read = nuc900_get_cycles, - .mask = CLOCKSOURCE_MASK(32), - .shift = 20, + .mask = CLOCKSOURCE_MASK(TDR_SHIFT), + .shift = 10, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -static void __init nuc900_clocksource_init(unsigned int rate) +static void __init nuc900_clocksource_init(void) { unsigned int val; + unsigned int rate; + struct clk *clk = clk_get(NULL, "timer1"); + + BUG_ON(IS_ERR(clk)); + + __raw_writel(0x00, REG_TCSR1); + + clk_enable(clk); + rate = clk_get_rate(clk) / (PRESCALE + 1); __raw_writel(0xffffffff, REG_TICR1); val = __raw_readl(REG_TCSR1); - val |= (COUNTEN | PERIOD); + val |= (COUNTEN | PERIOD | PRESCALE); __raw_writel(val, REG_TCSR1); clocksource_nuc900.mult = @@ -156,25 +183,8 @@ static void __init nuc900_clocksource_init(unsigned int rate) static void __init nuc900_timer_init(void) { - struct clk *ck_ext = clk_get(NULL, "ext"); - unsigned int rate; - - BUG_ON(IS_ERR(ck_ext)); - - rate = clk_get_rate(ck_ext); - clk_put(ck_ext); - rate = rate / (PRESCALE + 0x01); - - /* set a known state */ - __raw_writel(0x00, REG_TCSR0); - __raw_writel(0x00, REG_TCSR1); - __raw_writel(RESETINT, REG_TISR); - timer0_load = (rate / TICKS_PER_SEC); - - setup_irq(IRQ_TIMER0, &nuc900_timer0_irq); - - nuc900_clocksource_init(rate); - nuc900_clockevents_init(rate); + nuc900_clocksource_init(); + nuc900_clockevents_init(); } struct sys_timer nuc900_timer = { diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 827e238e5d4a..e8d34a80851c 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -27,6 +27,9 @@ obj-$(CONFIG_CPU_ABRT_EV5TJ) += abort-ev5tj.o obj-$(CONFIG_CPU_ABRT_EV6) += abort-ev6.o obj-$(CONFIG_CPU_ABRT_EV7) += abort-ev7.o +AFLAGS_abort-ev6.o :=-Wa,-march=armv6k +AFLAGS_abort-ev7.o :=-Wa,-march=armv7-a + obj-$(CONFIG_CPU_PABRT_LEGACY) += pabort-legacy.o obj-$(CONFIG_CPU_PABRT_V6) += pabort-v6.o obj-$(CONFIG_CPU_PABRT_V7) += pabort-v7.o @@ -39,6 +42,9 @@ obj-$(CONFIG_CPU_CACHE_V6) += cache-v6.o obj-$(CONFIG_CPU_CACHE_V7) += cache-v7.o obj-$(CONFIG_CPU_CACHE_FA) += cache-fa.o +AFLAGS_cache-v6.o :=-Wa,-march=armv6 +AFLAGS_cache-v7.o :=-Wa,-march=armv7-a + obj-$(CONFIG_CPU_COPY_V3) += copypage-v3.o obj-$(CONFIG_CPU_COPY_V4WT) += copypage-v4wt.o obj-$(CONFIG_CPU_COPY_V4WB) += copypage-v4wb.o @@ -58,6 +64,9 @@ obj-$(CONFIG_CPU_TLB_V6) += tlb-v6.o obj-$(CONFIG_CPU_TLB_V7) += tlb-v7.o obj-$(CONFIG_CPU_TLB_FA) += tlb-fa.o +AFLAGS_tlb-v6.o :=-Wa,-march=armv6 +AFLAGS_tlb-v7.o :=-Wa,-march=armv7-a + obj-$(CONFIG_CPU_ARM610) += proc-arm6_7.o obj-$(CONFIG_CPU_ARM710) += proc-arm6_7.o obj-$(CONFIG_CPU_ARM7TDMI) += proc-arm7tdmi.o @@ -84,6 +93,9 @@ obj-$(CONFIG_CPU_FEROCEON) += proc-feroceon.o obj-$(CONFIG_CPU_V6) += proc-v6.o obj-$(CONFIG_CPU_V7) += proc-v7.o +AFLAGS_proc-v6.o :=-Wa,-march=armv6 +AFLAGS_proc-v7.o :=-Wa,-march=armv7-a + obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index b270d6228fe2..0c5eb6983cef 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -898,11 +898,7 @@ static int __init alignment_init(void) #ifdef CONFIG_PROC_FS struct proc_dir_entry *res; - res = proc_mkdir("cpu", NULL); - if (!res) - return -ENOMEM; - - res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); + res = create_proc_entry("cpu/alignment", S_IWUSR | S_IRUGO, NULL); if (!res) return -ENOMEM; diff --git a/arch/arm/mm/cache-fa.S b/arch/arm/mm/cache-fa.S index a89444a3c016..7148e53e6078 100644 --- a/arch/arm/mm/cache-fa.S +++ b/arch/arm/mm/cache-fa.S @@ -157,7 +157,7 @@ ENTRY(fa_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -ENTRY(fa_dma_inv_range) +fa_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c14, 1 @ clean & invalidate D entry @@ -180,7 +180,7 @@ ENTRY(fa_dma_inv_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(fa_dma_clean_range) +fa_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -205,6 +205,30 @@ ENTRY(fa_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain write buffer mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(fa_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq fa_dma_clean_range + bcs fa_dma_inv_range + b fa_dma_flush_range +ENDPROC(fa_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(fa_dma_unmap_area) + mov pc, lr +ENDPROC(fa_dma_unmap_area) + __INITDATA .type fa_cache_fns, #object @@ -215,7 +239,7 @@ ENTRY(fa_cache_fns) .long fa_coherent_kern_range .long fa_coherent_user_range .long fa_flush_kern_dcache_area - .long fa_dma_inv_range - .long fa_dma_clean_range + .long fa_dma_map_area + .long fa_dma_unmap_area .long fa_dma_flush_range .size fa_cache_fns, . - fa_cache_fns diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S index 2a482731ea36..c2ff3c599fee 100644 --- a/arch/arm/mm/cache-v3.S +++ b/arch/arm/mm/cache-v3.S @@ -84,20 +84,6 @@ ENTRY(v3_flush_kern_dcache_area) /* FALLTHROUGH */ /* - * dma_inv_range(start, end) - * - * Invalidate (discard) the specified virtual address range. - * May not write back any entries. If 'start' or 'end' - * are not cache line aligned, those lines must be written - * back. - * - * - start - virtual start address - * - end - virtual end address - */ -ENTRY(v3_dma_inv_range) - /* FALLTHROUGH */ - -/* * dma_flush_range(start, end) * * Clean and invalidate the specified virtual address range. @@ -108,18 +94,29 @@ ENTRY(v3_dma_inv_range) ENTRY(v3_dma_flush_range) mov r0, #0 mcr p15, 0, r0, c7, c0, 0 @ flush ID cache + mov pc, lr + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v3_dma_unmap_area) + teq r2, #DMA_TO_DEVICE + bne v3_dma_flush_range /* FALLTHROUGH */ /* - * dma_clean_range(start, end) - * - * Clean (write back) the specified virtual address range. - * - * - start - virtual start address - * - end - virtual end address + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction */ -ENTRY(v3_dma_clean_range) +ENTRY(v3_dma_map_area) mov pc, lr +ENDPROC(v3_dma_unmap_area) +ENDPROC(v3_dma_map_area) __INITDATA @@ -131,7 +128,7 @@ ENTRY(v3_cache_fns) .long v3_coherent_kern_range .long v3_coherent_user_range .long v3_flush_kern_dcache_area - .long v3_dma_inv_range - .long v3_dma_clean_range + .long v3_dma_map_area + .long v3_dma_unmap_area .long v3_dma_flush_range .size v3_cache_fns, . - v3_cache_fns diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S index 5c7da3e372e9..4810f7e3e813 100644 --- a/arch/arm/mm/cache-v4.S +++ b/arch/arm/mm/cache-v4.S @@ -94,20 +94,6 @@ ENTRY(v4_flush_kern_dcache_area) /* FALLTHROUGH */ /* - * dma_inv_range(start, end) - * - * Invalidate (discard) the specified virtual address range. - * May not write back any entries. If 'start' or 'end' - * are not cache line aligned, those lines must be written - * back. - * - * - start - virtual start address - * - end - virtual end address - */ -ENTRY(v4_dma_inv_range) - /* FALLTHROUGH */ - -/* * dma_flush_range(start, end) * * Clean and invalidate the specified virtual address range. @@ -120,18 +106,29 @@ ENTRY(v4_dma_flush_range) mov r0, #0 mcr p15, 0, r0, c7, c7, 0 @ flush ID cache #endif + mov pc, lr + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v4_dma_unmap_area) + teq r2, #DMA_TO_DEVICE + bne v4_dma_flush_range /* FALLTHROUGH */ /* - * dma_clean_range(start, end) - * - * Clean (write back) the specified virtual address range. - * - * - start - virtual start address - * - end - virtual end address + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction */ -ENTRY(v4_dma_clean_range) +ENTRY(v4_dma_map_area) mov pc, lr +ENDPROC(v4_dma_unmap_area) +ENDPROC(v4_dma_map_area) __INITDATA @@ -143,7 +140,7 @@ ENTRY(v4_cache_fns) .long v4_coherent_kern_range .long v4_coherent_user_range .long v4_flush_kern_dcache_area - .long v4_dma_inv_range - .long v4_dma_clean_range + .long v4_dma_map_area + .long v4_dma_unmap_area .long v4_dma_flush_range .size v4_cache_fns, . - v4_cache_fns diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S index 3dbedf1ec0e7..df8368afa102 100644 --- a/arch/arm/mm/cache-v4wb.S +++ b/arch/arm/mm/cache-v4wb.S @@ -173,7 +173,7 @@ ENTRY(v4wb_coherent_user_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(v4wb_dma_inv_range) +v4wb_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -194,7 +194,7 @@ ENTRY(v4wb_dma_inv_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(v4wb_dma_clean_range) +v4wb_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -216,6 +216,30 @@ ENTRY(v4wb_dma_clean_range) .globl v4wb_dma_flush_range .set v4wb_dma_flush_range, v4wb_coherent_kern_range +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v4wb_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq v4wb_dma_clean_range + bcs v4wb_dma_inv_range + b v4wb_dma_flush_range +ENDPROC(v4wb_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v4wb_dma_unmap_area) + mov pc, lr +ENDPROC(v4wb_dma_unmap_area) + __INITDATA .type v4wb_cache_fns, #object @@ -226,7 +250,7 @@ ENTRY(v4wb_cache_fns) .long v4wb_coherent_kern_range .long v4wb_coherent_user_range .long v4wb_flush_kern_dcache_area - .long v4wb_dma_inv_range - .long v4wb_dma_clean_range + .long v4wb_dma_map_area + .long v4wb_dma_unmap_area .long v4wb_dma_flush_range .size v4wb_cache_fns, . - v4wb_cache_fns diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S index b3b7410270b4..45c70312f43b 100644 --- a/arch/arm/mm/cache-v4wt.S +++ b/arch/arm/mm/cache-v4wt.S @@ -142,23 +142,12 @@ ENTRY(v4wt_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -ENTRY(v4wt_dma_inv_range) +v4wt_dma_inv_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b - /* FALLTHROUGH */ - -/* - * dma_clean_range(start, end) - * - * Clean the specified virtual address range. - * - * - start - virtual start address - * - end - virtual end address - */ -ENTRY(v4wt_dma_clean_range) mov pc, lr /* @@ -172,6 +161,29 @@ ENTRY(v4wt_dma_clean_range) .globl v4wt_dma_flush_range .equ v4wt_dma_flush_range, v4wt_dma_inv_range +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v4wt_dma_unmap_area) + add r1, r1, r0 + teq r2, #DMA_TO_DEVICE + bne v4wt_dma_inv_range + /* FALLTHROUGH */ + +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v4wt_dma_map_area) + mov pc, lr +ENDPROC(v4wt_dma_unmap_area) +ENDPROC(v4wt_dma_map_area) + __INITDATA .type v4wt_cache_fns, #object @@ -182,7 +194,7 @@ ENTRY(v4wt_cache_fns) .long v4wt_coherent_kern_range .long v4wt_coherent_user_range .long v4wt_flush_kern_dcache_area - .long v4wt_dma_inv_range - .long v4wt_dma_clean_range + .long v4wt_dma_map_area + .long v4wt_dma_unmap_area .long v4wt_dma_flush_range .size v4wt_cache_fns, . - v4wt_cache_fns diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 4ba0a24ce6f5..9d89c67a1cc3 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -195,7 +195,7 @@ ENTRY(v6_flush_kern_dcache_area) * - start - virtual start address of region * - end - virtual end address of region */ -ENTRY(v6_dma_inv_range) +v6_dma_inv_range: tst r0, #D_CACHE_LINE_SIZE - 1 bic r0, r0, #D_CACHE_LINE_SIZE - 1 #ifdef HARVARD_CACHE @@ -228,7 +228,7 @@ ENTRY(v6_dma_inv_range) * - start - virtual start address of region * - end - virtual end address of region */ -ENTRY(v6_dma_clean_range) +v6_dma_clean_range: bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef HARVARD_CACHE @@ -263,6 +263,32 @@ ENTRY(v6_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain write buffer mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v6_dma_map_area) + add r1, r1, r0 + teq r2, #DMA_FROM_DEVICE + beq v6_dma_inv_range + b v6_dma_clean_range +ENDPROC(v6_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v6_dma_unmap_area) + add r1, r1, r0 + teq r2, #DMA_TO_DEVICE + bne v6_dma_inv_range + mov pc, lr +ENDPROC(v6_dma_unmap_area) + __INITDATA .type v6_cache_fns, #object @@ -273,7 +299,7 @@ ENTRY(v6_cache_fns) .long v6_coherent_kern_range .long v6_coherent_user_range .long v6_flush_kern_dcache_area - .long v6_dma_inv_range - .long v6_dma_clean_range + .long v6_dma_map_area + .long v6_dma_unmap_area .long v6_dma_flush_range .size v6_cache_fns, . - v6_cache_fns diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 9073db849fb4..bcd64f265870 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -216,7 +216,7 @@ ENDPROC(v7_flush_kern_dcache_area) * - start - virtual start address of region * - end - virtual end address of region */ -ENTRY(v7_dma_inv_range) +v7_dma_inv_range: dcache_line_size r2, r3 sub r3, r2, #1 tst r0, r3 @@ -240,7 +240,7 @@ ENDPROC(v7_dma_inv_range) * - start - virtual start address of region * - end - virtual end address of region */ -ENTRY(v7_dma_clean_range) +v7_dma_clean_range: dcache_line_size r2, r3 sub r3, r2, #1 bic r0, r0, r3 @@ -271,6 +271,32 @@ ENTRY(v7_dma_flush_range) mov pc, lr ENDPROC(v7_dma_flush_range) +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v7_dma_map_area) + add r1, r1, r0 + teq r2, #DMA_FROM_DEVICE + beq v7_dma_inv_range + b v7_dma_clean_range +ENDPROC(v7_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(v7_dma_unmap_area) + add r1, r1, r0 + teq r2, #DMA_TO_DEVICE + bne v7_dma_inv_range + mov pc, lr +ENDPROC(v7_dma_unmap_area) + __INITDATA .type v7_cache_fns, #object @@ -281,7 +307,7 @@ ENTRY(v7_cache_fns) .long v7_coherent_kern_range .long v7_coherent_user_range .long v7_flush_kern_dcache_area - .long v7_dma_inv_range - .long v7_dma_clean_range + .long v7_dma_map_area + .long v7_dma_unmap_area .long v7_dma_flush_range .size v7_cache_fns, . - v7_cache_fns diff --git a/arch/arm/mm/cache-xsc3l2.c b/arch/arm/mm/cache-xsc3l2.c index 5d180cb0bd94..c3154928bccd 100644 --- a/arch/arm/mm/cache-xsc3l2.c +++ b/arch/arm/mm/cache-xsc3l2.c @@ -221,15 +221,14 @@ static int __init xsc3_l2_init(void) if (!cpu_is_xsc3() || !xsc3_l2_present()) return 0; - if (!(get_cr() & CR_L2)) { + if (get_cr() & CR_L2) { pr_info("XScale3 L2 cache enabled.\n"); - adjust_cr(CR_L2, CR_L2); xsc3_l2_inv_all(); - } - outer_cache.inv_range = xsc3_l2_inv_range; - outer_cache.clean_range = xsc3_l2_clean_range; - outer_cache.flush_range = xsc3_l2_flush_range; + outer_cache.inv_range = xsc3_l2_inv_range; + outer_cache.clean_range = xsc3_l2_clean_range; + outer_cache.flush_range = xsc3_l2_flush_range; + } return 0; } diff --git a/arch/arm/mm/copypage-feroceon.c b/arch/arm/mm/copypage-feroceon.c index 70997d5bee2d..5eb4fd93893d 100644 --- a/arch/arm/mm/copypage-feroceon.c +++ b/arch/arm/mm/copypage-feroceon.c @@ -68,12 +68,13 @@ feroceon_copy_user_page(void *kto, const void *kfrom) } void feroceon_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; kto = kmap_atomic(to, KM_USER0); kfrom = kmap_atomic(from, KM_USER1); + flush_cache_page(vma, vaddr, page_to_pfn(from)); feroceon_copy_user_page(kto, kfrom); kunmap_atomic(kfrom, KM_USER1); kunmap_atomic(kto, KM_USER0); diff --git a/arch/arm/mm/copypage-v3.c b/arch/arm/mm/copypage-v3.c index de9c06854ad7..f72303e1d804 100644 --- a/arch/arm/mm/copypage-v3.c +++ b/arch/arm/mm/copypage-v3.c @@ -38,7 +38,7 @@ v3_copy_user_page(void *kto, const void *kfrom) } void v3_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c index 7370a7142b04..598c51ad5071 100644 --- a/arch/arm/mm/copypage-v4mc.c +++ b/arch/arm/mm/copypage-v4mc.c @@ -69,7 +69,7 @@ mc_copy_user_page(void *from, void *to) } void v4_mc_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto = kmap_atomic(to, KM_USER1); diff --git a/arch/arm/mm/copypage-v4wb.c b/arch/arm/mm/copypage-v4wb.c index 9ab098414227..7c2eb55cd4a9 100644 --- a/arch/arm/mm/copypage-v4wb.c +++ b/arch/arm/mm/copypage-v4wb.c @@ -48,12 +48,13 @@ v4wb_copy_user_page(void *kto, const void *kfrom) } void v4wb_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; kto = kmap_atomic(to, KM_USER0); kfrom = kmap_atomic(from, KM_USER1); + flush_cache_page(vma, vaddr, page_to_pfn(from)); v4wb_copy_user_page(kto, kfrom); kunmap_atomic(kfrom, KM_USER1); kunmap_atomic(kto, KM_USER0); diff --git a/arch/arm/mm/copypage-v4wt.c b/arch/arm/mm/copypage-v4wt.c index 300efafd6643..172e6a55458e 100644 --- a/arch/arm/mm/copypage-v4wt.c +++ b/arch/arm/mm/copypage-v4wt.c @@ -44,7 +44,7 @@ v4wt_copy_user_page(void *kto, const void *kfrom) } void v4wt_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index 0fa1319273de..8bca4dea6dfa 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -34,7 +34,7 @@ static DEFINE_SPINLOCK(v6_lock); * attack the kernel's existing mapping of these pages. */ static void v6_copy_user_highpage_nonaliasing(struct page *to, - struct page *from, unsigned long vaddr) + struct page *from, unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; @@ -81,7 +81,7 @@ static void discard_old_kernel_data(void *kto) * Copy the page, taking account of the cache colour. */ static void v6_copy_user_highpage_aliasing(struct page *to, - struct page *from, unsigned long vaddr) + struct page *from, unsigned long vaddr, struct vm_area_struct *vma) { unsigned int offset = CACHE_COLOUR(vaddr); unsigned long kfrom, kto; diff --git a/arch/arm/mm/copypage-xsc3.c b/arch/arm/mm/copypage-xsc3.c index bc4525f5ab23..747ad4140fc7 100644 --- a/arch/arm/mm/copypage-xsc3.c +++ b/arch/arm/mm/copypage-xsc3.c @@ -71,12 +71,13 @@ xsc3_mc_copy_user_page(void *kto, const void *kfrom) } void xsc3_mc_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto, *kfrom; kto = kmap_atomic(to, KM_USER0); kfrom = kmap_atomic(from, KM_USER1); + flush_cache_page(vma, vaddr, page_to_pfn(from)); xsc3_mc_copy_user_page(kto, kfrom); kunmap_atomic(kfrom, KM_USER1); kunmap_atomic(kto, KM_USER0); diff --git a/arch/arm/mm/copypage-xscale.c b/arch/arm/mm/copypage-xscale.c index 76824d3e966a..9920c0ae2096 100644 --- a/arch/arm/mm/copypage-xscale.c +++ b/arch/arm/mm/copypage-xscale.c @@ -91,7 +91,7 @@ mc_copy_user_page(void *from, void *to) } void xscale_mc_copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr) + unsigned long vaddr, struct vm_area_struct *vma) { void *kto = kmap_atomic(to, KM_USER1); diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 26325cb5d368..64daef2173bd 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -404,78 +404,44 @@ EXPORT_SYMBOL(dma_free_coherent); * platforms with CONFIG_DMABOUNCE. * Use the driver DMA support - see dma-mapping.h (dma_sync_*) */ -void dma_cache_maint(const void *start, size_t size, int direction) +void ___dma_single_cpu_to_dev(const void *kaddr, size_t size, + enum dma_data_direction dir) { - void (*inner_op)(const void *, const void *); - void (*outer_op)(unsigned long, unsigned long); - - BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(start + size - 1)); - - switch (direction) { - case DMA_FROM_DEVICE: /* invalidate only */ - inner_op = dmac_inv_range; - outer_op = outer_inv_range; - break; - case DMA_TO_DEVICE: /* writeback only */ - inner_op = dmac_clean_range; - outer_op = outer_clean_range; - break; - case DMA_BIDIRECTIONAL: /* writeback and invalidate */ - inner_op = dmac_flush_range; - outer_op = outer_flush_range; - break; - default: - BUG(); - } + unsigned long paddr; + + BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); + + dmac_map_area(kaddr, size, dir); - inner_op(start, start + size); - outer_op(__pa(start), __pa(start) + size); + paddr = __pa(kaddr); + if (dir == DMA_FROM_DEVICE) { + outer_inv_range(paddr, paddr + size); + } else { + outer_clean_range(paddr, paddr + size); + } + /* FIXME: non-speculating: flush on bidirectional mappings? */ } -EXPORT_SYMBOL(dma_cache_maint); +EXPORT_SYMBOL(___dma_single_cpu_to_dev); -static void dma_cache_maint_contiguous(struct page *page, unsigned long offset, - size_t size, int direction) +void ___dma_single_dev_to_cpu(const void *kaddr, size_t size, + enum dma_data_direction dir) { - void *vaddr; - unsigned long paddr; - void (*inner_op)(const void *, const void *); - void (*outer_op)(unsigned long, unsigned long); - - switch (direction) { - case DMA_FROM_DEVICE: /* invalidate only */ - inner_op = dmac_inv_range; - outer_op = outer_inv_range; - break; - case DMA_TO_DEVICE: /* writeback only */ - inner_op = dmac_clean_range; - outer_op = outer_clean_range; - break; - case DMA_BIDIRECTIONAL: /* writeback and invalidate */ - inner_op = dmac_flush_range; - outer_op = outer_flush_range; - break; - default: - BUG(); - } + BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); - if (!PageHighMem(page)) { - vaddr = page_address(page) + offset; - inner_op(vaddr, vaddr + size); - } else { - vaddr = kmap_high_get(page); - if (vaddr) { - vaddr += offset; - inner_op(vaddr, vaddr + size); - kunmap_high(page); - } + /* FIXME: non-speculating: not required */ + /* don't bother invalidating if DMA to device */ + if (dir != DMA_TO_DEVICE) { + unsigned long paddr = __pa(kaddr); + outer_inv_range(paddr, paddr + size); } - paddr = page_to_phys(page) + offset; - outer_op(paddr, paddr + size); + dmac_unmap_area(kaddr, size, dir); } +EXPORT_SYMBOL(___dma_single_dev_to_cpu); -void dma_cache_maint_page(struct page *page, unsigned long offset, - size_t size, int dir) +static void dma_cache_maint_page(struct page *page, unsigned long offset, + size_t size, enum dma_data_direction dir, + void (*op)(const void *, size_t, int)) { /* * A single sg entry may refer to multiple physically contiguous @@ -486,20 +452,62 @@ void dma_cache_maint_page(struct page *page, unsigned long offset, size_t left = size; do { size_t len = left; - if (PageHighMem(page) && len + offset > PAGE_SIZE) { - if (offset >= PAGE_SIZE) { - page += offset / PAGE_SIZE; - offset %= PAGE_SIZE; + void *vaddr; + + if (PageHighMem(page)) { + if (len + offset > PAGE_SIZE) { + if (offset >= PAGE_SIZE) { + page += offset / PAGE_SIZE; + offset %= PAGE_SIZE; + } + len = PAGE_SIZE - offset; } - len = PAGE_SIZE - offset; + vaddr = kmap_high_get(page); + if (vaddr) { + vaddr += offset; + op(vaddr, len, dir); + kunmap_high(page); + } + } else { + vaddr = page_address(page) + offset; + op(vaddr, len, dir); } - dma_cache_maint_contiguous(page, offset, len, dir); offset = 0; page++; left -= len; } while (left); } -EXPORT_SYMBOL(dma_cache_maint_page); + +void ___dma_page_cpu_to_dev(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + unsigned long paddr; + + dma_cache_maint_page(page, off, size, dir, dmac_map_area); + + paddr = page_to_phys(page) + off; + if (dir == DMA_FROM_DEVICE) { + outer_inv_range(paddr, paddr + size); + } else { + outer_clean_range(paddr, paddr + size); + } + /* FIXME: non-speculating: flush on bidirectional mappings? */ +} +EXPORT_SYMBOL(___dma_page_cpu_to_dev); + +void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ + unsigned long paddr = page_to_phys(page) + off; + + /* FIXME: non-speculating: not required */ + /* don't bother invalidating if DMA to device */ + if (dir != DMA_TO_DEVICE) + outer_inv_range(paddr, paddr + size); + + dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); +} +EXPORT_SYMBOL(___dma_page_dev_to_cpu); /** * dma_map_sg - map a set of SG buffers for streaming mode DMA @@ -573,8 +581,12 @@ void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int i; for_each_sg(sg, s, nents, i) { - dmabounce_sync_for_cpu(dev, sg_dma_address(s), 0, - sg_dma_len(s), dir); + if (!dmabounce_sync_for_cpu(dev, sg_dma_address(s), 0, + sg_dma_len(s), dir)) + continue; + + __dma_page_dev_to_cpu(sg_page(s), s->offset, + s->length, dir); } } EXPORT_SYMBOL(dma_sync_sg_for_cpu); @@ -597,9 +609,8 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, sg_dma_len(s), dir)) continue; - if (!arch_is_coherent()) - dma_cache_maint_page(sg_page(s), s->offset, - s->length, dir); + __dma_page_cpu_to_dev(sg_page(s), s->offset, + s->length, dir); } } EXPORT_SYMBOL(dma_sync_sg_for_device); diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 6f3a4b7a3b82..e34f095e2090 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -13,6 +13,7 @@ #include <asm/cacheflush.h> #include <asm/cachetype.h> +#include <asm/smp_plat.h> #include <asm/system.h> #include <asm/tlbflush.h> @@ -87,13 +88,26 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) __flush_icache_all(); } +#else +#define flush_pfn_alias(pfn,vaddr) do { } while (0) +#endif +#ifdef CONFIG_SMP +static void flush_ptrace_access_other(void *args) +{ + __flush_icache_all(); +} +#endif + +static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, - unsigned long uaddr, void *kaddr, - unsigned long len, int write) + unsigned long uaddr, void *kaddr, unsigned long len) { if (cache_is_vivt()) { - vivt_flush_ptrace_access(vma, page, uaddr, kaddr, len, write); + if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { + unsigned long addr = (unsigned long)kaddr; + __cpuc_coherent_kern_range(addr, addr + len); + } return; } @@ -104,16 +118,37 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, } /* VIPT non-aliasing cache */ - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm)) && - vma->vm_flags & VM_EXEC) { + if (vma->vm_flags & VM_EXEC) { unsigned long addr = (unsigned long)kaddr; - /* only flushing the kernel mapping on non-aliasing VIPT */ __cpuc_coherent_kern_range(addr, addr + len); +#ifdef CONFIG_SMP + if (cache_ops_need_broadcast()) + smp_call_function(flush_ptrace_access_other, + NULL, 1); +#endif } } -#else -#define flush_pfn_alias(pfn,vaddr) do { } while (0) + +/* + * Copy user data from/to a page which is mapped into a different + * processes address space. Really, we want to allow our "user + * space" model to handle this. + * + * Note that this code needs to run on the current CPU. + */ +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long uaddr, void *dst, const void *src, + unsigned long len) +{ +#ifdef CONFIG_SMP + preempt_disable(); #endif + memcpy(dst, src, len); + flush_ptrace_access(vma, page, uaddr, dst, len); +#ifdef CONFIG_SMP + preempt_enable(); +#endif +} void __flush_dcache_page(struct address_space *mapping, struct page *page) { diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 52c40d155672..a340569b991e 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -32,19 +32,21 @@ static unsigned long phys_initrd_start __initdata = 0; static unsigned long phys_initrd_size __initdata = 0; -static void __init early_initrd(char **p) +static int __init early_initrd(char *p) { unsigned long start, size; + char *endp; - start = memparse(*p, p); - if (**p == ',') { - size = memparse((*p) + 1, p); + start = memparse(p, &endp); + if (*endp == ',') { + size = memparse(endp + 1, NULL); phys_initrd_start = start; phys_initrd_size = size; } + return 0; } -__early_param("initrd=", early_initrd); +early_param("initrd", early_initrd); static int __init parse_tag_initrd(const struct tag *tag) { @@ -616,7 +618,7 @@ void __init mem_init(void) "%dK data, %dK init, %luK highmem)\n", nr_free_pages() << (PAGE_SHIFT-10), codesize >> 10, datasize >> 10, initsize >> 10, - (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))); + totalhigh_pages << (PAGE_SHIFT-10)); if (PAGE_SIZE >= 16384 && num_physpages <= 128) { extern int sysctl_overcommit_memory; diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 0ab75c60f7cf..28c8b950ef04 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -139,8 +139,8 @@ void __check_kvm_seq(struct mm_struct *mm) * which requires the new ioremap'd region to be referenced, the CPU will * reference the _old_ region. * - * Note that get_vm_area() allocates a guard 4K page, so we need to mask - * the size back to 1MB aligned or we will overflow in the loop below. + * Note that get_vm_area_caller() allocates a guard 4K page, so we need to + * mask the size back to 1MB aligned or we will overflow in the loop below. */ static void unmap_area_sections(unsigned long virt, unsigned long size) { @@ -254,22 +254,8 @@ remap_area_supersections(unsigned long virt, unsigned long pfn, } #endif - -/* - * Remap an arbitrary physical address space into the kernel virtual - * address space. Needed when the kernel wants to access high addresses - * directly. - * - * NOTE! We need to allow non-page-aligned mappings too: we will obviously - * have to convert them into an offset in a page-aligned mapping, but the - * caller shouldn't need to know that small detail. - * - * 'flags' are the extra L_PTE_ flags that you want to specify for this - * mapping. See <asm/pgtable.h> for more information. - */ -void __iomem * -__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, - unsigned int mtype) +void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, + unsigned long offset, size_t size, unsigned int mtype, void *caller) { const struct mem_type *type; int err; @@ -291,7 +277,7 @@ __arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, */ size = PAGE_ALIGN(offset + size); - area = get_vm_area(size, VM_IOREMAP); + area = get_vm_area_caller(size, VM_IOREMAP, caller); if (!area) return NULL; addr = (unsigned long)area->addr; @@ -318,10 +304,9 @@ __arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, flush_cache_vmap(addr, addr + size); return (void __iomem *) (offset + addr); } -EXPORT_SYMBOL(__arm_ioremap_pfn); -void __iomem * -__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) +void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size, + unsigned int mtype, void *caller) { unsigned long last_addr; unsigned long offset = phys_addr & ~PAGE_MASK; @@ -334,7 +319,33 @@ __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) if (!size || last_addr < phys_addr) return NULL; - return __arm_ioremap_pfn(pfn, offset, size, mtype); + return __arm_ioremap_pfn_caller(pfn, offset, size, mtype, + caller); +} + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ +void __iomem * +__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, + unsigned int mtype) +{ + return __arm_ioremap_pfn_caller(pfn, offset, size, mtype, + __builtin_return_address(0)); +} +EXPORT_SYMBOL(__arm_ioremap_pfn); + +void __iomem * +__arm_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(__arm_ioremap); diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 1708da82da96..88f5d71248d9 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -100,18 +100,17 @@ static struct cachepolicy cache_policies[] __initdata = { * writebuffer to be turned off. (Note: the write * buffer should not be on and the cache off). */ -static void __init early_cachepolicy(char **p) +static int __init early_cachepolicy(char *p) { int i; for (i = 0; i < ARRAY_SIZE(cache_policies); i++) { int len = strlen(cache_policies[i].policy); - if (memcmp(*p, cache_policies[i].policy, len) == 0) { + if (memcmp(p, cache_policies[i].policy, len) == 0) { cachepolicy = i; cr_alignment &= ~cache_policies[i].cr_mask; cr_no_alignment &= ~cache_policies[i].cr_mask; - *p += len; break; } } @@ -130,36 +129,37 @@ static void __init early_cachepolicy(char **p) } flush_cache_all(); set_cr(cr_alignment); + return 0; } -__early_param("cachepolicy=", early_cachepolicy); +early_param("cachepolicy", early_cachepolicy); -static void __init early_nocache(char **__unused) +static int __init early_nocache(char *__unused) { char *p = "buffered"; printk(KERN_WARNING "nocache is deprecated; use cachepolicy=%s\n", p); - early_cachepolicy(&p); + early_cachepolicy(p); + return 0; } -__early_param("nocache", early_nocache); +early_param("nocache", early_nocache); -static void __init early_nowrite(char **__unused) +static int __init early_nowrite(char *__unused) { char *p = "uncached"; printk(KERN_WARNING "nowb is deprecated; use cachepolicy=%s\n", p); - early_cachepolicy(&p); + early_cachepolicy(p); + return 0; } -__early_param("nowb", early_nowrite); +early_param("nowb", early_nowrite); -static void __init early_ecc(char **p) +static int __init early_ecc(char *p) { - if (memcmp(*p, "on", 2) == 0) { + if (memcmp(p, "on", 2) == 0) ecc_mask = PMD_PROTECTION; - *p += 2; - } else if (memcmp(*p, "off", 3) == 0) { + else if (memcmp(p, "off", 3) == 0) ecc_mask = 0; - *p += 3; - } + return 0; } -__early_param("ecc=", early_ecc); +early_param("ecc", early_ecc); static int __init noalign_setup(char *__unused) { @@ -670,9 +670,9 @@ static unsigned long __initdata vmalloc_reserve = SZ_128M; * bytes. This can be used to increase (or decrease) the vmalloc * area - the default is 128m. */ -static void __init early_vmalloc(char **arg) +static int __init early_vmalloc(char *arg) { - vmalloc_reserve = memparse(*arg, arg); + vmalloc_reserve = memparse(arg, NULL); if (vmalloc_reserve < SZ_16M) { vmalloc_reserve = SZ_16M; @@ -687,8 +687,9 @@ static void __init early_vmalloc(char **arg) "vmalloc area is too big, limiting to %luMB\n", vmalloc_reserve >> 20); } + return 0; } -__early_param("vmalloc=", early_vmalloc); +early_param("vmalloc", early_vmalloc); #define VMALLOC_MIN (void *)(VMALLOC_END - vmalloc_reserve) diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 374a8311bc84..9bfeb6b9509a 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -74,6 +74,12 @@ void __iomem *__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, } EXPORT_SYMBOL(__arm_ioremap_pfn); +void __iomem *__arm_ioremap_pfn_caller(unsigned long pfn, unsigned long offset, + size_t size, unsigned int mtype, void *caller) +{ + return __arm_ioremap_pfn(pfn, offset, size, mtype); +} + void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) { @@ -81,6 +87,12 @@ void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size, } EXPORT_SYMBOL(__arm_ioremap); +void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size, + unsigned int mtype, void *caller) +{ + return __arm_ioremap(phys_addr, size, mtype); +} + void __iounmap(volatile void __iomem *addr) { } diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index 8012e24282b2..72507c630ceb 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -265,7 +265,7 @@ ENTRY(arm1020_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm1020_dma_inv_range) +arm1020_dma_inv_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -295,7 +295,7 @@ ENTRY(arm1020_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm1020_dma_clean_range) +arm1020_dma_clean_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -330,6 +330,30 @@ ENTRY(arm1020_dma_flush_range) mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1020_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm1020_dma_clean_range + bcs arm1020_dma_inv_range + b arm1020_dma_flush_range +ENDPROC(arm1020_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1020_dma_unmap_area) + mov pc, lr +ENDPROC(arm1020_dma_unmap_area) + ENTRY(arm1020_cache_fns) .long arm1020_flush_kern_cache_all .long arm1020_flush_user_cache_all @@ -337,8 +361,8 @@ ENTRY(arm1020_cache_fns) .long arm1020_coherent_kern_range .long arm1020_coherent_user_range .long arm1020_flush_kern_dcache_area - .long arm1020_dma_inv_range - .long arm1020_dma_clean_range + .long arm1020_dma_map_area + .long arm1020_dma_unmap_area .long arm1020_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index 41fe25d234f5..d27829805609 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -258,7 +258,7 @@ ENTRY(arm1020e_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm1020e_dma_inv_range) +arm1020e_dma_inv_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -284,7 +284,7 @@ ENTRY(arm1020e_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm1020e_dma_clean_range) +arm1020e_dma_clean_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -316,6 +316,30 @@ ENTRY(arm1020e_dma_flush_range) mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1020e_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm1020e_dma_clean_range + bcs arm1020e_dma_inv_range + b arm1020e_dma_flush_range +ENDPROC(arm1020e_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1020e_dma_unmap_area) + mov pc, lr +ENDPROC(arm1020e_dma_unmap_area) + ENTRY(arm1020e_cache_fns) .long arm1020e_flush_kern_cache_all .long arm1020e_flush_user_cache_all @@ -323,8 +347,8 @@ ENTRY(arm1020e_cache_fns) .long arm1020e_coherent_kern_range .long arm1020e_coherent_user_range .long arm1020e_flush_kern_dcache_area - .long arm1020e_dma_inv_range - .long arm1020e_dma_clean_range + .long arm1020e_dma_map_area + .long arm1020e_dma_unmap_area .long arm1020e_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 20a5b1b31a70..ce13e4a827de 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -247,7 +247,7 @@ ENTRY(arm1022_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm1022_dma_inv_range) +arm1022_dma_inv_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -273,7 +273,7 @@ ENTRY(arm1022_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm1022_dma_clean_range) +arm1022_dma_clean_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -305,6 +305,30 @@ ENTRY(arm1022_dma_flush_range) mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1022_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm1022_dma_clean_range + bcs arm1022_dma_inv_range + b arm1022_dma_flush_range +ENDPROC(arm1022_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1022_dma_unmap_area) + mov pc, lr +ENDPROC(arm1022_dma_unmap_area) + ENTRY(arm1022_cache_fns) .long arm1022_flush_kern_cache_all .long arm1022_flush_user_cache_all @@ -312,8 +336,8 @@ ENTRY(arm1022_cache_fns) .long arm1022_coherent_kern_range .long arm1022_coherent_user_range .long arm1022_flush_kern_dcache_area - .long arm1022_dma_inv_range - .long arm1022_dma_clean_range + .long arm1022_dma_map_area + .long arm1022_dma_unmap_area .long arm1022_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index 96aedb10fcc4..636672a29c6d 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -241,7 +241,7 @@ ENTRY(arm1026_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm1026_dma_inv_range) +arm1026_dma_inv_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -267,7 +267,7 @@ ENTRY(arm1026_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm1026_dma_clean_range) +arm1026_dma_clean_range: mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -299,6 +299,30 @@ ENTRY(arm1026_dma_flush_range) mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1026_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm1026_dma_clean_range + bcs arm1026_dma_inv_range + b arm1026_dma_flush_range +ENDPROC(arm1026_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm1026_dma_unmap_area) + mov pc, lr +ENDPROC(arm1026_dma_unmap_area) + ENTRY(arm1026_cache_fns) .long arm1026_flush_kern_cache_all .long arm1026_flush_user_cache_all @@ -306,8 +330,8 @@ ENTRY(arm1026_cache_fns) .long arm1026_coherent_kern_range .long arm1026_coherent_user_range .long arm1026_flush_kern_dcache_area - .long arm1026_dma_inv_range - .long arm1026_dma_clean_range + .long arm1026_dma_map_area + .long arm1026_dma_unmap_area .long arm1026_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index 471669e2d7cb..8be81992645d 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -239,7 +239,7 @@ ENTRY(arm920_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm920_dma_inv_range) +arm920_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -262,7 +262,7 @@ ENTRY(arm920_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm920_dma_clean_range) +arm920_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -288,6 +288,30 @@ ENTRY(arm920_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm920_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm920_dma_clean_range + bcs arm920_dma_inv_range + b arm920_dma_flush_range +ENDPROC(arm920_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm920_dma_unmap_area) + mov pc, lr +ENDPROC(arm920_dma_unmap_area) + ENTRY(arm920_cache_fns) .long arm920_flush_kern_cache_all .long arm920_flush_user_cache_all @@ -295,8 +319,8 @@ ENTRY(arm920_cache_fns) .long arm920_coherent_kern_range .long arm920_coherent_user_range .long arm920_flush_kern_dcache_area - .long arm920_dma_inv_range - .long arm920_dma_clean_range + .long arm920_dma_map_area + .long arm920_dma_unmap_area .long arm920_dma_flush_range #endif diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index ee111b00fa41..c0ff8e4b1074 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -241,7 +241,7 @@ ENTRY(arm922_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm922_dma_inv_range) +arm922_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -264,7 +264,7 @@ ENTRY(arm922_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm922_dma_clean_range) +arm922_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -290,6 +290,30 @@ ENTRY(arm922_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm922_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm922_dma_clean_range + bcs arm922_dma_inv_range + b arm922_dma_flush_range +ENDPROC(arm922_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm922_dma_unmap_area) + mov pc, lr +ENDPROC(arm922_dma_unmap_area) + ENTRY(arm922_cache_fns) .long arm922_flush_kern_cache_all .long arm922_flush_user_cache_all @@ -297,8 +321,8 @@ ENTRY(arm922_cache_fns) .long arm922_coherent_kern_range .long arm922_coherent_user_range .long arm922_flush_kern_dcache_area - .long arm922_dma_inv_range - .long arm922_dma_clean_range + .long arm922_dma_map_area + .long arm922_dma_unmap_area .long arm922_dma_flush_range #endif diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index 8deb5bde58e4..3c6cffe400f6 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -283,7 +283,7 @@ ENTRY(arm925_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm925_dma_inv_range) +arm925_dma_inv_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -308,7 +308,7 @@ ENTRY(arm925_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm925_dma_clean_range) +arm925_dma_clean_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -341,6 +341,30 @@ ENTRY(arm925_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm925_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm925_dma_clean_range + bcs arm925_dma_inv_range + b arm925_dma_flush_range +ENDPROC(arm925_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm925_dma_unmap_area) + mov pc, lr +ENDPROC(arm925_dma_unmap_area) + ENTRY(arm925_cache_fns) .long arm925_flush_kern_cache_all .long arm925_flush_user_cache_all @@ -348,8 +372,8 @@ ENTRY(arm925_cache_fns) .long arm925_coherent_kern_range .long arm925_coherent_user_range .long arm925_flush_kern_dcache_area - .long arm925_dma_inv_range - .long arm925_dma_clean_range + .long arm925_dma_map_area + .long arm925_dma_unmap_area .long arm925_dma_flush_range ENTRY(cpu_arm925_dcache_clean_area) diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 64db6e275a44..75b707c9cce1 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -246,7 +246,7 @@ ENTRY(arm926_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(arm926_dma_inv_range) +arm926_dma_inv_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -271,7 +271,7 @@ ENTRY(arm926_dma_inv_range) * * (same as v4wb) */ -ENTRY(arm926_dma_clean_range) +arm926_dma_clean_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -304,6 +304,30 @@ ENTRY(arm926_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm926_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm926_dma_clean_range + bcs arm926_dma_inv_range + b arm926_dma_flush_range +ENDPROC(arm926_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm926_dma_unmap_area) + mov pc, lr +ENDPROC(arm926_dma_unmap_area) + ENTRY(arm926_cache_fns) .long arm926_flush_kern_cache_all .long arm926_flush_user_cache_all @@ -311,8 +335,8 @@ ENTRY(arm926_cache_fns) .long arm926_coherent_kern_range .long arm926_coherent_user_range .long arm926_flush_kern_dcache_area - .long arm926_dma_inv_range - .long arm926_dma_clean_range + .long arm926_dma_map_area + .long arm926_dma_unmap_area .long arm926_dma_flush_range ENTRY(cpu_arm926_dcache_clean_area) diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S index 8196b9f401fb..1af1657819eb 100644 --- a/arch/arm/mm/proc-arm940.S +++ b/arch/arm/mm/proc-arm940.S @@ -171,7 +171,7 @@ ENTRY(arm940_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -ENTRY(arm940_dma_inv_range) +arm940_dma_inv_range: mov ip, #0 mov r1, #(CACHE_DSEGMENTS - 1) << 4 @ 4 segments 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries @@ -192,7 +192,7 @@ ENTRY(arm940_dma_inv_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(arm940_dma_clean_range) +arm940_dma_clean_range: ENTRY(cpu_arm940_dcache_clean_area) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH @@ -233,6 +233,30 @@ ENTRY(arm940_dma_flush_range) mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm940_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm940_dma_clean_range + bcs arm940_dma_inv_range + b arm940_dma_flush_range +ENDPROC(arm940_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm940_dma_unmap_area) + mov pc, lr +ENDPROC(arm940_dma_unmap_area) + ENTRY(arm940_cache_fns) .long arm940_flush_kern_cache_all .long arm940_flush_user_cache_all @@ -240,8 +264,8 @@ ENTRY(arm940_cache_fns) .long arm940_coherent_kern_range .long arm940_coherent_user_range .long arm940_flush_kern_dcache_area - .long arm940_dma_inv_range - .long arm940_dma_clean_range + .long arm940_dma_map_area + .long arm940_dma_unmap_area .long arm940_dma_flush_range __INIT diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S index 9a951239c86c..1664b6aaff79 100644 --- a/arch/arm/mm/proc-arm946.S +++ b/arch/arm/mm/proc-arm946.S @@ -215,7 +215,7 @@ ENTRY(arm946_flush_kern_dcache_area) * - end - virtual end address * (same as arm926) */ -ENTRY(arm946_dma_inv_range) +arm946_dma_inv_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -240,7 +240,7 @@ ENTRY(arm946_dma_inv_range) * * (same as arm926) */ -ENTRY(arm946_dma_clean_range) +arm946_dma_clean_range: #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -275,6 +275,30 @@ ENTRY(arm946_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm946_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq arm946_dma_clean_range + bcs arm946_dma_inv_range + b arm946_dma_flush_range +ENDPROC(arm946_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(arm946_dma_unmap_area) + mov pc, lr +ENDPROC(arm946_dma_unmap_area) + ENTRY(arm946_cache_fns) .long arm946_flush_kern_cache_all .long arm946_flush_user_cache_all @@ -282,8 +306,8 @@ ENTRY(arm946_cache_fns) .long arm946_coherent_kern_range .long arm946_coherent_user_range .long arm946_flush_kern_dcache_area - .long arm946_dma_inv_range - .long arm946_dma_clean_range + .long arm946_dma_map_area + .long arm946_dma_unmap_area .long arm946_dma_flush_range diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S index dbc39383e66a..53e632343849 100644 --- a/arch/arm/mm/proc-feroceon.S +++ b/arch/arm/mm/proc-feroceon.S @@ -274,7 +274,7 @@ ENTRY(feroceon_range_flush_kern_dcache_area) * (same as v4wb) */ .align 5 -ENTRY(feroceon_dma_inv_range) +feroceon_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -288,7 +288,7 @@ ENTRY(feroceon_dma_inv_range) mov pc, lr .align 5 -ENTRY(feroceon_range_dma_inv_range) +feroceon_range_dma_inv_range: mrs r2, cpsr tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -314,7 +314,7 @@ ENTRY(feroceon_range_dma_inv_range) * (same as v4wb) */ .align 5 -ENTRY(feroceon_dma_clean_range) +feroceon_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -324,7 +324,7 @@ ENTRY(feroceon_dma_clean_range) mov pc, lr .align 5 -ENTRY(feroceon_range_dma_clean_range) +feroceon_range_dma_clean_range: mrs r2, cpsr cmp r1, r0 subne r1, r1, #1 @ top address is inclusive @@ -367,6 +367,44 @@ ENTRY(feroceon_range_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(feroceon_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq feroceon_dma_clean_range + bcs feroceon_dma_inv_range + b feroceon_dma_flush_range +ENDPROC(feroceon_dma_map_area) + +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(feroceon_range_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq feroceon_range_dma_clean_range + bcs feroceon_range_dma_inv_range + b feroceon_range_dma_flush_range +ENDPROC(feroceon_range_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(feroceon_dma_unmap_area) + mov pc, lr +ENDPROC(feroceon_dma_unmap_area) + ENTRY(feroceon_cache_fns) .long feroceon_flush_kern_cache_all .long feroceon_flush_user_cache_all @@ -374,8 +412,8 @@ ENTRY(feroceon_cache_fns) .long feroceon_coherent_kern_range .long feroceon_coherent_user_range .long feroceon_flush_kern_dcache_area - .long feroceon_dma_inv_range - .long feroceon_dma_clean_range + .long feroceon_dma_map_area + .long feroceon_dma_unmap_area .long feroceon_dma_flush_range ENTRY(feroceon_range_cache_fns) @@ -385,8 +423,8 @@ ENTRY(feroceon_range_cache_fns) .long feroceon_coherent_kern_range .long feroceon_coherent_user_range .long feroceon_range_flush_kern_dcache_area - .long feroceon_range_dma_inv_range - .long feroceon_range_dma_clean_range + .long feroceon_range_dma_map_area + .long feroceon_dma_unmap_area .long feroceon_range_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S index 9674d36cc97d..caa31154e7db 100644 --- a/arch/arm/mm/proc-mohawk.S +++ b/arch/arm/mm/proc-mohawk.S @@ -218,7 +218,7 @@ ENTRY(mohawk_flush_kern_dcache_area) * * (same as v4wb) */ -ENTRY(mohawk_dma_inv_range) +mohawk_dma_inv_range: tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry tst r1, #CACHE_DLINESIZE - 1 @@ -241,7 +241,7 @@ ENTRY(mohawk_dma_inv_range) * * (same as v4wb) */ -ENTRY(mohawk_dma_clean_range) +mohawk_dma_clean_range: bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -268,6 +268,30 @@ ENTRY(mohawk_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(mohawk_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq mohawk_dma_clean_range + bcs mohawk_dma_inv_range + b mohawk_dma_flush_range +ENDPROC(mohawk_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(mohawk_dma_unmap_area) + mov pc, lr +ENDPROC(mohawk_dma_unmap_area) + ENTRY(mohawk_cache_fns) .long mohawk_flush_kern_cache_all .long mohawk_flush_user_cache_all @@ -275,8 +299,8 @@ ENTRY(mohawk_cache_fns) .long mohawk_coherent_kern_range .long mohawk_coherent_user_range .long mohawk_flush_kern_dcache_area - .long mohawk_dma_inv_range - .long mohawk_dma_clean_range + .long mohawk_dma_map_area + .long mohawk_dma_unmap_area .long mohawk_dma_flush_range ENTRY(cpu_mohawk_dcache_clean_area) diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S index 96456f548798..e5797f1c1db7 100644 --- a/arch/arm/mm/proc-xsc3.S +++ b/arch/arm/mm/proc-xsc3.S @@ -257,7 +257,7 @@ ENTRY(xsc3_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -ENTRY(xsc3_dma_inv_range) +xsc3_dma_inv_range: tst r0, #CACHELINESIZE - 1 bic r0, r0, #CACHELINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean L1 D line @@ -278,7 +278,7 @@ ENTRY(xsc3_dma_inv_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(xsc3_dma_clean_range) +xsc3_dma_clean_range: bic r0, r0, #CACHELINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line add r0, r0, #CACHELINESIZE @@ -304,6 +304,30 @@ ENTRY(xsc3_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ data write barrier mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(xsc3_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq xsc3_dma_clean_range + bcs xsc3_dma_inv_range + b xsc3_dma_flush_range +ENDPROC(xsc3_dma_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(xsc3_dma_unmap_area) + mov pc, lr +ENDPROC(xsc3_dma_unmap_area) + ENTRY(xsc3_cache_fns) .long xsc3_flush_kern_cache_all .long xsc3_flush_user_cache_all @@ -311,8 +335,8 @@ ENTRY(xsc3_cache_fns) .long xsc3_coherent_kern_range .long xsc3_coherent_user_range .long xsc3_flush_kern_dcache_area - .long xsc3_dma_inv_range - .long xsc3_dma_clean_range + .long xsc3_dma_map_area + .long xsc3_dma_unmap_area .long xsc3_dma_flush_range ENTRY(cpu_xsc3_dcache_clean_area) @@ -407,6 +431,13 @@ __xsc3_setup: adr r5, xsc3_crval ldmia r5, {r5, r6} + +#ifdef CONFIG_CACHE_XSC3L2 + mrc p15, 1, r0, c0, c0, 1 @ get L2 present information + ands r0, r0, #0xf8 + orrne r6, r6, #(1 << 26) @ enable L2 if present +#endif + mrc p15, 0, r0, c1, c0, 0 @ get control register bic r0, r0, r5 @ ..V. ..R. .... ..A. orr r0, r0, r6 @ ..VI Z..S .... .C.M (mmu) diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 93df47265f2d..63037e2162f2 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -315,7 +315,7 @@ ENTRY(xscale_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -ENTRY(xscale_dma_inv_range) +xscale_dma_inv_range: tst r0, #CACHELINESIZE - 1 bic r0, r0, #CACHELINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -336,7 +336,7 @@ ENTRY(xscale_dma_inv_range) * - start - virtual start address * - end - virtual end address */ -ENTRY(xscale_dma_clean_range) +xscale_dma_clean_range: bic r0, r0, #CACHELINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHELINESIZE @@ -363,6 +363,43 @@ ENTRY(xscale_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer mov pc, lr +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(xscale_dma_map_area) + add r1, r1, r0 + cmp r2, #DMA_TO_DEVICE + beq xscale_dma_clean_range + bcs xscale_dma_inv_range + b xscale_dma_flush_range +ENDPROC(xscale_dma_map_area) + +/* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(xscale_dma_a0_map_area) + add r1, r1, r0 + teq r2, #DMA_TO_DEVICE + beq xscale_dma_clean_range + b xscale_dma_flush_range +ENDPROC(xscsale_dma_a0_map_area) + +/* + * dma_unmap_area(start, size, dir) + * - start - kernel virtual start address + * - size - size of region + * - dir - DMA direction + */ +ENTRY(xscale_dma_unmap_area) + mov pc, lr +ENDPROC(xscale_dma_unmap_area) + ENTRY(xscale_cache_fns) .long xscale_flush_kern_cache_all .long xscale_flush_user_cache_all @@ -370,8 +407,8 @@ ENTRY(xscale_cache_fns) .long xscale_coherent_kern_range .long xscale_coherent_user_range .long xscale_flush_kern_dcache_area - .long xscale_dma_inv_range - .long xscale_dma_clean_range + .long xscale_dma_map_area + .long xscale_dma_unmap_area .long xscale_dma_flush_range /* @@ -394,8 +431,8 @@ ENTRY(xscale_80200_A0_A1_cache_fns) .long xscale_coherent_kern_range .long xscale_coherent_user_range .long xscale_flush_kern_dcache_area - .long xscale_dma_flush_range - .long xscale_dma_clean_range + .long xscale_dma_a0_map_area + .long xscale_dma_unmap_area .long xscale_dma_flush_range ENTRY(cpu_xscale_dcache_clean_area) diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S index a26a605b73bd..0cb1848bd876 100644 --- a/arch/arm/mm/tlb-v7.S +++ b/arch/arm/mm/tlb-v7.S @@ -40,7 +40,6 @@ ENTRY(v7wbi_flush_user_tlb_range) asid r3, r3 @ mask ASID orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA mov r1, r1, lsl #PAGE_SHIFT - vma_vm_flags r2, r2 @ get vma->vm_flags 1: #ifdef CONFIG_SMP mcr p15, 0, r0, c8, c3, 1 @ TLB invalidate U MVA (shareable) diff --git a/arch/arm/plat-iop/io.c b/arch/arm/plat-iop/io.c index ed0bbece0d61..e15bc17db90b 100644 --- a/arch/arm/plat-iop/io.c +++ b/arch/arm/plat-iop/io.c @@ -34,7 +34,8 @@ void * __iomem __iop3xx_ioremap(unsigned long cookie, size_t size, retval = (void *) IOP3XX_PMMR_PHYS_TO_VIRT(cookie); break; default: - retval = __arm_ioremap(cookie, size, mtype); + retval = __arm_ioremap_caller(cookie, size, mtype, + __builtin_return_address(0)); } return retval; diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx25.h b/arch/arm/plat-mxc/include/mach/iomux-mx25.h index 810c47f56e77..9af494f0ab3d 100644 --- a/arch/arm/plat-mxc/include/mach/iomux-mx25.h +++ b/arch/arm/plat-mxc/include/mach/iomux-mx25.h @@ -58,19 +58,19 @@ #define MX25_PAD_A18__A18 IOMUX_PAD(0x23c, 0x020, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A18__GPIO_2_4 IOMUX_PAD(0x23c, 0x020, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A18__FEC_COL IOMUX_PAD(0x23c, 0x020, 0x17, 0x504, 0, NO_PAD_CTL) +#define MX25_PAD_A18__FEC_COL IOMUX_PAD(0x23c, 0x020, 0x17, 0x504, 0, NO_PAD_CTRL) #define MX25_PAD_A19__A19 IOMUX_PAD(0x240, 0x024, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A19__FEC_RX_ER IOMUX_PAD(0x240, 0x024, 0x17, 0x518, 0, NO_PAD_CTL) +#define MX25_PAD_A19__FEC_RX_ER IOMUX_PAD(0x240, 0x024, 0x17, 0x518, 0, NO_PAD_CTRL) #define MX25_PAD_A19__GPIO_2_5 IOMUX_PAD(0x240, 0x024, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A20__A20 IOMUX_PAD(0x244, 0x028, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A20__GPIO_2_6 IOMUX_PAD(0x244, 0x028, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A20__FEC_RDATA2 IOMUX_PAD(0x244, 0x028, 0x17, 0x50c, 0, NO_PAD_CTL) +#define MX25_PAD_A20__FEC_RDATA2 IOMUX_PAD(0x244, 0x028, 0x17, 0x50c, 0, NO_PAD_CTRL) #define MX25_PAD_A21__A21 IOMUX_PAD(0x248, 0x02c, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A21__GPIO_2_7 IOMUX_PAD(0x248, 0x02c, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A21__FEC_RDATA3 IOMUX_PAD(0x248, 0x02c, 0x17, 0x510, 0, NO_PAD_CTL) +#define MX25_PAD_A21__FEC_RDATA3 IOMUX_PAD(0x248, 0x02c, 0x17, 0x510, 0, NO_PAD_CTRL) #define MX25_PAD_A22__A22 IOMUX_PAD(0x000, 0x030, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A22__GPIO_2_8 IOMUX_PAD(0x000, 0x030, 0x15, 0, 0, NO_PAD_CTRL) @@ -80,11 +80,11 @@ #define MX25_PAD_A24__A24 IOMUX_PAD(0x250, 0x038, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A24__GPIO_2_10 IOMUX_PAD(0x250, 0x038, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A24__FEC_RX_CLK IOMUX_PAD(0x250, 0x038, 0x17, 0x514, 0, NO_PAD_CTL) +#define MX25_PAD_A24__FEC_RX_CLK IOMUX_PAD(0x250, 0x038, 0x17, 0x514, 0, NO_PAD_CTRL) #define MX25_PAD_A25__A25 IOMUX_PAD(0x254, 0x03c, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_A25__GPIO_2_11 IOMUX_PAD(0x254, 0x03c, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_A25__FEC_CRS IOMUX_PAD(0x254, 0x03c, 0x17, 0x508, 0, NO_PAD_CTL) +#define MX25_PAD_A25__FEC_CRS IOMUX_PAD(0x254, 0x03c, 0x17, 0x508, 0, NO_PAD_CTRL) #define MX25_PAD_EB0__EB0 IOMUX_PAD(0x258, 0x040, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_EB0__AUD4_TXD IOMUX_PAD(0x258, 0x040, 0x14, 0x464, 0, NO_PAD_CTRL) @@ -112,7 +112,7 @@ #define MX25_PAD_CS5__UART5_RTS IOMUX_PAD(0x268, 0x058, 0x13, 0x574, 0, NO_PAD_CTRL) #define MX25_PAD_CS5__GPIO_3_21 IOMUX_PAD(0x268, 0x058, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_NF_CE0__NF_CE0 IOMUX_PAD(0x26c, 0x05c, 0x10, 0, 0, NO_PAD_CTL) +#define MX25_PAD_NF_CE0__NF_CE0 IOMUX_PAD(0x26c, 0x05c, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_NF_CE0__GPIO_3_22 IOMUX_PAD(0x26c, 0x05c, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_ECB__ECB IOMUX_PAD(0x270, 0x060, 0x10, 0, 0, NO_PAD_CTRL) @@ -229,28 +229,28 @@ #define MX25_PAD_LD7__GPIO_1_21 IOMUX_PAD(0x2dc, 0x0e4, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_LD8__LD8 IOMUX_PAD(0x2e0, 0x0e8, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD8__FEC_TX_ERR IOMUX_PAD(0x2e0, 0x0e8, 0x15, 0, 0, NO_PAD_CTL) +#define MX25_PAD_LD8__FEC_TX_ERR IOMUX_PAD(0x2e0, 0x0e8, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_LD9__LD9 IOMUX_PAD(0x2e4, 0x0ec, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD9__FEC_COL IOMUX_PAD(0x2e4, 0x0ec, 0x15, 0x504, 1, NO_PAD_CTL) +#define MX25_PAD_LD9__FEC_COL IOMUX_PAD(0x2e4, 0x0ec, 0x15, 0x504, 1, NO_PAD_CTRL) #define MX25_PAD_LD10__LD10 IOMUX_PAD(0x2e8, 0x0f0, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD10__FEC_RX_ER IOMUX_PAD(0x2e8, 0x0f0, 0x15, 0x518, 1, NO_PAD_CTL) +#define MX25_PAD_LD10__FEC_RX_ER IOMUX_PAD(0x2e8, 0x0f0, 0x15, 0x518, 1, NO_PAD_CTRL) #define MX25_PAD_LD11__LD11 IOMUX_PAD(0x2ec, 0x0f4, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD11__FEC_RDATA2 IOMUX_PAD(0x2ec, 0x0f4, 0x15, 0x50c, 1, NO_PAD_CTL) +#define MX25_PAD_LD11__FEC_RDATA2 IOMUX_PAD(0x2ec, 0x0f4, 0x15, 0x50c, 1, NO_PAD_CTRL) #define MX25_PAD_LD12__LD12 IOMUX_PAD(0x2f0, 0x0f8, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD12__FEC_RDATA3 IOMUX_PAD(0x2f0, 0x0f8, 0x15, 0x510, 1, NO_PAD_CTL) +#define MX25_PAD_LD12__FEC_RDATA3 IOMUX_PAD(0x2f0, 0x0f8, 0x15, 0x510, 1, NO_PAD_CTRL) #define MX25_PAD_LD13__LD13 IOMUX_PAD(0x2f4, 0x0fc, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD13__FEC_TDATA2 IOMUX_PAD(0x2f4, 0x0fc, 0x15, 0, 0, NO_PAD_CTL) +#define MX25_PAD_LD13__FEC_TDATA2 IOMUX_PAD(0x2f4, 0x0fc, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_LD14__LD14 IOMUX_PAD(0x2f8, 0x100, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD14__FEC_TDATA3 IOMUX_PAD(0x2f8, 0x100, 0x15, 0, 0, NO_PAD_CTL) +#define MX25_PAD_LD14__FEC_TDATA3 IOMUX_PAD(0x2f8, 0x100, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_LD15__LD15 IOMUX_PAD(0x2fc, 0x104, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_LD15__FEC_RX_CLK IOMUX_PAD(0x2fc, 0x104, 0x15, 0x514, 1, NO_PAD_CTL) +#define MX25_PAD_LD15__FEC_RX_CLK IOMUX_PAD(0x2fc, 0x104, 0x15, 0x514, 1, NO_PAD_CTRL) #define MX25_PAD_HSYNC__HSYNC IOMUX_PAD(0x300, 0x108, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_HSYNC__GPIO_1_22 IOMUX_PAD(0x300, 0x108, 0x15, 0, 0, NO_PAD_CTRL) @@ -265,7 +265,7 @@ #define MX25_PAD_OE_ACD__GPIO_1_25 IOMUX_PAD(0x30c, 0x114, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_CONTRAST__CONTRAST IOMUX_PAD(0x310, 0x118, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_CONTRAST__FEC_CRS IOMUX_PAD(0x310, 0x118, 0x15, 0x508, 1, NO_PAD_CTL) +#define MX25_PAD_CONTRAST__FEC_CRS IOMUX_PAD(0x310, 0x118, 0x15, 0x508, 1, NO_PAD_CTRL) #define MX25_PAD_PWM__PWM IOMUX_PAD(0x314, 0x11c, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_PWM__GPIO_1_26 IOMUX_PAD(0x314, 0x11c, 0x15, 0, 0, NO_PAD_CTRL) @@ -354,19 +354,19 @@ #define MX25_PAD_UART2_TXD__GPIO_4_27 IOMUX_PAD(0x37c, 0x184, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_UART2_RTS__UART2_RTS IOMUX_PAD(0x380, 0x188, 0x10, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_UART2_RTS__FEC_COL IOMUX_PAD(0x380, 0x188, 0x12, 0x504, 2, NO_PAD_CTL) +#define MX25_PAD_UART2_RTS__FEC_COL IOMUX_PAD(0x380, 0x188, 0x12, 0x504, 2, NO_PAD_CTRL) #define MX25_PAD_UART2_RTS__GPIO_4_28 IOMUX_PAD(0x380, 0x188, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_UART2_CTS__FEC_RX_ER IOMUX_PAD(0x384, 0x18c, 0x12, 0x518, 2, NO_PAD_CTL) +#define MX25_PAD_UART2_CTS__FEC_RX_ER IOMUX_PAD(0x384, 0x18c, 0x12, 0x518, 2, NO_PAD_CTRL) #define MX25_PAD_UART2_CTS__UART2_CTS IOMUX_PAD(0x384, 0x18c, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_UART2_CTS__GPIO_4_29 IOMUX_PAD(0x384, 0x18c, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_SD1_CMD__SD1_CMD IOMUX_PAD(0x388, 0x190, 0x10, 0, 0, PAD_CTL_PUS_47K_UP) -#define MX25_PAD_SD1_CMD__FEC_RDATA2 IOMUX_PAD(0x388, 0x190, 0x12, 0x50c, 2, NO_PAD_CTL) +#define MX25_PAD_SD1_CMD__FEC_RDATA2 IOMUX_PAD(0x388, 0x190, 0x12, 0x50c, 2, NO_PAD_CTRL) #define MX25_PAD_SD1_CMD__GPIO_2_23 IOMUX_PAD(0x388, 0x190, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_SD1_CLK__SD1_CLK IOMUX_PAD(0x38c, 0x194, 0x10, 0, 0, PAD_CTL_PUS_47K_UP) -#define MX25_PAD_SD1_CLK__FEC_RDATA3 IOMUX_PAD(0x38c, 0x194, 0x12, 0x510, 2, NO_PAD_CTL) +#define MX25_PAD_SD1_CLK__FEC_RDATA3 IOMUX_PAD(0x38c, 0x194, 0x12, 0x510, 2, NO_PAD_CTRL) #define MX25_PAD_SD1_CLK__GPIO_2_24 IOMUX_PAD(0x38c, 0x194, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_SD1_DATA0__SD1_DATA0 IOMUX_PAD(0x390, 0x198, 0x10, 0, 0, PAD_CTL_PUS_47K_UP) @@ -377,11 +377,11 @@ #define MX25_PAD_SD1_DATA1__GPIO_2_26 IOMUX_PAD(0x394, 0x19c, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_SD1_DATA2__SD1_DATA2 IOMUX_PAD(0x398, 0x1a0, 0x10, 0, 0, PAD_CTL_PUS_47K_UP) -#define MX25_PAD_SD1_DATA2__FEC_RX_CLK IOMUX_PAD(0x398, 0x1a0, 0x15, 0x514, 2, NO_PAD_CTL) +#define MX25_PAD_SD1_DATA2__FEC_RX_CLK IOMUX_PAD(0x398, 0x1a0, 0x15, 0x514, 2, NO_PAD_CTRL) #define MX25_PAD_SD1_DATA2__GPIO_2_27 IOMUX_PAD(0x398, 0x1a0, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_SD1_DATA3__SD1_DATA3 IOMUX_PAD(0x39c, 0x1a4, 0x10, 0, 0, PAD_CTL_PUS_47K_UP) -#define MX25_PAD_SD1_DATA3__FEC_CRS IOMUX_PAD(0x39c, 0x1a4, 0x10, 0x508, 2, NO_PAD_CTL) +#define MX25_PAD_SD1_DATA3__FEC_CRS IOMUX_PAD(0x39c, 0x1a4, 0x10, 0x508, 2, NO_PAD_CTRL) #define MX25_PAD_SD1_DATA3__GPIO_2_28 IOMUX_PAD(0x39c, 0x1a4, 0x15, 0, 0, NO_PAD_CTRL) #define MX25_PAD_KPP_ROW0__KPP_ROW0 IOMUX_PAD(0x3a0, 0x1a8, 0x10, 0, 0, PAD_CTL_PKE) @@ -410,7 +410,7 @@ #define MX25_PAD_KPP_COL3__KPP_COL3 IOMUX_PAD(0x3bc, 0x1c4, 0x10, 0, 0, PAD_CTL_PKE | PAD_CTL_ODE) #define MX25_PAD_KPP_COL3__GPIO_3_4 IOMUX_PAD(0x3bc, 0x1c4, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_MDC__FEC_MDC IOMUX_PAD(0x3c0, 0x1c8, 0x10, 0, 0, NO_PAD_CTL) +#define MX25_PAD_FEC_MDC__FEC_MDC IOMUX_PAD(0x3c0, 0x1c8, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_FEC_MDC__AUD4_TXD IOMUX_PAD(0x3c0, 0x1c8, 0x12, 0x464, 1, NO_PAD_CTRL) #define MX25_PAD_FEC_MDC__GPIO_3_5 IOMUX_PAD(0x3c0, 0x1c8, 0x15, 0, 0, NO_PAD_CTRL) @@ -418,23 +418,23 @@ #define MX25_PAD_FEC_MDIO__AUD4_RXD IOMUX_PAD(0x3c4, 0x1cc, 0x12, 0x460, 1, NO_PAD_CTRL) #define MX25_PAD_FEC_MDIO__GPIO_3_6 IOMUX_PAD(0x3c4, 0x1cc, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_TDATA0__FEC_TDATA0 IOMUX_PAD(0x3c8, 0x1d0, 0x10, 0, 0, NO_PAD_CTL) +#define MX25_PAD_FEC_TDATA0__FEC_TDATA0 IOMUX_PAD(0x3c8, 0x1d0, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_FEC_TDATA0__GPIO_3_7 IOMUX_PAD(0x3c8, 0x1d0, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_TDATA1__FEC_TDATA1 IOMUX_PAD(0x3cc, 0x1d4, 0x10, 0, 0, NO_PAD_CTL) +#define MX25_PAD_FEC_TDATA1__FEC_TDATA1 IOMUX_PAD(0x3cc, 0x1d4, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_FEC_TDATA1__AUD4_TXFS IOMUX_PAD(0x3cc, 0x1d4, 0x12, 0x474, 1, NO_PAD_CTRL) #define MX25_PAD_FEC_TDATA1__GPIO_3_8 IOMUX_PAD(0x3cc, 0x1d4, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_TX_EN__FEC_TX_EN IOMUX_PAD(0x3d0, 0x1d8, 0x10, 0, 0, NO_PAD_CTL) +#define MX25_PAD_FEC_TX_EN__FEC_TX_EN IOMUX_PAD(0x3d0, 0x1d8, 0x10, 0, 0, NO_PAD_CTRL) #define MX25_PAD_FEC_TX_EN__GPIO_3_9 IOMUX_PAD(0x3d0, 0x1d8, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_RDATA0__FEC_RDATA0 IOMUX_PAD(0x3d4, 0x1dc, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL) +#define MX25_PAD_FEC_RDATA0__FEC_RDATA0 IOMUX_PAD(0x3d4, 0x1dc, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTRL) #define MX25_PAD_FEC_RDATA0__GPIO_3_10 IOMUX_PAD(0x3d4, 0x1dc, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_RDATA1__FEC_RDATA1 IOMUX_PAD(0x3d8, 0x1e0, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL) +#define MX25_PAD_FEC_RDATA1__FEC_RDATA1 IOMUX_PAD(0x3d8, 0x1e0, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTRL) #define MX25_PAD_FEC_RDATA1__GPIO_3_11 IOMUX_PAD(0x3d8, 0x1e0, 0x15, 0, 0, NO_PAD_CTRL) -#define MX25_PAD_FEC_RX_DV__FEC_RX_DV IOMUX_PAD(0x3dc, 0x1e4, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL) +#define MX25_PAD_FEC_RX_DV__FEC_RX_DV IOMUX_PAD(0x3dc, 0x1e4, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTRL) #define MX25_PAD_FEC_RX_DV__CAN2_RX IOMUX_PAD(0x3dc, 0x1e4, 0x14, 0x484, 0, PAD_CTL_PUS_22K_UP) #define MX25_PAD_FEC_RX_DV__GPIO_3_12 IOMUX_PAD(0x3dc, 0x1e4, 0x15, 0, 0, NO_PAD_CTRL) diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h index 91e738144804..854e2dc58481 100644 --- a/arch/arm/plat-mxc/include/mach/mx25.h +++ b/arch/arm/plat-mxc/include/mach/mx25.h @@ -41,4 +41,8 @@ #define UART1_BASE_ADDR 0x43f90000 #define UART2_BASE_ADDR 0x43f94000 +#define MX25_FEC_BASE_ADDR 0x50038000 + +#define MX25_INT_FEC 57 + #endif /* __MACH_MX25_H__ */ diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 89cafc937249..d9f8c844c385 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -36,10 +36,6 @@ static struct clk_functions *arch_clock; * Standard clock functions defined in include/linux/clk.h *-------------------------------------------------------------------------*/ -/* This functions is moved to arch/arm/common/clkdev.c. For OMAP4 since - * clock framework is not up , it is defined here to avoid rework in - * every driver. Also dummy prcm reset function is added */ - int clk_enable(struct clk *clk) { unsigned long flags; @@ -305,7 +301,6 @@ void clk_enable_init_clocks(void) clk_enable(clkp); } } -EXPORT_SYMBOL(clk_enable_init_clocks); /* * Low level helpers @@ -334,7 +329,16 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table) arch_clock->clk_init_cpufreq_table(table); spin_unlock_irqrestore(&clockfw_lock, flags); } -EXPORT_SYMBOL(clk_init_cpufreq_table); + +void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table) +{ + unsigned long flags; + + spin_lock_irqsave(&clockfw_lock, flags); + if (arch_clock->clk_exit_cpufreq_table) + arch_clock->clk_exit_cpufreq_table(table); + spin_unlock_irqrestore(&clockfw_lock, flags); +} #endif /*-------------------------------------------------------------------------*/ diff --git a/arch/arm/plat-omap/cpu-omap.c b/arch/arm/plat-omap/cpu-omap.c index f8ddbdd8b076..6d3d33360056 100644 --- a/arch/arm/plat-omap/cpu-omap.c +++ b/arch/arm/plat-omap/cpu-omap.c @@ -134,6 +134,7 @@ static int __init omap_cpu_init(struct cpufreq_policy *policy) static int omap_cpu_exit(struct cpufreq_policy *policy) { + clk_exit_cpufreq_table(&freq_table); clk_put(mpu_clk); return 0; } diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index 09d82b3c66ce..728c64204184 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -1183,7 +1183,7 @@ void omap_dma_unlink_lch(int lch_head, int lch_queue) } if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || - (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) { + (dma_chan[lch_queue].flags & OMAP_DMA_ACTIVE)) { printk(KERN_ERR "omap_dma: You need to stop the DMA channels " "before unlinking\n"); dump_stack(); diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index 04846811d0aa..d17620c50c28 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -192,6 +192,7 @@ struct gpio_bank { u32 saved_risingdetect; #endif u32 level_mask; + u32 toggle_mask; spinlock_t lock; struct gpio_chip chip; struct clk *dbck; @@ -749,6 +750,44 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, } #endif +/* + * This only applies to chips that can't do both rising and falling edge + * detection at once. For all other chips, this function is a noop. + */ +static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + break; +#endif + default: + return; + } + + l = __raw_readl(reg); + if ((l >> gpio) & 1) + l &= ~(1 << gpio); + else + l |= 1 << gpio; + + __raw_writel(l, reg); +} + static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) { void __iomem *reg = bank->base; @@ -759,6 +798,8 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) case METHOD_MPUIO: reg += OMAP_MPUIO_GPIO_INT_EDGE; l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) @@ -771,6 +812,8 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) case METHOD_GPIO_1510: reg += OMAP1510_GPIO_INT_CONTROL; l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) @@ -803,6 +846,8 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) case METHOD_GPIO_7XX: reg += OMAP7XX_GPIO_INT_CONTROL; l = __raw_readl(reg); + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) @@ -1072,7 +1117,7 @@ static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int ena */ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) { - unsigned long flags; + unsigned long uninitialized_var(flags); switch (bank->method) { #ifdef CONFIG_ARCH_OMAP16XX @@ -1217,7 +1262,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { void __iomem *isr_reg = NULL; u32 isr; - unsigned int gpio_irq; + unsigned int gpio_irq, gpio_index; struct gpio_bank *bank; u32 retrigger = 0; int unmasked = 0; @@ -1284,9 +1329,23 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) gpio_irq = bank->virtual_irq_start; for (; isr != 0; isr >>= 1, gpio_irq++) { + gpio_index = get_gpio_index(irq_to_gpio(gpio_irq)); + if (!(isr & 1)) continue; +#ifdef CONFIG_ARCH_OMAP1 + /* + * Some chips can't respond to both rising and falling + * at the same time. If this irq was requested with + * both flags, we need to flip the ICR data for the IRQ + * to respond to the IRQ for the opposite direction. + * This will be indicated in the bank toggle_mask. + */ + if (bank->toggle_mask & (1 << gpio_index)) + _toggle_gpio_edge_triggering(bank, gpio_index); +#endif + generic_handle_irq(gpio_irq); } } diff --git a/arch/arm/plat-omap/include/plat/board.h b/arch/arm/plat-omap/include/plat/board.h index 376ce18216ff..5cd622039da0 100644 --- a/arch/arm/plat-omap/include/plat/board.h +++ b/arch/arm/plat-omap/include/plat/board.h @@ -99,7 +99,6 @@ struct fb_info; struct omap_backlight_config { int default_intensity; int (*set_power)(struct device *dev, int state); - int (*check_fb)(struct fb_info *fb); }; struct omap_fbmem_config { diff --git a/arch/arm/plat-omap/include/plat/clock.h b/arch/arm/plat-omap/include/plat/clock.h index 309b6d1dccdb..94fe2a0ce40a 100644 --- a/arch/arm/plat-omap/include/plat/clock.h +++ b/arch/arm/plat-omap/include/plat/clock.h @@ -119,6 +119,7 @@ struct clk_functions { void (*clk_disable_unused)(struct clk *clk); #ifdef CONFIG_CPU_FREQ void (*clk_init_cpufreq_table)(struct cpufreq_frequency_table **); + void (*clk_exit_cpufreq_table)(struct cpufreq_frequency_table **); #endif }; @@ -135,6 +136,7 @@ extern unsigned long followparent_recalc(struct clk *clk); extern void clk_enable_init_clocks(void); #ifdef CONFIG_CPU_FREQ extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table); +extern void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table); #endif extern const struct clkops clkops_null; diff --git a/arch/arm/plat-omap/include/plat/control.h b/arch/arm/plat-omap/include/plat/control.h index 2ae884378638..a745d62fad0d 100644 --- a/arch/arm/plat-omap/include/plat/control.h +++ b/arch/arm/plat-omap/include/plat/control.h @@ -147,7 +147,7 @@ #define OMAP343X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190) #define OMAP343X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194) #define OMAP343X_CONTROL_DEBOBS(i) (OMAP2_CONTROL_GENERAL + 0x01B0 \ - + ((i) >> 1) * 4 + (!(i) & 1) * 2) + + ((i) >> 1) * 4 + (!((i) & 1)) * 2) #define OMAP343X_CONTROL_PROG_IO0 (OMAP2_CONTROL_GENERAL + 0x01D4) #define OMAP343X_CONTROL_PROG_IO1 (OMAP2_CONTROL_GENERAL + 0x01D8) #define OMAP343X_CONTROL_DSS_DPLL_SPREADING (OMAP2_CONTROL_GENERAL + 0x01E0) diff --git a/arch/arm/plat-omap/include/plat/flash.h b/arch/arm/plat-omap/include/plat/flash.h new file mode 100644 index 000000000000..3e6327016b40 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/flash.h @@ -0,0 +1,16 @@ +/* + * Flash support for OMAP1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __OMAP_FLASH_H +#define __OMAP_FLASH_H + +#include <linux/mtd/map.h> + +extern void omap1_set_vpp(struct map_info *map, int enable); + +#endif diff --git a/arch/arm/plat-omap/include/plat/io.h b/arch/arm/plat-omap/include/plat/io.h index 7e5319f907d1..a3e7b471bcba 100644 --- a/arch/arm/plat-omap/include/plat/io.h +++ b/arch/arm/plat-omap/include/plat/io.h @@ -122,16 +122,21 @@ #define OMAP243X_SMS_VIRT (OMAP243X_SMS_PHYS + OMAP2_L3_IO_OFFSET) #define OMAP243X_SMS_SIZE SZ_1M -/* DSP */ -#define DSP_MEM_24XX_PHYS OMAP2420_DSP_MEM_BASE /* 0x58000000 */ -#define DSP_MEM_24XX_VIRT 0xe0000000 -#define DSP_MEM_24XX_SIZE 0x28000 -#define DSP_IPI_24XX_PHYS OMAP2420_DSP_IPI_BASE /* 0x59000000 */ -#define DSP_IPI_24XX_VIRT 0xe1000000 -#define DSP_IPI_24XX_SIZE SZ_4K -#define DSP_MMU_24XX_PHYS OMAP2420_DSP_MMU_BASE /* 0x5a000000 */ -#define DSP_MMU_24XX_VIRT 0xe2000000 -#define DSP_MMU_24XX_SIZE SZ_4K +/* 2420 IVA */ +#define DSP_MEM_2420_PHYS OMAP2420_DSP_MEM_BASE + /* 0x58000000 --> 0xfc100000 */ +#define DSP_MEM_2420_VIRT 0xfc100000 +#define DSP_MEM_2420_SIZE 0x28000 +#define DSP_IPI_2420_PHYS OMAP2420_DSP_IPI_BASE + /* 0x59000000 --> 0xfc128000 */ +#define DSP_IPI_2420_VIRT 0xfc128000 +#define DSP_IPI_2420_SIZE SZ_4K +#define DSP_MMU_2420_PHYS OMAP2420_DSP_MMU_BASE + /* 0x5a000000 --> 0xfc129000 */ +#define DSP_MMU_2420_VIRT 0xfc129000 +#define DSP_MMU_2420_SIZE SZ_4K + +/* 2430 IVA2.1 - currently unmapped */ /* * ---------------------------------------------------------------------------- @@ -182,16 +187,7 @@ #define OMAP343X_SDRC_VIRT (OMAP343X_SDRC_PHYS + OMAP2_L3_IO_OFFSET) #define OMAP343X_SDRC_SIZE SZ_1M -/* DSP */ -#define DSP_MEM_34XX_PHYS OMAP34XX_DSP_MEM_BASE /* 0x58000000 */ -#define DSP_MEM_34XX_VIRT 0xe0000000 -#define DSP_MEM_34XX_SIZE 0x28000 -#define DSP_IPI_34XX_PHYS OMAP34XX_DSP_IPI_BASE /* 0x59000000 */ -#define DSP_IPI_34XX_VIRT 0xe1000000 -#define DSP_IPI_34XX_SIZE SZ_4K -#define DSP_MMU_34XX_PHYS OMAP34XX_DSP_MMU_BASE /* 0x5a000000 */ -#define DSP_MMU_34XX_VIRT 0xe2000000 -#define DSP_MMU_34XX_SIZE SZ_4K +/* 3430 IVA - currently unmapped */ /* * ---------------------------------------------------------------------------- diff --git a/arch/arm/plat-omap/include/plat/mux.h b/arch/arm/plat-omap/include/plat/mux.h index 8f069cc80350..692c90e89ac3 100644 --- a/arch/arm/plat-omap/include/plat/mux.h +++ b/arch/arm/plat-omap/include/plat/mux.h @@ -183,6 +183,14 @@ enum omap7xx_index { /* I2C */ I2C_7XX_SCL, I2C_7XX_SDA, + + /* SPI */ + SPI_7XX_1, + SPI_7XX_2, + SPI_7XX_3, + SPI_7XX_4, + SPI_7XX_5, + SPI_7XX_6, }; enum omap1xxx_index { diff --git a/arch/arm/plat-omap/include/plat/omap7xx.h b/arch/arm/plat-omap/include/plat/omap7xx.h index 53f52414b0e9..48e4757e1e30 100644 --- a/arch/arm/plat-omap/include/plat/omap7xx.h +++ b/arch/arm/plat-omap/include/plat/omap7xx.h @@ -46,6 +46,9 @@ #define OMAP7XX_DSPREG_SIZE SZ_128K #define OMAP7XX_DSPREG_START 0xE1000000 +#define OMAP7XX_SPI1_BASE 0xfffc0800 +#define OMAP7XX_SPI2_BASE 0xfffc1000 + /* * ---------------------------------------------------------------------------- * OMAP7XX specific configuration registers diff --git a/arch/arm/plat-omap/io.c b/arch/arm/plat-omap/io.c index 11f5d7961c73..4cbd4fb3232c 100644 --- a/arch/arm/plat-omap/io.c +++ b/arch/arm/plat-omap/io.c @@ -66,12 +66,12 @@ void __iomem *omap_ioremap(unsigned long p, size_t size, unsigned int type) return XLATE(p, L4_24XX_PHYS, L4_24XX_VIRT); } if (cpu_is_omap2420()) { - if (BETWEEN(p, DSP_MEM_24XX_PHYS, DSP_MEM_24XX_SIZE)) - return XLATE(p, DSP_MEM_24XX_PHYS, DSP_MEM_24XX_VIRT); - if (BETWEEN(p, DSP_IPI_24XX_PHYS, DSP_IPI_24XX_SIZE)) - return XLATE(p, DSP_IPI_24XX_PHYS, DSP_IPI_24XX_SIZE); - if (BETWEEN(p, DSP_MMU_24XX_PHYS, DSP_MMU_24XX_SIZE)) - return XLATE(p, DSP_MMU_24XX_PHYS, DSP_MMU_24XX_VIRT); + if (BETWEEN(p, DSP_MEM_2420_PHYS, DSP_MEM_2420_SIZE)) + return XLATE(p, DSP_MEM_2420_PHYS, DSP_MEM_2420_VIRT); + if (BETWEEN(p, DSP_IPI_2420_PHYS, DSP_IPI_2420_SIZE)) + return XLATE(p, DSP_IPI_2420_PHYS, DSP_IPI_2420_SIZE); + if (BETWEEN(p, DSP_MMU_2420_PHYS, DSP_MMU_2420_SIZE)) + return XLATE(p, DSP_MMU_2420_PHYS, DSP_MMU_2420_VIRT); } if (cpu_is_omap2430()) { if (BETWEEN(p, L4_WK_243X_PHYS, L4_WK_243X_SIZE)) @@ -128,7 +128,7 @@ void __iomem *omap_ioremap(unsigned long p, size_t size, unsigned int type) return XLATE(p, L4_EMU_44XX_PHYS, L4_EMU_44XX_VIRT); } #endif - return __arm_ioremap(p, size, type); + return __arm_ioremap_caller(p, size, type, __builtin_return_address(0)); } EXPORT_SYMBOL(omap_ioremap); diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c index c0ff1e39d893..463d6386aff2 100644 --- a/arch/arm/plat-omap/iommu.c +++ b/arch/arm/plat-omap/iommu.c @@ -827,7 +827,7 @@ EXPORT_SYMBOL_GPL(iommu_get); **/ void iommu_put(struct iommu *obj) { - if (!obj && IS_ERR(obj)) + if (!obj || IS_ERR(obj)) return; mutex_lock(&obj->iommu_lock); diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index 2cc1cc328bac..f75767278fc3 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c @@ -436,7 +436,7 @@ int omap_mcbsp_request(unsigned int id) dev_err(mcbsp->dev, "Unable to request TX IRQ %d " "for McBSP%d\n", mcbsp->tx_irq, mcbsp->id); - return err; + goto error; } init_completion(&mcbsp->rx_irq_completion); @@ -446,12 +446,26 @@ int omap_mcbsp_request(unsigned int id) dev_err(mcbsp->dev, "Unable to request RX IRQ %d " "for McBSP%d\n", mcbsp->rx_irq, mcbsp->id); - free_irq(mcbsp->tx_irq, (void *)mcbsp); - return err; + goto tx_irq; } } return 0; +tx_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +error: + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(id); + + /* Do procedure specific to omap34xx arch, if applicable */ + omap34xx_mcbsp_free(mcbsp); + + clk_disable(mcbsp->fclk); + clk_disable(mcbsp->iclk); + + mcbsp->free = 1; + + return err; } EXPORT_SYMBOL(omap_mcbsp_request); diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c index a9eabdcfa163..51dc5c8106c0 100644 --- a/arch/arm/plat-pxa/pwm.c +++ b/arch/arm/plat-pxa/pwm.c @@ -204,14 +204,14 @@ static int __devinit pwm_probe(struct platform_device *pdev) goto err_free_clk; } - r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); + r = request_mem_region(r->start, resource_size(r), pdev->name); if (r == NULL) { dev_err(&pdev->dev, "failed to request memory resource\n"); ret = -EBUSY; goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); + pwm->mmio_base = ioremap(r->start, resource_size(r)); if (pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; @@ -241,7 +241,7 @@ static int __devinit pwm_probe(struct platform_device *pdev) return 0; err_free_mem: - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); err_free_clk: clk_put(pwm->clk); err_free: @@ -271,7 +271,7 @@ static int __devexit pwm_remove(struct platform_device *pdev) iounmap(pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); clk_put(pwm->clk); kfree(pwm); diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 9e9d0286e48f..454cc39b7adc 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -130,39 +130,6 @@ config S3C_GPIO_TRACK Internal configuration option to enable the s3c specific gpio chip tracking if the platform requires it. -config S3C_GPIO_PULL_UPDOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_DOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_UP - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_CFG_S3C24XX - bool - help - Internal configuration to enable S3C24XX style GPIO configuration - functions. - -config S3C_GPIO_CFG_S3C64XX - bool - help - Internal configuration to enable S3C64XX style GPIO configuration - functions. - -config S5P_GPIO_CFG_S5PC1XX - bool - help - Internal configuration to enable S5PC1XX style GPIO configuration - functions. - # DMA config S3C_DMA @@ -170,46 +137,4 @@ config S3C_DMA help Internal configuration for S3C DMA core -# device definitions to compile in - -config S3C_DEV_HSMMC - bool - help - Compile in platform device definitions for HSMMC code - -config S3C_DEV_HSMMC1 - bool - help - Compile in platform device definitions for HSMMC channel 1 - -config S3C_DEV_HSMMC2 - bool - help - Compile in platform device definitions for HSMMC channel 2 - -config S3C_DEV_I2C1 - bool - help - Compile in platform device definitions for I2C channel 1 - -config S3C_DEV_FB - bool - help - Compile in platform device definition for framebuffer - -config S3C_DEV_USB_HOST - bool - help - Compile in platform device definition for USB host. - -config S3C_DEV_USB_HSOTG - bool - help - Compile in platform device definition for USB high-speed OtG - -config S3C_DEV_NAND - bool - help - Compile in platform device definition for NAND controller - endif diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 50444da98425..ea4a001f6793 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -11,12 +11,9 @@ obj- := # Core support for all Samsung SoCs -obj-y += init.o +obj-y += init.o obj-y += time.o -obj-y += clock.o -obj-y += pwm-clock.o obj-y += gpio.o -obj-y += gpio-config.o # DMA support @@ -31,15 +28,3 @@ obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o # PWM support obj-$(CONFIG_HAVE_PWM) += pwm.o - -# devices - -obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o -obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o -obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o -obj-y += dev-i2c0.o -obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o -obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o -obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o -obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o -obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 226147b7e026..b64115fa93a4 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -3,7 +3,7 @@ * Copyright (c) 2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C2410 - NAND device controller platfrom_device info + * S3C2410 - NAND device controller platform_device info * * 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 diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c index ac061a1bcb37..cf97caafe56b 100644 --- a/arch/arm/plat-s3c24xx/clock-dclk.c +++ b/arch/arm/plat-s3c24xx/clock-dclk.c @@ -161,14 +161,18 @@ static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) /* external clock definitions */ +static struct clk_ops dclk_ops = { + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + struct clk s3c24xx_dclk0 = { .name = "dclk0", .id = -1, .ctrlbit = S3C2410_DCLKCON_DCLK0EN, .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, + .ops = &dclk_ops, }; struct clk s3c24xx_dclk1 = { @@ -176,19 +180,21 @@ struct clk s3c24xx_dclk1 = { .id = -1, .ctrlbit = S3C2410_DCLKCON_DCLK1EN, .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, + .ops = &dclk_ops, +}; + +static struct clk_ops clkout_ops = { + .set_parent = s3c24xx_clkout_setparent, }; struct clk s3c24xx_clkout0 = { .name = "clkout0", .id = -1, - .set_parent = s3c24xx_clkout_setparent, + .ops = &clkout_ops, }; struct clk s3c24xx_clkout1 = { .name = "clkout1", .id = -1, - .set_parent = s3c24xx_clkout_setparent, + .ops = &clkout_ops, }; diff --git a/arch/arm/plat-s3c24xx/s3c244x-clock.c b/arch/arm/plat-s3c24xx/s3c244x-clock.c index 79371091aa38..f8d96130d1d1 100644 --- a/arch/arm/plat-s3c24xx/s3c244x-clock.c +++ b/arch/arm/plat-s3c24xx/s3c244x-clock.c @@ -68,7 +68,9 @@ static int s3c2440_setparent_armclk(struct clk *clk, struct clk *parent) static struct clk clk_arm = { .name = "armclk", .id = -1, - .set_parent = s3c2440_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2440_setparent_armclk, + }, }; static int s3c244x_clk_add(struct sys_device *sysdev) diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index e6da87a5885c..0fba1f956b8a 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -13,6 +13,9 @@ config PLAT_S3C64XX select ARM_VIC select NO_IOPORT select ARCH_REQUIRE_GPIOLIB + select SAMSUNG_CLKSRC + select SAMSUNG_IRQ_VIC_TIMER + select SAMSUNG_IRQ_UART select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c index 7a36e899360d..ae5883c00e7a 100644 --- a/arch/arm/plat-s3c64xx/clock.c +++ b/arch/arm/plat-s3c64xx/clock.c @@ -274,15 +274,7 @@ void __init s3c64xx_register_clocks(void) int ptr; s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); - - clkp = init_clocks; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); clkp = init_clocks_disable; for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c index 49796d2db86d..c0e6f2a45154 100644 --- a/arch/arm/plat-s3c64xx/cpu.c +++ b/arch/arm/plat-s3c64xx/cpu.c @@ -78,12 +78,12 @@ static struct map_desc s3c_iodesc[] __initdata = { .length = SZ_4K, .type = MT_DEVICE, }, { - .virtual = (unsigned long)S3C_VA_VIC0, + .virtual = (unsigned long)VA_VIC0, .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), .length = SZ_16K, .type = MT_DEVICE, }, { - .virtual = (unsigned long)S3C_VA_VIC1, + .virtual = (unsigned long)VA_VIC1, .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), .length = SZ_16K, .type = MT_DEVICE, diff --git a/arch/arm/plat-s3c64xx/include/plat/irqs.h b/arch/arm/plat-s3c64xx/include/plat/irqs.h index 7956fd3bb194..176fe15a61d6 100644 --- a/arch/arm/plat-s3c64xx/include/plat/irqs.h +++ b/arch/arm/plat-s3c64xx/include/plat/irqs.h @@ -24,8 +24,8 @@ #define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET) -#define S3C_VIC0_BASE S3C_IRQ(0) -#define S3C_VIC1_BASE S3C_IRQ(32) +#define IRQ_VIC0_BASE S3C_IRQ(0) +#define IRQ_VIC1_BASE S3C_IRQ(32) /* UART interrupts, each UART has 4 intterupts per channel so * use the space between the ISA and S3C main interrupts. Note, these @@ -59,8 +59,8 @@ /* VIC based IRQs */ -#define S3C64XX_IRQ_VIC0(x) (S3C_VIC0_BASE + (x)) -#define S3C64XX_IRQ_VIC1(x) (S3C_VIC1_BASE + (x)) +#define S3C64XX_IRQ_VIC0(x) (IRQ_VIC0_BASE + (x)) +#define S3C64XX_IRQ_VIC1(x) (IRQ_VIC1_BASE + (x)) /* VIC0 */ diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h index ff46e7fa957a..3ef62741e5d1 100644 --- a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h +++ b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h @@ -35,14 +35,6 @@ #define S3C_MEM0_GATE S3C_CLKREG(0x3C) /* CLKDIV0 */ -#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28) -#define S3C6400_CLKDIV0_MFC_SHIFT (28) -#define S3C6400_CLKDIV0_JPEG_MASK (0xf << 24) -#define S3C6400_CLKDIV0_JPEG_SHIFT (24) -#define S3C6400_CLKDIV0_CAM_MASK (0xf << 20) -#define S3C6400_CLKDIV0_CAM_SHIFT (20) -#define S3C6400_CLKDIV0_SECURITY_MASK (0x3 << 18) -#define S3C6400_CLKDIV0_SECURITY_SHIFT (18) #define S3C6400_CLKDIV0_PCLK_MASK (0xf << 12) #define S3C6400_CLKDIV0_PCLK_SHIFT (12) #define S3C6400_CLKDIV0_HCLK2_MASK (0x7 << 9) @@ -51,42 +43,11 @@ #define S3C6400_CLKDIV0_HCLK_SHIFT (8) #define S3C6400_CLKDIV0_MPLL_MASK (0x1 << 4) #define S3C6400_CLKDIV0_MPLL_SHIFT (4) + #define S3C6400_CLKDIV0_ARM_MASK (0x7 << 0) #define S3C6410_CLKDIV0_ARM_MASK (0xf << 0) #define S3C6400_CLKDIV0_ARM_SHIFT (0) -/* CLKDIV1 */ -#define S3C6410_CLKDIV1_FIMC_MASK (0xf << 24) -#define S3C6410_CLKDIV1_FIMC_SHIFT (24) -#define S3C6400_CLKDIV1_UHOST_MASK (0xf << 20) -#define S3C6400_CLKDIV1_UHOST_SHIFT (20) -#define S3C6400_CLKDIV1_SCALER_MASK (0xf << 16) -#define S3C6400_CLKDIV1_SCALER_SHIFT (16) -#define S3C6400_CLKDIV1_LCD_MASK (0xf << 12) -#define S3C6400_CLKDIV1_LCD_SHIFT (12) -#define S3C6400_CLKDIV1_MMC2_MASK (0xf << 8) -#define S3C6400_CLKDIV1_MMC2_SHIFT (8) -#define S3C6400_CLKDIV1_MMC1_MASK (0xf << 4) -#define S3C6400_CLKDIV1_MMC1_SHIFT (4) -#define S3C6400_CLKDIV1_MMC0_MASK (0xf << 0) -#define S3C6400_CLKDIV1_MMC0_SHIFT (0) - -/* CLKDIV2 */ -#define S3C6410_CLKDIV2_AUDIO2_MASK (0xf << 24) -#define S3C6410_CLKDIV2_AUDIO2_SHIFT (24) -#define S3C6400_CLKDIV2_IRDA_MASK (0xf << 20) -#define S3C6400_CLKDIV2_IRDA_SHIFT (20) -#define S3C6400_CLKDIV2_UART_MASK (0xf << 16) -#define S3C6400_CLKDIV2_UART_SHIFT (16) -#define S3C6400_CLKDIV2_AUDIO1_MASK (0xf << 12) -#define S3C6400_CLKDIV2_AUDIO1_SHIFT (12) -#define S3C6400_CLKDIV2_AUDIO0_MASK (0xf << 8) -#define S3C6400_CLKDIV2_AUDIO0_SHIFT (8) -#define S3C6400_CLKDIV2_SPI1_MASK (0xf << 4) -#define S3C6400_CLKDIV2_SPI1_SHIFT (4) -#define S3C6400_CLKDIV2_SPI0_MASK (0xf << 0) -#define S3C6400_CLKDIV2_SPI0_SHIFT (0) - /* HCLK GATE Registers */ #define S3C_CLKCON_HCLK_3DSE (1<<31) #define S3C_CLKCON_HCLK_UHOST (1<<29) @@ -192,34 +153,4 @@ #define S3C6400_CLKSRC_EPLL_MOUT_SHIFT (2) #define S3C6400_CLKSRC_MFC (1 << 4) -#define S3C6410_CLKSRC_TV27_MASK (0x1 << 31) -#define S3C6410_CLKSRC_TV27_SHIFT (31) -#define S3C6410_CLKSRC_DAC27_MASK (0x1 << 30) -#define S3C6410_CLKSRC_DAC27_SHIFT (30) -#define S3C6400_CLKSRC_SCALER_MASK (0x3 << 28) -#define S3C6400_CLKSRC_SCALER_SHIFT (28) -#define S3C6400_CLKSRC_LCD_MASK (0x3 << 26) -#define S3C6400_CLKSRC_LCD_SHIFT (26) -#define S3C6400_CLKSRC_IRDA_MASK (0x3 << 24) -#define S3C6400_CLKSRC_IRDA_SHIFT (24) -#define S3C6400_CLKSRC_MMC2_MASK (0x3 << 22) -#define S3C6400_CLKSRC_MMC2_SHIFT (22) -#define S3C6400_CLKSRC_MMC1_MASK (0x3 << 20) -#define S3C6400_CLKSRC_MMC1_SHIFT (20) -#define S3C6400_CLKSRC_MMC0_MASK (0x3 << 18) -#define S3C6400_CLKSRC_MMC0_SHIFT (18) -#define S3C6400_CLKSRC_SPI1_MASK (0x3 << 16) -#define S3C6400_CLKSRC_SPI1_SHIFT (16) -#define S3C6400_CLKSRC_SPI0_MASK (0x3 << 14) -#define S3C6400_CLKSRC_SPI0_SHIFT (14) -#define S3C6400_CLKSRC_UART_MASK (0x1 << 13) -#define S3C6400_CLKSRC_UART_SHIFT (13) -#define S3C6400_CLKSRC_AUDIO1_MASK (0x7 << 10) -#define S3C6400_CLKSRC_AUDIO1_SHIFT (10) -#define S3C6400_CLKSRC_AUDIO0_MASK (0x7 << 7) -#define S3C6400_CLKSRC_AUDIO0_SHIFT (7) -#define S3C6400_CLKSRC_UHOST_MASK (0x3 << 5) -#define S3C6400_CLKSRC_UHOST_SHIFT (5) - - #endif /* _PLAT_REGS_CLOCK_H */ diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c index 8dc5b6da9789..67a145d440f3 100644 --- a/arch/arm/plat-s3c64xx/irq.c +++ b/arch/arm/plat-s3c64xx/irq.c @@ -21,88 +21,11 @@ #include <asm/hardware/vic.h> #include <mach/map.h> -#include <plat/regs-serial.h> -#include <plat/regs-timer.h> +#include <plat/irq-vic-timer.h> +#include <plat/irq-uart.h> #include <plat/cpu.h> -/* Timer interrupt handling */ - -static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq) -{ - generic_handle_irq(sub_irq); -} - -static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER0); -} - -static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER1); -} - -static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER2); -} - -static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER3); -} - -static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER4); -} - -/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ - -static void s3c_irq_timer_mask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg &= ~(1 << (irq - IRQ_TIMER0)); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_unmask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg |= 1 << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_ack(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; - reg |= (1 << 5) << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static struct irq_chip s3c_irq_timer = { - .name = "s3c-timer", - .mask = s3c_irq_timer_mask, - .unmask = s3c_irq_timer_unmask, - .ack = s3c_irq_timer_ack, -}; - -struct uart_irq { - void __iomem *regs; - unsigned int base_irq; - unsigned int parent_irq; -}; - -/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] - * are consecutive when looking up the interrupt in the demux routines. - */ -static struct uart_irq uart_irqs[] = { +static struct s3c_uart_irq uart_irqs[] = { [0] = { .regs = S3C_VA_UART0, .base_irq = IRQ_S3CUART_BASE0, @@ -125,132 +48,22 @@ static struct uart_irq uart_irqs[] = { }, }; -static inline void __iomem *s3c_irq_uart_base(unsigned int irq) -{ - struct uart_irq *uirq = get_irq_chip_data(irq); - return uirq->regs; -} - -static inline unsigned int s3c_irq_uart_bit(unsigned int irq) -{ - return irq & 3; -} - -/* UART interrupt registers, not worth adding to seperate include header */ - -static void s3c_irq_uart_mask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_maskack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_uart_unmask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg &= ~(1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_ack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) -{ - struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0]; - u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); - int base = uirq->base_irq; - - if (pend & (1 << 0)) - generic_handle_irq(base); - if (pend & (1 << 1)) - generic_handle_irq(base + 1); - if (pend & (1 << 2)) - generic_handle_irq(base + 2); - if (pend & (1 << 3)) - generic_handle_irq(base + 3); -} - -static struct irq_chip s3c_irq_uart = { - .name = "s3c-uart", - .mask = s3c_irq_uart_mask, - .unmask = s3c_irq_uart_unmask, - .mask_ack = s3c_irq_uart_maskack, - .ack = s3c_irq_uart_ack, -}; - -static void __init s3c64xx_uart_irq(struct uart_irq *uirq) -{ - void __iomem *reg_base = uirq->regs; - unsigned int irq; - int offs; - - /* mask all interrupts at the start. */ - __raw_writel(0xf, reg_base + S3C64XX_UINTM); - - for (offs = 0; offs < 3; offs++) { - irq = uirq->base_irq + offs; - - set_irq_chip(irq, &s3c_irq_uart); - set_irq_chip_data(irq, uirq); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - - set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); -} void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) { - int uart, irq; - printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); /* initialise the pair of VICs */ - vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid, 0); - vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid, 0); + vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0); + vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0); /* add the timer sub-irqs */ - set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0); - set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1); - set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2); - set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3); - set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4); - - for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) { - set_irq_chip(irq, &s3c_irq_timer); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } + s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0); + s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1); + s3c_init_vic_timer_irq(IRQ_TIMER2_VIC, IRQ_TIMER2); + s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); + s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); - for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) - s3c64xx_uart_irq(&uart_irqs[uart]); + s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } - - diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index 6ffa21eb1b91..cb2bf4bff051 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -29,6 +29,7 @@ #include <plat/regs-clock.h> #include <plat/clock.h> +#include <plat/clock-clksrc.h> #include <plat/cpu.h> #include <plat/pll.h> @@ -46,22 +47,7 @@ static struct clk clk_ext_xtal_mux = { #define clk_fin_epll clk_ext_xtal_mux #define clk_fout_mpll clk_mpll - -struct clk_sources { - unsigned int nr_sources; - struct clk **sources; -}; - -struct clksrc_clk { - struct clk clk; - unsigned int mask; - unsigned int shift; - - struct clk_sources *sources; - - unsigned int divider_shift; - void __iomem *reg_divider; -}; +#define clk_fout_epll clk_epll static struct clk clk_fout_apll = { .name = "fout_apll", @@ -73,7 +59,7 @@ static struct clk *clk_src_apll_list[] = { [1] = &clk_fout_apll, }; -static struct clk_sources clk_src_apll = { +static struct clksrc_sources clk_src_apll = { .sources = clk_src_apll_list, .nr_sources = ARRAY_SIZE(clk_src_apll_list), }; @@ -83,22 +69,16 @@ static struct clksrc_clk clk_mout_apll = { .name = "mout_apll", .id = -1, }, - .shift = S3C6400_CLKSRC_APLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_APLL_MOUT, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 0, .size = 1 }, .sources = &clk_src_apll, }; -static struct clk clk_fout_epll = { - .name = "fout_epll", - .id = -1, -}; - static struct clk *clk_src_epll_list[] = { [0] = &clk_fin_epll, [1] = &clk_fout_epll, }; -static struct clk_sources clk_src_epll = { +static struct clksrc_sources clk_src_epll = { .sources = clk_src_epll_list, .nr_sources = ARRAY_SIZE(clk_src_epll_list), }; @@ -108,8 +88,7 @@ static struct clksrc_clk clk_mout_epll = { .name = "mout_epll", .id = -1, }, - .shift = S3C6400_CLKSRC_EPLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_EPLL_MOUT, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 2, .size = 1 }, .sources = &clk_src_epll, }; @@ -118,7 +97,7 @@ static struct clk *clk_src_mpll_list[] = { [1] = &clk_fout_mpll, }; -static struct clk_sources clk_src_mpll = { +static struct clksrc_sources clk_src_mpll = { .sources = clk_src_mpll_list, .nr_sources = ARRAY_SIZE(clk_src_mpll_list), }; @@ -128,8 +107,7 @@ static struct clksrc_clk clk_mout_mpll = { .name = "mout_mpll", .id = -1, }, - .shift = S3C6400_CLKSRC_MPLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_MPLL_MOUT, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 1, .size = 1 }, .sources = &clk_src_mpll, }; @@ -187,9 +165,11 @@ static struct clk clk_arm = { .name = "armclk", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s3c64xx_clk_arm_get_rate, - .set_rate = s3c64xx_clk_arm_set_rate, - .round_rate = s3c64xx_clk_arm_round_rate, + .ops = &(struct clk_ops) { + .get_rate = s3c64xx_clk_arm_get_rate, + .set_rate = s3c64xx_clk_arm_set_rate, + .round_rate = s3c64xx_clk_arm_round_rate, + }, }; static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) @@ -204,11 +184,15 @@ static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) return rate; } +static struct clk_ops clk_dout_ops = { + .get_rate = s3c64xx_clk_doutmpll_get_rate, +}; + static struct clk clk_dout_mpll = { .name = "dout_mpll", .id = -1, .parent = &clk_mout_mpll.clk, - .get_rate = s3c64xx_clk_doutmpll_get_rate, + .ops = &clk_dout_ops, }; static struct clk *clkset_spi_mmc_list[] = { @@ -218,7 +202,7 @@ static struct clk *clkset_spi_mmc_list[] = { &clk_27m, }; -static struct clk_sources clkset_spi_mmc = { +static struct clksrc_sources clkset_spi_mmc = { .sources = clkset_spi_mmc_list, .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list), }; @@ -230,7 +214,7 @@ static struct clk *clkset_irda_list[] = { &clk_27m, }; -static struct clk_sources clkset_irda = { +static struct clksrc_sources clkset_irda = { .sources = clkset_irda_list, .nr_sources = ARRAY_SIZE(clkset_irda_list), }; @@ -242,7 +226,7 @@ static struct clk *clkset_uart_list[] = { NULL }; -static struct clk_sources clkset_uart = { +static struct clksrc_sources clkset_uart = { .sources = clkset_uart_list, .nr_sources = ARRAY_SIZE(clkset_uart_list), }; @@ -254,12 +238,11 @@ static struct clk *clkset_uhost_list[] = { &clk_fin_epll, }; -static struct clk_sources clkset_uhost = { +static struct clksrc_sources clkset_uhost = { .sources = clkset_uhost_list, .nr_sources = ARRAY_SIZE(clkset_uhost_list), }; - /* The peripheral clocks are all controlled via clocksource followed * by an optional divider and gate stage. We currently roll this into * one clock which hides the intermediate clock from the mux. @@ -270,221 +253,7 @@ static struct clk_sources clkset_uhost = { * have a common parent divisor so are not included here. */ -static inline struct clksrc_clk *to_clksrc(struct clk *clk) -{ - return container_of(clk, struct clksrc_clk, clk); -} - -static unsigned long s3c64xx_getrate_clksrc(struct clk *clk) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - unsigned long rate = clk_get_rate(clk->parent); - u32 clkdiv = __raw_readl(sclk->reg_divider); - - clkdiv >>= sclk->divider_shift; - clkdiv &= 0xf; - clkdiv++; - - rate /= clkdiv; - return rate; -} - -static int s3c64xx_setrate_clksrc(struct clk *clk, unsigned long rate) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - void __iomem *reg = sclk->reg_divider; - unsigned int div; - u32 val; - - rate = clk_round_rate(clk, rate); - div = clk_get_rate(clk->parent) / rate; - if (div > 16) - return -EINVAL; - - val = __raw_readl(reg); - val &= ~(0xf << sclk->divider_shift); - val |= (div - 1) << sclk->divider_shift; - __raw_writel(val, reg); - - return 0; -} - -static int s3c64xx_setparent_clksrc(struct clk *clk, struct clk *parent) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - struct clk_sources *srcs = sclk->sources; - u32 clksrc = __raw_readl(S3C_CLK_SRC); - int src_nr = -1; - int ptr; - - for (ptr = 0; ptr < srcs->nr_sources; ptr++) - if (srcs->sources[ptr] == parent) { - src_nr = ptr; - break; - } - - if (src_nr >= 0) { - clksrc &= ~sclk->mask; - clksrc |= src_nr << sclk->shift; - - __raw_writel(clksrc, S3C_CLK_SRC); - - clk->parent = parent; - return 0; - } - - return -EINVAL; -} - -static unsigned long s3c64xx_roundrate_clksrc(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate = clk_get_rate(clk->parent); - int div; - - if (rate > parent_rate) - rate = parent_rate; - else { - div = parent_rate / rate; - - if (div == 0) - div = 1; - if (div > 16) - div = 16; - - rate = parent_rate / div; - } - - return rate; -} - -static struct clksrc_clk clk_mmc0 = { - .clk = { - .name = "mmc_bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_MMC0, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_MMC0_SHIFT, - .mask = S3C6400_CLKSRC_MMC0_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_mmc1 = { - .clk = { - .name = "mmc_bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_MMC1, - .enable = s3c64xx_sclk_ctrl, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .set_parent = s3c64xx_setparent_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_MMC1_SHIFT, - .mask = S3C6400_CLKSRC_MMC1_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_mmc2 = { - .clk = { - .name = "mmc_bus", - .id = 2, - .ctrlbit = S3C_CLKCON_SCLK_MMC2, - .enable = s3c64xx_sclk_ctrl, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .set_parent = s3c64xx_setparent_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_MMC2_SHIFT, - .mask = S3C6400_CLKSRC_MMC2_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_usbhost = { - .clk = { - .name = "usb-bus-host", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_UHOST, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_UHOST_SHIFT, - .mask = S3C6400_CLKSRC_UHOST_MASK, - .sources = &clkset_uhost, - .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_uart_uclk1 = { - .clk = { - .name = "uclk1", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_UART, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_UART_SHIFT, - .mask = S3C6400_CLKSRC_UART_MASK, - .sources = &clkset_uart, - .divider_shift = S3C6400_CLKDIV2_UART_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -/* Where does UCLK0 come from? */ - -static struct clksrc_clk clk_spi0 = { - .clk = { - .name = "spi-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_SPI0, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_SPI0_SHIFT, - .mask = S3C6400_CLKSRC_SPI0_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -static struct clksrc_clk clk_spi1 = { - .clk = { - .name = "spi-bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_SPI1, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_SPI1_SHIFT, - .mask = S3C6400_CLKSRC_SPI1_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; +/* clocks that feed other parts of the clock source tree */ static struct clk clk_iis_cd0 = { .name = "iis_cdclk0", @@ -509,29 +278,11 @@ static struct clk *clkset_audio0_list[] = { [4] = &clk_pcm_cd, }; -static struct clk_sources clkset_audio0 = { +static struct clksrc_sources clkset_audio0 = { .sources = clkset_audio0_list, .nr_sources = ARRAY_SIZE(clkset_audio0_list), }; -static struct clksrc_clk clk_audio0 = { - .clk = { - .name = "audio-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_AUDIO0_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO0_MASK, - .sources = &clkset_audio0, - .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - static struct clk *clkset_audio1_list[] = { [0] = &clk_mout_epll.clk, [1] = &clk_dout_mpll, @@ -540,72 +291,133 @@ static struct clk *clkset_audio1_list[] = { [4] = &clk_pcm_cd, }; -static struct clk_sources clkset_audio1 = { +static struct clksrc_sources clkset_audio1 = { .sources = clkset_audio1_list, .nr_sources = ARRAY_SIZE(clkset_audio1_list), }; -static struct clksrc_clk clk_audio1 = { - .clk = { - .name = "audio-bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_AUDIO1_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO1_MASK, - .sources = &clkset_audio1, - .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -static struct clksrc_clk clk_irda = { - .clk = { - .name = "irda-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_IRDA, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, - }, - .shift = S3C6400_CLKSRC_IRDA_SHIFT, - .mask = S3C6400_CLKSRC_IRDA_MASK, - .sources = &clkset_irda, - .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - static struct clk *clkset_camif_list[] = { &clk_h2, }; -static struct clk_sources clkset_camif = { +static struct clksrc_sources clkset_camif = { .sources = clkset_camif_list, .nr_sources = ARRAY_SIZE(clkset_camif_list), }; -static struct clksrc_clk clk_camif = { - .clk = { - .name = "camera", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_CAM, - .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, +static struct clksrc_clk clksrcs[] = { + { + .clk = { + .name = "mmc_bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_MMC0, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 18, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 0, .size = 4 }, + .sources = &clkset_spi_mmc, + }, { + .clk = { + .name = "mmc_bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_MMC1, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 20, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 4, .size = 4 }, + .sources = &clkset_spi_mmc, + }, { + .clk = { + .name = "mmc_bus", + .id = 2, + .ctrlbit = S3C_CLKCON_SCLK_MMC2, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 22, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 8, .size = 4 }, + .sources = &clkset_spi_mmc, + }, { + .clk = { + .name = "usb-bus-host", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_UHOST, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 5, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 20, .size = 4 }, + .sources = &clkset_uhost, + }, { + .clk = { + .name = "uclk1", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_UART, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 13, .size = 1 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 16, .size = 4 }, + .sources = &clkset_uart, + }, { +/* Where does UCLK0 come from? */ + .clk = { + .name = "spi-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_SPI0, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 14, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 0, .size = 4 }, + .sources = &clkset_spi_mmc, + }, { + .clk = { + .name = "spi-bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_SPI1, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 16, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 4, .size = 4 }, + .sources = &clkset_spi_mmc, + }, { + .clk = { + .name = "audio-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 7, .size = 3 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 8, .size = 4 }, + .sources = &clkset_audio0, + }, { + .clk = { + .name = "audio-bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 10, .size = 3 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 12, .size = 4 }, + .sources = &clkset_audio1, + }, { + .clk = { + .name = "irda-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_IRDA, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 24, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 20, .size = 4 }, + .sources = &clkset_irda, + }, { + .clk = { + .name = "camera", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_CAM, + .enable = s3c64xx_sclk_ctrl, + }, + .reg_div = { .reg = S3C_CLK_DIV0, .shift = 20, .size = 4 }, + .reg_src = { .reg = NULL, .shift = 0, .size = 0 }, + .sources = &clkset_camif, }, - .shift = 0, - .mask = 0, - .sources = &clkset_camif, - .divider_shift = S3C6400_CLKDIV0_CAM_SHIFT, - .reg_divider = S3C_CLK_DIV0, }; /* Clock initialisation code */ @@ -614,39 +426,7 @@ static struct clksrc_clk *init_parents[] = { &clk_mout_apll, &clk_mout_epll, &clk_mout_mpll, - &clk_mmc0, - &clk_mmc1, - &clk_mmc2, - &clk_usbhost, - &clk_uart_uclk1, - &clk_spi0, - &clk_spi1, - &clk_audio0, - &clk_audio1, - &clk_irda, - &clk_camif, -}; - -static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) -{ - struct clk_sources *srcs = clk->sources; - u32 clksrc = __raw_readl(S3C_CLK_SRC); - - clksrc &= clk->mask; - clksrc >>= clk->shift; - - if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { - printk(KERN_ERR "%s: bad source %d\n", - clk->clk.name, clksrc); - return; - } - - clk->clk.parent = srcs->sources[clksrc]; - - printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", - clk->clk.name, clk->clk.parent->name, clksrc, - clk_get_rate(&clk->clk)); -} +}; #define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) @@ -706,7 +486,10 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) clk_f.rate = fclk; for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) - s3c6400_set_clksrc(init_parents[ptr]); + s3c_set_clksrc(init_parents[ptr], true); + + for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) + s3c_set_clksrc(&clksrcs[ptr], true); } static struct clk *clks[] __initdata = { @@ -715,20 +498,8 @@ static struct clk *clks[] __initdata = { &clk_iis_cd1, &clk_pcm_cd, &clk_mout_epll.clk, - &clk_fout_epll, &clk_mout_mpll.clk, &clk_dout_mpll, - &clk_mmc0.clk, - &clk_mmc1.clk, - &clk_mmc2.clk, - &clk_usbhost.clk, - &clk_uart_uclk1.clk, - &clk_spi0.clk, - &clk_spi1.clk, - &clk_audio0.clk, - &clk_audio1.clk, - &clk_irda.clk, - &clk_camif.clk, &clk_arm, }; @@ -761,6 +532,5 @@ void __init s3c6400_register_clocks(unsigned armclk_divlimit) } } - clk_mpll.parent = &clk_mout_mpll.clk; - clk_epll.parent = &clk_mout_epll.clk; + s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs)); } diff --git a/arch/arm/plat-s5pc1xx/clock.c b/arch/arm/plat-s5pc1xx/clock.c index 26c21d849790..cc21a8bc884e 100644 --- a/arch/arm/plat-s5pc1xx/clock.c +++ b/arch/arm/plat-s5pc1xx/clock.c @@ -70,6 +70,10 @@ static int clk_default_setrate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_ops_default_setrate = { + .set_rate = clk_default_setrate, +}; + static int clk_dummy_enable(struct clk *clk, int enable) { return 0; @@ -81,8 +85,8 @@ struct clk clk_hd0 = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, .enable = clk_dummy_enable, + .ops = &clk_ops_default_setrate, }; struct clk clk_pd0 = { @@ -91,7 +95,7 @@ struct clk clk_pd0 = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_default_setrate, .enable = clk_dummy_enable, }; @@ -700,16 +704,8 @@ void __init s5pc1xx_register_clocks(void) s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); - clkp = s5pc100_init_clocks; - size = ARRAY_SIZE(s5pc100_init_clocks); - - for (ptr = 0; ptr < size; ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(s5pc100_init_clocks, + ARRAY_SIZE(s5pc100_init_clocks); clkp = s5pc100_init_clocks_disable; size = ARRAY_SIZE(s5pc100_init_clocks_disable); diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c index b436d44510c8..16f0b9077390 100644 --- a/arch/arm/plat-s5pc1xx/s5pc100-clock.c +++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c @@ -111,7 +111,9 @@ static struct clk clk_dout_apll = { .name = "dout_apll", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s5pc100_clk_dout_apll_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_apll_get_rate, + }, }; static unsigned long s5pc100_clk_arm_get_rate(struct clk *clk) @@ -165,9 +167,11 @@ static struct clk clk_arm = { .name = "armclk", .id = -1, .parent = &clk_dout_apll, - .get_rate = s5pc100_clk_arm_get_rate, - .set_rate = s5pc100_clk_arm_set_rate, - .round_rate = s5pc100_clk_arm_round_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_arm_get_rate, + .set_rate = s5pc100_clk_arm_set_rate, + .round_rate = s5pc100_clk_arm_round_rate, + }, }; static unsigned long s5pc100_clk_dout_d0_bus_get_rate(struct clk *clk) @@ -185,7 +189,9 @@ static struct clk clk_dout_d0_bus = { .name = "dout_d0_bus", .id = -1, .parent = &clk_arm, - .get_rate = s5pc100_clk_dout_d0_bus_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_d0_bus_get_rate, + }, }; static unsigned long s5pc100_clk_dout_pclkd0_get_rate(struct clk *clk) @@ -203,7 +209,9 @@ static struct clk clk_dout_pclkd0 = { .name = "dout_pclkd0", .id = -1, .parent = &clk_dout_d0_bus, - .get_rate = s5pc100_clk_dout_pclkd0_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_pclkd0_get_rate, + }, }; static unsigned long s5pc100_clk_dout_apll2_get_rate(struct clk *clk) @@ -221,7 +229,9 @@ static struct clk clk_dout_apll2 = { .name = "dout_apll2", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s5pc100_clk_dout_apll2_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_apll2_get_rate, + }, }; /* MPLL */ @@ -284,7 +294,9 @@ static struct clk clk_dout_d1_bus = { .name = "dout_d1_bus", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_d1_bus_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_d1_bus_get_rate, + }, }; static struct clk *clkset_onenand_list[] = { @@ -325,7 +337,9 @@ static struct clk clk_dout_pclkd1 = { .name = "dout_pclkd1", .id = -1, .parent = &clk_dout_d1_bus, - .get_rate = s5pc100_clk_dout_pclkd1_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_pclkd1_get_rate, + }, }; static unsigned long s5pc100_clk_dout_mpll2_get_rate(struct clk *clk) @@ -345,7 +359,9 @@ static struct clk clk_dout_mpll2 = { .name = "dout_mpll2", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_mpll2_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_mpll2_get_rate, + }, }; static unsigned long s5pc100_clk_dout_cam_get_rate(struct clk *clk) @@ -365,7 +381,9 @@ static struct clk clk_dout_cam = { .name = "dout_cam", .id = -1, .parent = &clk_dout_mpll2, - .get_rate = s5pc100_clk_dout_cam_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_cam_get_rate, + }, }; static unsigned long s5pc100_clk_dout_mpll_get_rate(struct clk *clk) @@ -385,7 +403,9 @@ static struct clk clk_dout_mpll = { .name = "dout_mpll", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_mpll_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_mpll_get_rate, + }, }; /* EPLL */ @@ -540,6 +560,13 @@ static unsigned long s5pc100_roundrate_clksrc(struct clk *clk, return rate; } +static struct clk_ops s5pc100_clksrc_ops = { + .set_parent = s5pc100_setparent_clksrc, + .get_rate = s5pc100_getrate_clksrc, + .set_rate = s5pc100_setrate_clksrc, + .round_rate = s5pc100_roundrate_clksrc, +}; + static struct clk *clkset_spi_list[] = { &clk_mout_epll.clk, &clk_dout_mpll2, @@ -558,10 +585,7 @@ static struct clksrc_clk clk_spi0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI0, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + }, .shift = S5PC100_CLKSRC1_SPI0_SHIFT, .mask = S5PC100_CLKSRC1_SPI0_MASK, @@ -577,10 +601,7 @@ static struct clksrc_clk clk_spi1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_SPI1_SHIFT, .mask = S5PC100_CLKSRC1_SPI1_MASK, @@ -596,10 +617,7 @@ static struct clksrc_clk clk_spi2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_SPI2_SHIFT, .mask = S5PC100_CLKSRC1_SPI2_MASK, @@ -625,10 +643,7 @@ static struct clksrc_clk clk_uart_uclk1 = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_UART, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_UART_SHIFT, .mask = S5PC100_CLKSRC1_UART_MASK, @@ -683,10 +698,7 @@ static struct clksrc_clk clk_audio0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO0, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO0_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO0_MASK, @@ -716,10 +728,7 @@ static struct clksrc_clk clk_audio1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO1, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO1_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO1_MASK, @@ -748,10 +757,7 @@ static struct clksrc_clk clk_audio2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO2, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO2_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO2_MASK, @@ -801,10 +807,7 @@ static struct clksrc_clk clk_lcd = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK1_LCD, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_LCD_SHIFT, .mask = S5PC100_CLKSRC2_LCD_MASK, @@ -820,10 +823,7 @@ static struct clksrc_clk clk_fimc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC0, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC0_SHIFT, .mask = S5PC100_CLKSRC2_FIMC0_MASK, @@ -839,10 +839,7 @@ static struct clksrc_clk clk_fimc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC1, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC1_SHIFT, .mask = S5PC100_CLKSRC2_FIMC1_MASK, @@ -858,10 +855,7 @@ static struct clksrc_clk clk_fimc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC2, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC2_SHIFT, .mask = S5PC100_CLKSRC2_FIMC2_MASK, @@ -889,10 +883,7 @@ static struct clksrc_clk clk_mmc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC0_SHIFT, .mask = S5PC100_CLKSRC2_MMC0_MASK, @@ -908,10 +899,7 @@ static struct clksrc_clk clk_mmc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC1_SHIFT, .mask = S5PC100_CLKSRC2_MMC1_MASK, @@ -927,10 +915,7 @@ static struct clksrc_clk clk_mmc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC2_SHIFT, .mask = S5PC100_CLKSRC2_MMC2_MASK, @@ -959,10 +944,7 @@ static struct clksrc_clk clk_usbhost = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_USBHOST, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_UHOST_SHIFT, .mask = S5PC100_CLKSRC1_UHOST_MASK, diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 486a0d6301e7..faec4b8c626c 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -13,5 +13,107 @@ config PLAT_SAMSUNG if PLAT_SAMSUNG +config SAMSUNG_CLKSRC + bool + help + Select the clock code for the clksrc implementation + used by newer systems such as the S3C64XX. + +# options for IRQ support + +config SAMSUNG_IRQ_VIC_TIMER + bool + help + Internal configuration to build the VIC timer interrupt code. + +config SAMSUNG_IRQ_UART + bool + help + Internal configuration to build the IRQ UART demux code. + +# options for gpio configuration support + +config S3C_GPIO_CFG_S3C24XX + bool + help + Internal configuration to enable S3C24XX style GPIO configuration + functions. + +config S3C_GPIO_CFG_S3C64XX + bool + help + Internal configuration to enable S3C64XX style GPIO configuration + functions. + +config S5P_GPIO_CFG_S5PC1XX + bool + help + Internal configuration to enable S5PC1XX style GPIO configuration + functions. + +config S3C_GPIO_PULL_UPDOWN + bool + help + Internal configuration to enable the correct GPIO pull helper + +config S3C_GPIO_PULL_DOWN + bool + help + Internal configuration to enable the correct GPIO pull helper + +config S3C_GPIO_PULL_UP + bool + help + Internal configuration to enable the correct GPIO pull helper + +config SAMSUNG_GPIO_EXTRA + int "Number of additional GPIO pins" + default 0 + help + Use additional GPIO space in addition to the GPIO's the SOC + provides. This allows expanding the GPIO space for use with + GPIO expanders. + +# device definitions to compile in + +config S3C_DEV_HSMMC + bool + help + Compile in platform device definitions for HSMMC code + +config S3C_DEV_HSMMC1 + bool + help + Compile in platform device definitions for HSMMC channel 1 + +config S3C_DEV_HSMMC2 + bool + help + Compile in platform device definitions for HSMMC channel 2 + +config S3C_DEV_I2C1 + bool + help + Compile in platform device definitions for I2C channel 1 + +config S3C_DEV_FB + bool + help + Compile in platform device definition for framebuffer + +config S3C_DEV_USB_HOST + bool + help + Compile in platform device definition for USB host. + +config S3C_DEV_USB_HSOTG + bool + help + Compile in platform device definition for USB high-speed OtG + +config S3C_DEV_NAND + bool + help + Compile in platform device definition for NAND controller endif diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 4478b9f7dc34..aeb7e12d1f63 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -9,3 +9,25 @@ obj-m := obj-n := dummy.o obj- := +# Objects we always build independent of SoC choice + +obj-y += clock.o +obj-y += pwm-clock.o +obj-y += gpio-config.o + +obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o + +obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o +obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o + +# devices + +obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o +obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o +obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o +obj-y += dev-i2c0.o +obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o +obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o +obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o +obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o +obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c new file mode 100644 index 000000000000..33c633a8be8d --- /dev/null +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -0,0 +1,203 @@ +/* linux/arch/arm/plat-samsung/clock-clksrc.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/sysdev.h> +#include <linux/io.h> + +#include <plat/clock.h> +#include <plat/clock-clksrc.h> +#include <plat/cpu-freq.h> + +static inline struct clksrc_clk *to_clksrc(struct clk *clk) +{ + return container_of(clk, struct clksrc_clk, clk); +} + +static inline u32 bit_mask(u32 shift, u32 nr_bits) +{ + u32 mask = 0xffffffff >> (32 - nr_bits); + + return mask << shift; +} + +static unsigned long s3c_getrate_clksrc(struct clk *clk) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + unsigned long rate = clk_get_rate(clk->parent); + u32 clkdiv = __raw_readl(sclk->reg_div.reg); + u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); + + clkdiv &= mask; + clkdiv >>= sclk->reg_div.shift; + clkdiv++; + + rate /= clkdiv; + return rate; +} + +static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + void __iomem *reg = sclk->reg_div.reg; + unsigned int div; + u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); + u32 val; + + rate = clk_round_rate(clk, rate); + div = clk_get_rate(clk->parent) / rate; + if (div > 16) + return -EINVAL; + + val = __raw_readl(reg); + val &= ~mask; + val |= (div - 1) << sclk->reg_div.shift; + __raw_writel(val, reg); + + return 0; +} + +static int s3c_setparent_clksrc(struct clk *clk, struct clk *parent) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + struct clksrc_sources *srcs = sclk->sources; + u32 clksrc = __raw_readl(sclk->reg_src.reg); + u32 mask = bit_mask(sclk->reg_src.shift, sclk->reg_src.size); + int src_nr = -1; + int ptr; + + for (ptr = 0; ptr < srcs->nr_sources; ptr++) + if (srcs->sources[ptr] == parent) { + src_nr = ptr; + break; + } + + if (src_nr >= 0) { + clk->parent = parent; + + clksrc &= ~mask; + clksrc |= src_nr << sclk->reg_src.shift; + + __raw_writel(clksrc, sclk->reg_src.reg); + return 0; + } + + return -EINVAL; +} + +static unsigned long s3c_roundrate_clksrc(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + int div; + + if (rate >= parent_rate) + rate = parent_rate; + else { + div = parent_rate / rate; + if (parent_rate % rate) + div++; + + if (div == 0) + div = 1; + if (div > 16) + div = 16; + + rate = parent_rate / div; + } + + return rate; +} + +/* Clock initialisation code */ + +void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk, bool announce) +{ + struct clksrc_sources *srcs = clk->sources; + u32 mask = bit_mask(clk->reg_src.shift, clk->reg_src.size); + u32 clksrc = 0; + + if (clk->reg_src.reg) + clksrc = __raw_readl(clk->reg_src.reg); + + clksrc &= mask; + clksrc >>= clk->reg_src.shift; + + if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { + printk(KERN_ERR "%s: bad source %d\n", + clk->clk.name, clksrc); + return; + } + + clk->clk.parent = srcs->sources[clksrc]; + + if (announce) + printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", + clk->clk.name, clk->clk.parent->name, clksrc, + clk_get_rate(&clk->clk)); +} + +static struct clk_ops clksrc_ops = { + .set_parent = s3c_setparent_clksrc, + .get_rate = s3c_getrate_clksrc, + .set_rate = s3c_setrate_clksrc, + .round_rate = s3c_roundrate_clksrc, +}; + +static struct clk_ops clksrc_ops_nodiv = { + .set_parent = s3c_setparent_clksrc, +}; + +static struct clk_ops clksrc_ops_nosrc = { + .get_rate = s3c_getrate_clksrc, + .set_rate = s3c_setrate_clksrc, + .round_rate = s3c_roundrate_clksrc, +}; + +void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) +{ + int ret; + + WARN_ON(!clksrc->reg_div.reg && !clksrc->reg_src.reg); + + for (; size > 0; size--, clksrc++) { + /* fill in the default functions */ + + if (!clksrc->clk.ops) { + if (!clksrc->reg_div.reg) + clksrc->clk.ops = &clksrc_ops_nodiv; + else if (!clksrc->reg_src.reg) + clksrc->clk.ops = &clksrc_ops_nosrc; + else + clksrc->clk.ops = &clksrc_ops; + } + + /* setup the clocksource, but do not announce it + * as it may be re-set by the setup routines + * called after the rest of the clocks have been + * registered + */ + s3c_set_clksrc(clksrc, false); + + ret = s3c24xx_register_clock(&clksrc->clk); + + if (ret < 0) { + printk(KERN_ERR "%s: failed to register %s (%d)\n", + __func__, clksrc->clk.name, ret); + } + } +} diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-samsung/clock.c index 619cfa82dcab..0c746ae7b2a6 100644 --- a/arch/arm/plat-s3c/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -150,8 +150,8 @@ unsigned long clk_get_rate(struct clk *clk) if (clk->rate != 0) return clk->rate; - if (clk->get_rate != NULL) - return (clk->get_rate)(clk); + if (clk->ops != NULL && clk->ops->get_rate != NULL) + return (clk->ops->get_rate)(clk); if (clk->parent != NULL) return clk_get_rate(clk->parent); @@ -161,8 +161,8 @@ unsigned long clk_get_rate(struct clk *clk) long clk_round_rate(struct clk *clk, unsigned long rate) { - if (!IS_ERR(clk) && clk->round_rate) - return (clk->round_rate)(clk, rate); + if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) + return (clk->ops->round_rate)(clk, rate); return rate; } @@ -178,13 +178,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) * the clock may have been made this way by choice. */ - WARN_ON(clk->set_rate == NULL); + WARN_ON(clk->ops == NULL); + WARN_ON(clk->ops && clk->ops->set_rate == NULL); - if (clk->set_rate == NULL) + if (clk->ops == NULL || clk->ops->set_rate == NULL) return -EINVAL; spin_lock(&clocks_lock); - ret = (clk->set_rate)(clk, rate); + ret = (clk->ops->set_rate)(clk, rate); spin_unlock(&clocks_lock); return ret; @@ -204,8 +205,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent) spin_lock(&clocks_lock); - if (clk->set_parent) - ret = (clk->set_parent)(clk, parent); + if (clk->ops && clk->ops->set_parent) + ret = (clk->ops->set_parent)(clk, parent); spin_unlock(&clocks_lock); @@ -224,12 +225,16 @@ EXPORT_SYMBOL(clk_set_parent); /* base clocks */ -static int clk_default_setrate(struct clk *clk, unsigned long rate) +int clk_default_setrate(struct clk *clk, unsigned long rate) { clk->rate = rate; return 0; } +struct clk_ops clk_ops_def_setrate = { + .set_rate = clk_default_setrate, +}; + struct clk clk_xtal = { .name = "xtal", .id = -1, @@ -251,7 +256,7 @@ struct clk clk_epll = { struct clk clk_mpll = { .name = "mpll", .id = -1, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_upll = { @@ -267,7 +272,6 @@ struct clk clk_f = { .rate = 0, .parent = &clk_mpll, .ctrlbit = 0, - .set_rate = clk_default_setrate, }; struct clk clk_h = { @@ -276,7 +280,7 @@ struct clk clk_h = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_p = { @@ -285,7 +289,7 @@ struct clk clk_p = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_usb_bus = { @@ -296,7 +300,6 @@ struct clk clk_usb_bus = { }; - struct clk s3c24xx_uclk = { .name = "uclk", .id = -1, @@ -333,6 +336,28 @@ int s3c24xx_register_clocks(struct clk **clks, int nr_clks) return fails; } +/** + * s3c_register_clocks() - register an array of clocks + * @clkp: Pointer to the first clock in the array. + * @nr_clks: Number of clocks to register. + * + * Call s3c24xx_register_clock() on the @clkp array given, printing an + * error if it fails to register the clock (unlikely). + */ +void __initdata s3c_register_clocks(struct clk *clkp, int nr_clks) +{ + int ret; + + for (; nr_clks > 0; nr_clks--, clkp++) { + ret = s3c24xx_register_clock(clkp); + + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } +} + /* initalise all the clocks */ int __init s3c24xx_register_baseclocks(unsigned long xtal) diff --git a/arch/arm/plat-s3c/dev-fb.c b/arch/arm/plat-samsung/dev-fb.c index a90198fc4b0f..a90198fc4b0f 100644 --- a/arch/arm/plat-s3c/dev-fb.c +++ b/arch/arm/plat-samsung/dev-fb.c diff --git a/arch/arm/plat-s3c/dev-hsmmc.c b/arch/arm/plat-samsung/dev-hsmmc.c index 4c05b39810e2..4c05b39810e2 100644 --- a/arch/arm/plat-s3c/dev-hsmmc.c +++ b/arch/arm/plat-samsung/dev-hsmmc.c diff --git a/arch/arm/plat-s3c/dev-hsmmc1.c b/arch/arm/plat-samsung/dev-hsmmc1.c index e49bc4cd0ee6..e49bc4cd0ee6 100644 --- a/arch/arm/plat-s3c/dev-hsmmc1.c +++ b/arch/arm/plat-samsung/dev-hsmmc1.c diff --git a/arch/arm/plat-s3c/dev-hsmmc2.c b/arch/arm/plat-samsung/dev-hsmmc2.c index 824580bc0e06..824580bc0e06 100644 --- a/arch/arm/plat-s3c/dev-hsmmc2.c +++ b/arch/arm/plat-samsung/dev-hsmmc2.c diff --git a/arch/arm/plat-s3c/dev-i2c0.c b/arch/arm/plat-samsung/dev-i2c0.c index 4c761529b949..4c761529b949 100644 --- a/arch/arm/plat-s3c/dev-i2c0.c +++ b/arch/arm/plat-samsung/dev-i2c0.c diff --git a/arch/arm/plat-s3c/dev-i2c1.c b/arch/arm/plat-samsung/dev-i2c1.c index d44f79110506..d44f79110506 100644 --- a/arch/arm/plat-s3c/dev-i2c1.c +++ b/arch/arm/plat-samsung/dev-i2c1.c diff --git a/arch/arm/plat-s3c/dev-nand.c b/arch/arm/plat-samsung/dev-nand.c index 84808ccda70e..84808ccda70e 100644 --- a/arch/arm/plat-s3c/dev-nand.c +++ b/arch/arm/plat-samsung/dev-nand.c diff --git a/arch/arm/plat-s3c/dev-usb-hsotg.c b/arch/arm/plat-samsung/dev-usb-hsotg.c index e2f604b51c86..e2f604b51c86 100644 --- a/arch/arm/plat-s3c/dev-usb-hsotg.c +++ b/arch/arm/plat-samsung/dev-usb-hsotg.c diff --git a/arch/arm/plat-s3c/dev-usb.c b/arch/arm/plat-samsung/dev-usb.c index 2ee85abed6d9..2ee85abed6d9 100644 --- a/arch/arm/plat-s3c/dev-usb.c +++ b/arch/arm/plat-samsung/dev-usb.c diff --git a/arch/arm/plat-s3c/gpio-config.c b/arch/arm/plat-samsung/gpio-config.c index 456969b6fa0d..456969b6fa0d 100644 --- a/arch/arm/plat-s3c/gpio-config.c +++ b/arch/arm/plat-samsung/gpio-config.c diff --git a/arch/arm/plat-samsung/include/plat/clock-clksrc.h b/arch/arm/plat-samsung/include/plat/clock-clksrc.h new file mode 100644 index 000000000000..50a8ca7c3760 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/clock-clksrc.h @@ -0,0 +1,83 @@ +/* linux/arch/arm/plat-samsung/include/plat/clock-clksrc.h + * + * Parts taken from arch/arm/plat-s3c64xx/clock.c + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Copyright 2009 Ben Dooks <ben-linux@fluff.org> + * Copyright 2009 Harald Welte + * + * 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. +*/ + +/** + * struct clksrc_sources - list of sources for a given clock + * @sources: array of pointers to clocks + * @nr_sources: The size of @sources + */ +struct clksrc_sources { + unsigned int nr_sources; + struct clk **sources; +}; + +/** + * struct clksrc_reg - register definition for clock control bits + * @reg: pointer to the register in virtual memory. + * @shift: the shift in bits to where the bitfield is. + * @size: the size in bits of the bitfield. + * + * This specifies the size and position of the bits we are interested + * in within the register specified by @reg. + */ +struct clksrc_reg { + void __iomem *reg; + unsigned short shift; + unsigned short size; +}; + +/** + * struct clksrc_clk - class of clock for newer style samsung devices. + * @clk: the standard clock representation + * @sources: the sources for this clock + * @reg_src: the register definition for selecting the clock's source + * @reg_div: the register definition for the clock's output divisor + * + * This clock implements the features required by the newer SoCs where + * the standard clock block provides an input mux and a post-mux divisor + * to provide the periperhal's clock. + * + * The array of @sources provides the mapping of mux position to the + * clock, and @reg_src shows the code where to modify to change the mux + * position. The @reg_div defines how to change the divider settings on + * the output. + */ +struct clksrc_clk { + struct clk clk; + struct clksrc_sources *sources; + + struct clksrc_reg reg_src; + struct clksrc_reg reg_div; +}; + +/** + * s3c_set_clksrc() - setup the clock from the register settings + * @clk: The clock to setup. + * @announce: true to announce the setting to printk(). + * + * Setup the clock from the current register settings, for when the + * kernel boots or if it is resuming from a possibly unknown state. + */ +extern void s3c_set_clksrc(struct clksrc_clk *clk, bool announce); + +/** + * s3c_register_clksrc() register clocks from an array of clksrc clocks + * @srcs: The array of clocks to register + * @size: The size of the @srcs array. + * + * Initialise and register the array of clocks described by @srcs. + */ +extern void s3c_register_clksrc(struct clksrc_clk *srcs, int size); diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index d86af84b5b8c..22e011497502 100644 --- a/arch/arm/plat-s3c/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -11,6 +11,30 @@ #include <linux/spinlock.h> +struct clk; + +/** + * struct clk_ops - standard clock operations + * @set_rate: set the clock rate, see clk_set_rate(). + * @get_rate: get the clock rate, see clk_get_rate(). + * @round_rate: round a given clock rate, see clk_round_rate(). + * @set_parent: set the clock's parent, see clk_set_parent(). + * + * Group the common clock implementations together so that we + * don't have to keep setting the same fiels again. We leave + * enable in struct clk. + * + * Adding an extra layer of indirection into the process should + * not be a problem as it is unlikely these operations are going + * to need to be called quickly. + */ +struct clk_ops { + int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); + unsigned long (*round_rate)(struct clk *c, unsigned long rate); + int (*set_parent)(struct clk *c, struct clk *parent); +}; + struct clk { struct list_head list; struct module *owner; @@ -21,11 +45,8 @@ struct clk { unsigned long rate; unsigned long ctrlbit; + struct clk_ops *ops; int (*enable)(struct clk *, int enable); - int (*set_rate)(struct clk *c, unsigned long rate); - unsigned long (*get_rate)(struct clk *c); - unsigned long (*round_rate)(struct clk *c, unsigned long rate); - int (*set_parent)(struct clk *c, struct clk *parent); }; /* other clocks which may be registered by board support */ @@ -54,6 +75,9 @@ extern struct clk clk_h2; extern struct clk clk_27m; extern struct clk clk_48m; +extern int clk_default_setrate(struct clk *clk, unsigned long rate); +extern struct clk_ops clk_ops_def_setrate; + /* exports for arch/arm/mach-s3c2410 * * Please DO NOT use these outside of arch/arm/mach-s3c2410 @@ -66,6 +90,8 @@ extern int s3c2410_clkcon_enable(struct clk *clk, int enable); extern int s3c24xx_register_clock(struct clk *clk); extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); +extern void s3c_register_clocks(struct clk *clk, int nr_clks); + extern int s3c24xx_register_baseclocks(unsigned long xtal); extern void s3c64xx_register_clocks(void); diff --git a/arch/arm/plat-samsung/include/plat/irq-uart.h b/arch/arm/plat-samsung/include/plat/irq-uart.h new file mode 100644 index 000000000000..a9331e49bea3 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/irq-uart.h @@ -0,0 +1,20 @@ +/* arch/arm/plat-samsung/include/plat/irq-uart.h + * + * Copyright (c) 2010 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for Samsung SoC UART IRQ demux for S3C64XX and later + * + * 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. +*/ + +struct s3c_uart_irq { + void __iomem *regs; + unsigned int base_irq; + unsigned int parent_irq; +}; + +extern void s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs); + diff --git a/arch/arm/plat-samsung/include/plat/irq-vic-timer.h b/arch/arm/plat-samsung/include/plat/irq-vic-timer.h new file mode 100644 index 000000000000..a90b53431b5b --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/irq-vic-timer.h @@ -0,0 +1,13 @@ +/* arch/arm/plat-samsung/include/plat/irq-vic-timer.h + * + * Copyright (c) 2010 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for Samsung SoC IRQ VIC timer + * + * 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. +*/ + +extern void s3c_init_vic_timer_irq(unsigned int vic, unsigned int timer); diff --git a/arch/arm/plat-samsung/irq-uart.c b/arch/arm/plat-samsung/irq-uart.c new file mode 100644 index 000000000000..4f8c102674ae --- /dev/null +++ b/arch/arm/plat-samsung/irq-uart.c @@ -0,0 +1,143 @@ +/* arch/arm/plat-samsung/irq-uart.c + * originally part of arch/arm/plat-s3c64xx/irq.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Samsung- UART Interrupt handling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/serial_core.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <mach/map.h> +#include <plat/irq-uart.h> +#include <plat/regs-serial.h> +#include <plat/cpu.h> + +/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] + * are consecutive when looking up the interrupt in the demux routines. + */ + +static inline void __iomem *s3c_irq_uart_base(unsigned int irq) +{ + struct s3c_uart_irq *uirq = get_irq_chip_data(irq); + return uirq->regs; +} + +static inline unsigned int s3c_irq_uart_bit(unsigned int irq) +{ + return irq & 3; +} + +static void s3c_irq_uart_mask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_maskack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_uart_unmask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg &= ~(1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_ack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) +{ + struct s3c_uart_irq *uirq = desc->handler_data; + u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); + int base = uirq->base_irq; + + if (pend & (1 << 0)) + generic_handle_irq(base); + if (pend & (1 << 1)) + generic_handle_irq(base + 1); + if (pend & (1 << 2)) + generic_handle_irq(base + 2); + if (pend & (1 << 3)) + generic_handle_irq(base + 3); +} + +static struct irq_chip s3c_irq_uart = { + .name = "s3c-uart", + .mask = s3c_irq_uart_mask, + .unmask = s3c_irq_uart_unmask, + .mask_ack = s3c_irq_uart_maskack, + .ack = s3c_irq_uart_ack, +}; + +static void __init s3c_init_uart_irq(struct s3c_uart_irq *uirq) +{ + struct irq_desc *desc = irq_to_desc(uirq->parent_irq); + void __iomem *reg_base = uirq->regs; + unsigned int irq; + int offs; + + /* mask all interrupts at the start. */ + __raw_writel(0xf, reg_base + S3C64XX_UINTM); + + for (offs = 0; offs < 3; offs++) { + irq = uirq->base_irq + offs; + + set_irq_chip(irq, &s3c_irq_uart); + set_irq_chip_data(irq, uirq); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + desc->handler_data = uirq; + set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); +} + +/** + * s3c_init_uart_irqs() - initialise UART IRQs and the necessary demuxing + * @irq: The interrupt data for registering + * @nr_irqs: The number of interrupt descriptions in @irq. + * + * Register the UART interrupts specified by @irq including the demuxing + * routines. This supports the S3C6400 and newer style of devices. + */ +void __init s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs) +{ + for (; nr_irqs > 0; nr_irqs--, irq++) + s3c_init_uart_irq(irq); +} diff --git a/arch/arm/plat-samsung/irq-vic-timer.c b/arch/arm/plat-samsung/irq-vic-timer.c new file mode 100644 index 000000000000..0270519fcabc --- /dev/null +++ b/arch/arm/plat-samsung/irq-vic-timer.c @@ -0,0 +1,86 @@ +/* arch/arm/plat-samsung/irq-vic-timer.c + * originally part of arch/arm/plat-s3c64xx/irq.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - Interrupt handling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <mach/map.h> +#include <plat/irq-vic-timer.h> +#include <plat/regs-timer.h> + +static void s3c_irq_demux_vic_timer(unsigned int irq, struct irq_desc *desc) +{ + generic_handle_irq((int)desc->handler_data); +} + +/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ + +static void s3c_irq_timer_mask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg &= ~(1 << (irq - IRQ_TIMER0)); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_unmask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg |= 1 << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_ack(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; + reg |= (1 << 5) << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static struct irq_chip s3c_irq_timer = { + .name = "s3c-timer", + .mask = s3c_irq_timer_mask, + .unmask = s3c_irq_timer_unmask, + .ack = s3c_irq_timer_ack, +}; + +/** + * s3c_init_vic_timer_irq() - initialise timer irq chanined off VIC.\ + * @parent_irq: The parent IRQ on the VIC for the timer. + * @timer_irq: The IRQ to be used for the timer. + * + * Register the necessary IRQ chaining and support for the timer IRQs + * chained of the VIC. + */ +void __init s3c_init_vic_timer_irq(unsigned int parent_irq, + unsigned int timer_irq) +{ + struct irq_desc *desc = irq_to_desc(parent_irq); + + set_irq_chained_handler(parent_irq, s3c_irq_demux_vic_timer); + + set_irq_chip(timer_irq, &s3c_irq_timer); + set_irq_handler(timer_irq, handle_level_irq); + set_irq_flags(timer_irq, IRQF_VALID); + + desc->handler_data = (void *)timer_irq; +} diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-samsung/pwm-clock.c index a318215ab535..46c9381e083b 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-samsung/pwm-clock.c @@ -130,20 +130,22 @@ static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_pwm_scaler_ops = { + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, +}; + static struct clk clk_timer_scaler[] = { [0] = { .name = "pwm-scaler0", .id = -1, - .get_rate = clk_pwm_scaler_get_rate, - .set_rate = clk_pwm_scaler_set_rate, - .round_rate = clk_pwm_scaler_round_rate, + .ops = &clk_pwm_scaler_ops, }, [1] = { .name = "pwm-scaler1", .id = -1, - .get_rate = clk_pwm_scaler_get_rate, - .set_rate = clk_pwm_scaler_set_rate, - .round_rate = clk_pwm_scaler_round_rate, + .ops = &clk_pwm_scaler_ops, }, }; @@ -256,50 +258,46 @@ static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_tdiv_ops = { + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, +}; + static struct pwm_tdiv_clk clk_timer_tdiv[] = { [0] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], }, }, [1] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], } }, [2] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, [3] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, [4] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, }; @@ -356,31 +354,35 @@ static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) return 0; } +static struct clk_ops clk_tin_ops = { + .set_parent = clk_pwm_tin_set_parent, +}; + static struct clk clk_tin[] = { [0] = { - .name = "pwm-tin", - .id = 0, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 0, + .ops = &clk_tin_ops, }, [1] = { - .name = "pwm-tin", - .id = 1, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 1, + .ops = &clk_tin_ops, }, [2] = { - .name = "pwm-tin", - .id = 2, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 2, + .ops = &clk_tin_ops, }, [3] = { - .name = "pwm-tin", - .id = 3, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 3, + .ops = &clk_tin_ops, }, [4] = { - .name = "pwm-tin", - .id = 4, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 4, + .ops = &clk_tin_ops, }, }; @@ -428,25 +430,15 @@ __init void s3c_pwmclk_init(void) return; } - for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) clk_timer_scaler[clk].parent = clk_timers; - ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); - return; - } - } - for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { - ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pww tclk%d\n", clk); - return; - } - } + s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler)); + s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk)); for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { ret = clk_pwm_tdiv_register(clk); + if (ret < 0) { printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); return; diff --git a/arch/arm/plat-stmp3xxx/clock.c b/arch/arm/plat-stmp3xxx/clock.c index 5d2f19a09e44..e593a2a801c6 100644 --- a/arch/arm/plat-stmp3xxx/clock.c +++ b/arch/arm/plat-stmp3xxx/clock.c @@ -1126,9 +1126,8 @@ static int __init clk_init(void) if (ops && ops->set_parent) ops->set_parent(cl->clk, cl->clk->parent); } - - clkdev_add(cl); } + clkdev_add_table(onchip_clks, ARRAY_SIZE(onchip_clks)); return 0; } |