diff options
Diffstat (limited to 'arch/arm/mach-stmp378x')
37 files changed, 7295 insertions, 137 deletions
diff --git a/arch/arm/mach-stmp378x/Kconfig b/arch/arm/mach-stmp378x/Kconfig new file mode 100644 index 000000000000..e0176671985b --- /dev/null +++ b/arch/arm/mach-stmp378x/Kconfig @@ -0,0 +1,59 @@ +if ARCH_STMP378X + +config FB_STMP37XX_HX8238A + depends on ARCH_STMP37XX + bool "HX8238A" + ---help--- + Use HX8238A dotclock LCD panel for STMP37XX + +config FB_STMP37XX_LMS350 + depends on ARCH_STMP378X + bool "LMS350" + ---help--- + Use LMS350 dotclock LCD panel for STMP378X + +config FB_STMP37XX_LMS430 + depends on ARCH_STMP378X + bool "LMS430" + ---help--- + Use LMS430 dotclock LCD panel for STMP378X + +config FB_STMP378X_TVENC + depends on ARCH_STMP378X + bool "TVENC" + ---help--- + Use TVOUT encoder for STMP378X + +config STMP3XXX_UNIQUE_ID + bool "Support for UniqueID on boot media" + default y + +config STMP3XXX_UNIQUE_ID_OTP + bool "UniqueID on OTP" + depends on STMP3XXX_UNIQUE_ID + default y + +config STMP378X_RAM_FREQ_SCALING + bool "RAM frequency scaling support" + depends on ARCH_STMP378X + default y + +choice + prompt "Select STMP378x RAM chip" + depends on STMP378X_RAM_FREQ_SCALING + +config STMP378X_RAM_MDDR + bool "mDDR SDRAM" +config STMP378X_RAM_DDR + bool "DDR SDRAM" + +endchoice + +config DMA_ZONE_SIZE + int "DMA memory zone size" + range 0 32 + default 12 + help + This is the size in MB for the DMA zone. The DMA zone is used for + dedicated memory for large contiguous video buffers +endif diff --git a/arch/arm/mach-stmp378x/Makefile b/arch/arm/mach-stmp378x/Makefile index d156f76b379f..df1f08140638 100644 --- a/arch/arm/mach-stmp378x/Makefile +++ b/arch/arm/mach-stmp378x/Makefile @@ -1,2 +1,29 @@ -obj-$(CONFIG_ARCH_STMP378X) += stmp378x.o -obj-$(CONFIG_MACH_STMP378X) += stmp378x_devb.o +# +# Makefile for the linux kernel. +# +obj-y += power.o persistent.o + +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + +# Chip family select +obj-$(CONFIG_ARCH_STMP37XX) += stmp37xx.o stmp37xx_lcdif.o +obj-$(CONFIG_ARCH_STMP378X) += stmp378x.o stmp378x_lcdif.o + +obj-$(CONFIG_MACH_STMP37XX) += stmp37xx_devb.o +obj-$(CONFIG_MACH_STMP378X) += stmp378x_devb.o stmp378x_i2c.o stmp378x_pwm_led.o + +# LCD panels support +obj-$(CONFIG_FB_STMP37XX_HX8238A) += lcd_hx8238a.o +obj-$(CONFIG_FB_STMP37XX_LMS350) += lcd_lms350.o +obj-$(CONFIG_FB_STMP37XX_LMS430) += lcd_lms430.o + +# TVOUT support +obj-$(CONFIG_FB_STMP378X_TVENC) += tvenc.o + +obj-$(CONFIG_STMP3XXX_UNIQUE_ID_OTP) += otp.o +obj-$(CONFIG_STMP3XXX_UNIQUE_ID_FILE) += unique-id-file.o + +obj-$(CONFIG_INPUT_STMP3XXX_ROTDEC) += stmp378x_devb_rotdec.o +obj-$(CONFIG_STMP378X_RAM_FREQ_SCALING) += emi.o + diff --git a/arch/arm/mach-stmp378x/emi.S b/arch/arm/mach-stmp378x/emi.S new file mode 100644 index 000000000000..2b7625597ed3 --- /dev/null +++ b/arch/arm/mach-stmp378x/emi.S @@ -0,0 +1,153 @@ +/* + * Freescale STMP378X low level RAM frequency manipulation + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/hardware.h> +#include <asm/system.h> +#include <asm/pgtable-hwdef.h> +#include <mach/regs-power.h> +#include <mach/regs-clkctrl.h> + +#define SCALING_DATA_EMI_DIV_OFFSET 0 +#define SCALING_DATA_FRAC_DIV_OFFSET 4 +#define SCALING_DATA_CUR_FREQ_OFFSET 8 +#define SCALING_DATA_NEW_FREQ_OFFSET 12 + +.global cpu_arm926_switch_mm + +.align 8 +ENTRY(stmp3xxx_ram_freq_scale) + stmfd sp!, {r1 - r9, lr} + + ldr r5, [r0, #SCALING_DATA_NEW_FREQ_OFFSET] + ldr r6, [r0, #SCALING_DATA_CUR_FREQ_OFFSET] + ldr r7, [r0, #SCALING_DATA_EMI_DIV_OFFSET] + ldr r8, [r0, #SCALING_DATA_FRAC_DIV_OFFSET] + + adr r9, __stmp_temp_stack + + @ clean cache + ldr r1, __stmp_flush_cache_addr + mov lr, pc + mov pc, r1 + + @ put DRAM into self refresh + ldr r0, __stmp_dram_ctl00 + ldr r1, [r0, #0x20] + orr r1, r1, #(1 << 8) + str r1, [r0, #0x20] + @ wait for it to actually happen + ldr r0, __stmp_dram_emi00 +1: ldr r1, [r0, #0x10] + tst r1, #(1 << 1) + beq 1b + nop + + @ prepare for change + cmp r5, #24 + bgt 2f + bl stmp3xxx_ram_24M_set_timings + b 100f +2: cmp r5, #48 + bgt 3f + bl stmp3xxx_ram_48M_set_timings + b 100f +3: cmp r5, #60 + bgt 4f + bl stmp3xxx_ram_60M_set_timings + b 100f +4: cmp r5, #80 + bgt 5f + bl stmp3xxx_ram_80M_set_timings + b 100f +5: cmp r5, #96 + bgt 6f + bl stmp3xxx_ram_96M_set_timings + b 100f +6: cmp r5, #120 + bgt 7f + bl stmp3xxx_ram_120M_set_timings + b 100f +7: cmp r5, #133 + bgt 8f + bl stmp3xxx_ram_133M_set_timings + b 100f +8: bl stmp3xxx_ram_150M_set_timings + +100: + @ RAM to clk from xtal + mov r0, #(HW_CLKCTRL_CLKSEQ & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0xFF000000) + mov r1, #(1<<6) + str r1, [r0, #4] + mov r0, #(HW_CLKCTRL_EMI & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_EMI & 0xFF000000) +101: ldr r1, [r0] + tst r1, #BM_CLKCTRL_EMI_BUSY_REF_XTAL + bne 101b + + bl __stmp_emi_set_values + + @ EMI back to PLL + mov r0, #(HW_CLKCTRL_CLKSEQ & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0xFF000000) + mov r1, #(1<<6) + str r1, [r0, #8] + + mov r0, #(HW_CLKCTRL_EMI & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_EMI & 0xFF000000) +1: ldr r1, [r0] + tst r1, #BM_CLKCTRL_EMI_BUSY_REF_EMI + bne 1b + bic r1, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE + str r1, [r0] + + @ restore normal DRAM mode + ldr r0, __stmp_dram_ctl00 + ldr r1, [r0, #0x20] + bic r1, r1, #(1 << 8) + str r1, [r0, #0x20] + @ wait for it to actually happen + ldr r0, __stmp_dram_emi00 +102: ldr r1, [r0, #0x10] + tst r1, #(1 << 1) + bne 102b + + @ restore regs and return + ldmfd sp!, {r1 - r9, lr} + mov pc, lr + + .space 0x100 +__stmp_temp_stack: + .word 0 + +#include "emi.inc" + +__stmp_flush_cache_addr: + .word arm926_flush_kern_cache_all + +ENTRY(stmp3xxx_ram_funcs_sz) + .word . - stmp3xxx_ram_freq_scale diff --git a/arch/arm/mach-stmp378x/emi.inc b/arch/arm/mach-stmp378x/emi.inc new file mode 100644 index 000000000000..0e003c994a2b --- /dev/null +++ b/arch/arm/mach-stmp378x/emi.inc @@ -0,0 +1,707 @@ +/* + * Freescale STMP378X low level RAM timings tables for Micron mDDR + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +__stmp_emi_set_values: + stmfd r9!, {r0 - r4, lr} + mov r1, #(HW_CLKCTRL_EMI & 0x000000FF) + orr r1, r1, #(HW_CLKCTRL_EMI & 0x0000FF00) + orr r1, r1, #(HW_CLKCTRL_EMI & 0x00FF0000) + orr r1, r1, #(HW_CLKCTRL_EMI & 0xFF000000) + + mov r3, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE + + mov r0, #(HW_CLKCTRL_FRAC & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0xFF000000) + ldr r2, [r0] + + and r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC + lsr r4, r4, #8 + /* new pll div > cur pll div? */ + cmp r4, r8 + bgt 1f + bic r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC + orr r4, r4, r8, lsl #8 + str r4, [r0] + nop + nop + nop + +1: ldr r4, [r1] + and r4, r4, #BM_CLKCTRL_EMI_DIV_EMI + /* new emi div > cur emi div? */ + cmp r4, r7 + bgt 2f + mov r4, r7 + orr r4, r4, #0x100 + orr r4, r4, r3 + str r4, [r1] +11: ldr r4, [r1] + tst r4, #BM_CLKCTRL_EMI_BUSY_REF_EMI + bne 11b + tst r4, #BM_CLKCTRL_EMI_BUSY_REF_XTAL + bne 11b + tst r4, #BM_CLKCTRL_EMI_BUSY_DCC_RESYNC + bne 11b + +2: ldr r2, [r0] + + and r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC + lsr r4, r4, #8 + /* new pll div != cur pll div? */ + cmp r4, r8 + beq 3f + bic r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC + orr r4, r4, r8, lsl #8 + str r4, [r0] + nop + nop + nop + +3: ldr r4, [r1] + and r4, r4, #BM_CLKCTRL_EMI_DIV_EMI + /* new emi div != cur emi div? */ + cmp r4, r7 + beq 4f + mov r4, r7 + orr r4, r4, #0x100 + orr r4, r4, r3 + str r4, [r1] +31: ldr r4, [r1] + tst r4, #BM_CLKCTRL_EMI_BUSY_REF_EMI + bne 31b + tst r4, #BM_CLKCTRL_EMI_BUSY_REF_XTAL + bne 31b + tst r4, #BM_CLKCTRL_EMI_BUSY_DCC_RESYNC + bne 31b + +4: ldmfd r9!, {r0 - r4, lr} + mov pc, lr + +stmp3xxx_ram_24M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_24M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_48M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_48M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_60M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_60M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_80M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_80M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_96M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_96M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_120M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_120M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_133M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_133M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + str r3, [r0, r2, lsl #2] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +stmp3xxx_ram_150M_set_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_150M_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + str r3, [r0, r2, lsl #2] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +__stmp_dram_ctl00: + .word IO_ADDRESS(0x800E0000) +__stmp_dram_emi00: + .word IO_ADDRESS(0x80020000) +__stmp_power_vdddctrl: + .word IO_ADDRESS(0x80044040) + +stmp3xxx_ram_save_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_saved_values +1: ldr r2, [r1] + mov r4, r2, lsl #2 + ldr r3, [r0, r4] + str r3, [r1, #4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +#ifdef CONFIG_STMP378X_RAM_MDDR +__stmp_dram_24M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 12 + .word 0x02010002 + .word 13 + .word 0x06060a02 + .word 15 + .word 0x01030000 + .word 17 + .word 0x2d000102 + .word 18 + .word 0x20200000 + .word 19 + .word 0x027f1414 + .word 20 + .word 0x01021608 + .word 21 + .word 0x00000002 + .word 26 + .word 0x000000b3 + .word 32 + .word 0x00030687 + .word 33 + .word 0x00000003 + .word 34 + .word 0x000012c1 + .word 40 + .word 0x00010000 + +__stmp_dram_48M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 13 + .word 0x06060a02 + .word 12 + .word 0x02010002 + .word 15 + .word 0x02040000 + .word 17 + .word 0x2d000104 + .word 18 + .word 0x1f1f0000 + .word 19 + .word 0x027f0a0a + .word 20 + .word 0x02030a10 + .word 21 + .word 0x00000004 + .word 26 + .word 0x0000016f + .word 32 + .word 0x00060d17 + .word 33 + .word 0x00000006 + .word 34 + .word 0x00002582 + .word 40 + .word 0x00020000 + +__stmp_dram_60M_values: +__stmp_dram_80M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 12 + .word 0x02020002 + .word 13 + .word 0x06060a02 + .word 15 + .word 0x02040000 + .word 17 + .word 0x2d000005 + .word 18 + .word 0x1f1f0000 + .word 19 + .word 0x027f0a0a + .word 20 + .word 0x02040a10 + .word 21 + .word 0x00000006 + .word 26 + .word 0x000001cc + .word 32 + .word 0x00081060 + .word 33 + .word 0x00000008 + .word 34 + .word 0x00002ee5 + .word 40 + .word 0x00020000 + +__stmp_dram_96M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 12 + .word 0x02020002 + .word 13 + .word 0x06070a02 + .word 15 + .word 0x03050000 + .word 17 + .word 0x2d000808 + .word 18 + .word 0x1f1f0000 + .word 19 + .word 0x020c1010 + .word 20 + .word 0x0305101c + .word 21 + .word 0x00000007 + .word 26 + .word 0x000002e6 + .word 32 + .word 0x000c1a3b + .word 33 + .word 0x0000000c + .word 34 + .word 0x00004b0d + .word 40 + .word 0x00030000 + +__stmp_dram_120M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 12 + .word 0x02020002 + .word 13 + .word 0x06070a02 + .word 15 + .word 0x03050000 + .word 17 + .word 0x2300080a + .word 18 + .word 0x1f1f0000 + .word 19 + .word 0x020c1010 + .word 20 + .word 0x0306101c + .word 21 + .word 0x00000009 + .word 26 + .word 0x000003a1 + .word 32 + .word 0x000f20ca + .word 33 + .word 0x0000000f + .word 34 + .word 0x00005dca + .word 40 + .word 0x00040000 + +__stmp_dram_133M_values: +__stmp_dram_150M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 12 + .word 0x02020002 + .word 13 + .word 0x06070a02 + .word 15 + .word 0x03050000 + .word 17 + .word 0x2000080a + .word 18 + .word 0x1f1f0000 + .word 19 + .word 0x020c1010 + .word 20 + .word 0x0306101c + .word 21 + .word 0x0000000a + .word 26 + .word 0x00000408 + .word 32 + .word 0x0010245f + .word 33 + .word 0x00000010 + .word 34 + .word 0x00006808 + .word 40 + .word 0x00040000 + +#elif CONFIG_STMP378X_RAM_DDR +/* XXX: not quite ready yet */ +__stmp_dram_24M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 11 + .word 0x00070206 + .word 12 + .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x01020000 @ t_rp 1, t_dal 2 + .word 17 + .word 0x3d000302 @ t_rc 2 + .word 20 + .word 0x01020508 + .word 21 + .word 0x00000002 @ t_rfc 2 + .word 26 + .word 0x000000b3 /* 0xd20 */ @ t_ref + .word 32 + .word 0x00020690 @ t_xsnr 2, t_rasmax 0x690 + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x000012c1 @ t_init + .word 40 + .word 0x00010000 + +@ not yet +__stmp_dram_48M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 11 + .word 0x00070206 + .word 12 + .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x01020000 @ t_rp 1, t_dal 2 + .word 17 + .word 0x39000104 @ t_rc 4 + .word 19 + .word 0x027f1010 + .word 20 + .word 0x02030a10 + .word 21 + .word 0x00000004 @ t_rfc + .word 26 + .word 0x00000173 /* 0x1a42 */ @ t_ref + .word 32 + .word 0x00040d21 @ t_xsnr 4, t_rasmax 0xd21 + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00002586 @ t_init + .word 40 + .word 0x00010000 + +__stmp_dram_60M_values: + .word 4 + .word 0x01000101 + .word 7 + .word 0x01000101 + .word 11 + .word 0x00070206 + .word 12 + .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x01020000 @ t_rp 1, t_dal 2 + .word 17 + .word 0x3d000105 @ t_rc 5 + .word 19 + .word 0x027f1313 + .word 20 + .word 0x01031523 @ t_rcd 1, t_rasmin 3 + .word 21 + .word 0x00000005 @ t_rfc 5 + .word 26 + .word 0x000001cc /* 0x20cd */ @ t_ref + .word 32 + .word 0x00051068 @ t_xsnr 5, t_rasmax 0x1068 + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00002ee5 @ t_init + .word 40 + .word 0x00010000 + +__stmp_dram_80M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 11 + .word 0x00070206 + .word 12 + .word 0x02010000 @ t_wr 2, t_rrd 1, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x02040000 @ t_rp 2, t_dal 4 + .word 17 + .word 0x20001c05 @ dll_start_point 0x20, dll_increment 0x1c, t_rc 5 + .word 19 + .word 0x027f1313 + .word 20 + .word 0x02041522 @ t_rcd 2, t_rasmin 4, wr_dqs_shift 0x22 + .word 21 + .word 0x00000006 @ t_rfc 6 + .word 26 + .word 0x00000269 @ t_ref + .word 32 + .word 0x000615d6 @ t_xsnr 6, t_rasmax 0x15d6 + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00003e80 @ t_init + .word 40 + .word 0x00010000 + +__stmp_dram_96M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 11 + .word 0x00070206 + .word 12 + .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x02040000 @ t_rp 2, t_dal 4 + .word 17 + .word 0x2f001706 @ dll_start_point 0x2f, dll_increment 0x17, t_rc 6 + .word 19 + .word 0x027f1a1a + .word 20 + .word 0x02051c21 @ t_rcd 2, t_rasmin 5, wr_dqs_shift 0x22 + .word 21 + .word 0x00000007 @ t_rfc 7 + .word 26 + .word 0x000002e6 /* 0x347b */ @ t_ref + .word 32 + .word 0x00081a3e @ t_xsnr 8, t_rasmax 0x1a3e + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00004b0d @ t_init + .word 40 + .word 0x00010000 + +__stmp_dram_120M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 11 + .word 0x00070206 + .word 12 + .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0 + .word 13 + .word 0x04040a01 @ t_wtr 1 + .word 15 + .word 0x02040000 @ t_rp 2, t_dal 4 + .word 17 + .word 0x26001308 @ dll_start_point 0x26, dll_increment 0x13, t_rc 8 + .word 19 + .word 0x027f1a1a + .word 20 + .word 0x02061c23 @ t_rcd 2, t_rasmin 6 + .word 21 + .word 0x00000009 @ t_rfc 9 + .word 26 + .word 0x000003a1 /* 0x41a6 */ @ t_ref + .word 32 + .word 0x000a20ca @ t_xsnr 9, t_rasmax 0x20ca + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00005dca @ t_init + .word 40 + .word 0x00010000 + +__stmp_dram_133M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 11 + .word 0x00070204 + .word 12 + .word 0x02020000 + .word 13 + .word 0x04040a01 + .word 15 + .word 0x02040000 + .word 17 + .word 0x19000f08 @ t_rc 0xa + .word 19 + .word 0x02021313 + .word 20 + .word 0x02061521 + .word 21 + .word 0x0000000a + .word 26 + .word 0x000003f7 /* 0x48b9 */ + .word 32 + .word 0x000a23cd + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00006665 + .word 40 + .word 0x00010000 + +__stmp_dram_150M_values: + .word 4 + .word 0x00000101 + .word 7 + .word 0x01000001 + .word 11 + .word 0x00070206 + .word 12 + .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0 + .word 13 + .word 0x05050a02 @ t_wtr 2 + .word 15 + .word 0x03060000 @ t_rp 3, t_dal 6 + .word 17 + .word 0x18000d0c @ dll_start_point 0x18, dll_increment 0xd, t_rc 0xc + .word 19 + .word 0x027f0f0f + .word 20 + .word 0x03071121 @ t_rcd 3, t_rasmin 7 + .word 21 + .word 0x0000000c @ t_rfc 0xc + .word 26 + .word 0x000001cc /* 0x20cd */ @ t_ref + .word 32 + .word 0x000c2860 @ t_xsnr 0xc, t_rasmax 0x2860 + .word 33 + .word 0x000000c8 @ t_xsr 0xc8 + .word 34 + .word 0x00007554 @ t_init + .word 40 + .word 0x00010000 + +#else +#error RAM chip not defined +#endif + +stmp3xxx_ram_restore_timings: + ldr r0, __stmp_dram_ctl00 + adr r1, __stmp_dram_saved_values +1: ldr r2, [r1] + ldr r3, [r1, #4] + mov r4, r2, lsl #2 + str r3, [r0, r4] + add r1, r1, #8 + cmp r2, #40 + bne 1b + mov pc, lr + +__stmp_dram_saved_values: + .word 4 + .word 0 + .word 7 + .word 0 + .word 12 + .word 0 + .word 13 + .word 0 + .word 15 + .word 0 + .word 17 + .word 0 + .word 18 + .word 0 + .word 19 + .word 0 + .word 20 + .word 0 + .word 21 + .word 0 + .word 26 + .word 0 + .word 32 + .word 0 + .word 33 + .word 0 + .word 34 + .word 0 + .word 40 + .word 0 diff --git a/arch/arm/mach-stmp378x/include/mach/ddi_bc.h b/arch/arm/mach-stmp378x/include/mach/ddi_bc.h new file mode 100644 index 000000000000..2018d167aeda --- /dev/null +++ b/arch/arm/mach-stmp378x/include/mach/ddi_bc.h @@ -0,0 +1,736 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +//////////////////////////////////////////////////////////////////////////////// +//! \addtogroup ddi_bc +//! @{ +// +// Copyright (c) 2004-2005 SigmaTel, Inc. +// +//! \file ddi_bc.h +//! \brief Header file for the Battery Charger device driver. +//! \date 06/2005 +//! +//! This file contains externally visible declarations for the Battery Charger +//! device driver. +//! +//! \see ddi_bc.c and related files. +//! \todo [PUBS] Add definitions for TBDs in this file. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _DDI_BC_H +#define _DDI_BC_H + +#include <linux/types.h> + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// +#define DDI_BC_MAX_RESTART_CYCLES 100 + +#define DDI_BC_LIION_CHARGING_VOLTAGE 4200 +#define DDI_BC_ALKALINE_NIMH_CHARGING_VOLTAGE 1750 + +//! \brief Defines battery charger states. +typedef enum _ddi_bc_State { + //! \brief TBD + DDI_BC_STATE_UNINITIALIZED = 0, + //! \brief TBD + DDI_BC_STATE_BROKEN = 1, + //! \brief TBD + DDI_BC_STATE_DISABLED = 2, + //! \brief TBD + DDI_BC_STATE_WAITING_TO_CHARGE = 3, + //! \brief TBD + DDI_BC_STATE_CONDITIONING = 4, + //! \brief TBD + DDI_BC_STATE_CHARGING = 5, + //! \brief TBD + DDI_BC_STATE_TOPPING_OFF = 6, + //! \brief TBD + DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE = 7, + +} ddi_bc_State_t; + +typedef enum _ddi_bc_BrokenReason { + //! \brief TBD + DDI_BC_BROKEN_UNINITIALIZED = 0, + //! \brief TBD + DDI_BC_BROKEN_CHARGING_TIMEOUT = 1, + //! \brief TBD + DDI_BC_BROKEN_FORCED_BY_APPLICATION = 2, + //! \brief TBD + DDI_BC_BROKEN_EXTERNAL_BATTERY_VOLTAGE_DETECTED = 3, + //! \brief TBD + DDI_BC_BROKEN_NO_BATTERY_DETECTED = 4, + +} ddi_bc_BrokenReason_t; + +//! \brief Defines the battery charger configuration. +typedef struct _ddi_bc_Cfg { + //! \brief Units in milliseconds. + //! + //! This field configures the expected period between calls to + //! ddi_bc_StateMachine. If die temperature monitoring is + //! enabled, then the data sheet recommends the period be around + //! 100ms or less. + //! + //! Note that this period defines the minimum time resolution of + //! the battery charger. + + uint32_t u32StateMachinePeriod; + + //! \brief Units in mA/s. + //! + //! This field configures the slope of the current ramp. Any + //! time the battery charger increases its current draw, it will + //! ramp up the current no faster than this rate. + //! + //! Note that the minimum time resolution of the battery charger + //! is the configured period between calls to advance the state + //! machine. Also, the hardware has a minimum current resolution + //! of 10mA. If the given ramp slope cannot be expressed + //! exactly, then the largest expressible smaller slope will be + //! the result. If the actual period between calls to + //! ddi_bc_StateMachine is irregular, the current may ramp faster + //! than indicated. + + uint16_t u16CurrentRampSlope; + + //! \brief Units in millivolts. + //! + //! This field configures the threshold conditioning voltage. If + //! the battery’s voltage is below this value, it will be + //! conditioned until its voltage rises above the maximum + //! conditioning voltage. After that, the battery will be + //! charged normally. + //! + //! Note that the hardware has a minimum resolution of 8mV. If + //! the given voltage cannot be expressed exactly, then the + //! smallest expressible larger value will be used. + + uint16_t u16ConditioningThresholdVoltage; + + //! \brief Units in millivolts. + //! + //! This field configures the maximum conditioning voltage. If + //! the battery charger is conditioning a battery, normal + //! charging begins when the voltage rises above this value. + //! + //! This value should be slightly higher than the threshold + //! conditioning voltage because it is measured while a + //! conditioning current is actually flowing to the battery. + //! With a conditioning current of 0.1C, reasonable values for + //! the threshold and maximum conditioning voltages are 2.9V + //! and 3.0V respectively. + //! + //! Note that the hardware has a minimum resolution of 8mV. If + //! the given voltage cannot be expressed exactly, then the + //! smallest expressible larger value will be used. + + uint16_t u16ConditioningMaxVoltage; + + //! \brief Units in milliamps. + //! + //! This field configures the maximum conditioning current. + //! This is the maximum current that will be offered to a + //! battery while it is being conditioned. A typical value is + //! 0.1C. + //! + //! Note that the hardware has a minimum resolution of 10mA + //! (see the data sheet for details). If the given current + //! cannot be expressed exactly, then the largest expressible + //! smaller value will be used. + + uint16_t u16ConditioningCurrent; + + //! \brief Units in milliseconds. + //! + //! This field configures the conditioning time-out. This is + //! the maximum amount of time that a battery will be + //! conditioned before the battery charger declares it to be + //! broken. + //! + //! Note that the minimum time resolution of the battery + //! charger is the configured period between calls to advance + //! the state machine. If the given time-out cannot be + //! expressed exactly, then the shortest expressible longer + //! value will be used. + + uint32_t u32ConditioningTimeout; + + //! \brief Units in millivolts. + //! + //! This field configures the final charging voltage. At this + //! writing, only two values are permitted: 4100 or 4200. + + uint16_t u16ChargingVoltage; + + //! \brief Units in milliamps. + //! + //! This field configures the maximum current offered to a + //! charging battery. + //! + //! Note that the hardware has a minimum resolution of 10mA + //! (see the data sheet for details). If the given current + //! cannot be expressed exactly, then the largest expressible + //! smaller value will be used. + + uint16_t u16ChargingCurrent; + + //! \brief Units in milliamps. + //! + //! This field configures the current flow below which a + //! charging battery is regarded as fully charged (typical + //! 0.1C). At this point, the battery will be topped off. + //! + //! Note that the hardware has a minimum resolution of 10mA + //! (see the data sheet for details). If the given current + //! cannot be expressed exactly, then the largest expressible + //! smaller value will be used. + + uint16_t u16ChargingThresholdCurrent; + + //! \brief Units in milliamps. + //! + //! When charging while the DCDC converter's are enabled, the charger + //! is suppling current to both the battery and the Vbat input of the + //! DCDC converter. Once the total battery charger current falls + //! below this level, the charger will then stop charging until the + //! the battery voltage reaches the BC_LOW_DCDCMODE_BATTERY_VOLTAGE + //! threshold or until the DCDCs are no longer enabled. + //! + //! Typically, this value should be left at 180 to avoid the risk + //! of topping off the battery too long in DCDC mode and avoid + //! exceeding the BC_CHARGING_TIMEOUT time which would put the charger + //! driver in the broken state and completely disable charging. + //! + //! Note that the hardware has a minimum resolution of 10mA + //! (see the data sheet for details). If the given current + //! cannot be expressed exactly, then the largest expressible + //! smaller value will be used. + uint16_t u16DdcdModeChargingThresholdCurrent; + + //! \brief Units in milliseconds. + //! + //! This field configures the charging time-out. This is the + //! maximum amount of time that a battery will be charged + //! before the battery charger declares it to be broken. + //! + //! Note that the minimum time resolution of the battery + //! charger is the configured period between calls to advance + //! the state machine. If the given time-out cannot be + //! expressed exactly, then the shortest expressible longer + //! value will be used. + + uint32_t u32ChargingTimeout; + + //! \brief Units in milliseconds. + //! + //! This field configures the top-off period. This is the + //! amount of time a battery will be held in the Topping Off + //! state before it is declared fully charged. + //! + //! Note that the minimum time resolution of the battery + //! charger is the configured period between calls to advance + //! the state machine. If the given time-out cannot be + //! expressed exactly, then the shortest expressible longer + //! value will be used. + + uint32_t u32TopOffPeriod; + + //! \brief Units in milliseconds. + //! + //! This field configures the top-off period when the DCDC + //! converters are enabled. To avoid topping off the LiIon + //! battery too long and reducing it's long term capacity, + //! This time should be kept failry short. + //! + //! Note that the minimum time resolution of the battery + //! charger is the configured period between calls to advance + //! the state machine. If the given time-out cannot be + //! expressed exactly, then the shortest expressible longer + //! value will be used. + uint32_t u32DcdcModeTopOffPeriod; + + //! \brief Causes the battery charger to use an externally generated bias current + //! + //! If cleared, this causes the battery charger to use an + //! externally generated bias current, which is expected to be + //! quite precise. Otherwise, the battery charger will + //! generate a lesser-quality bias current internally. + + uint8_t useInternalBias:1; + + //! \brief Indicates that the battery charger is to monitor the die temperature. + //! + //! If set, this field indicates that the battery charger is to + //! monitor the die temperature. See below for fields that + //! configure the details. + + uint8_t monitorDieTemp:1; + + //! \brief Indicates that the battery charger is to monitor the battery temperature. + //! + //! If set, this field indicates that the battery charger is to + //! monitor the battery temperature. See below for fields that + //! configure the details. + + uint8_t monitorBatteryTemp:1; + + //! \brief Units in degrees centigrade. + //! + //! Note that the hardware reports die temperature in ranges of + //! 10 degree resolution minimum (see the data sheet for + //! details). If the battery charger is monitoring the die + //! temperature, and it rises to a range that includes a + //! temperature greater than or equal to this value, the + //! charging current will be clamped to the safe current. + + int8_t u8DieTempHigh; + + //! \brief Units in degrees centigrade. + //! + //! Note that the hardware reports die temperature in ranges of + //! 10 degrees minimum (see the data sheet for details). If the + //! charging current is being clamped because of a high die + //! temperature, and it falls to a range that doesn’t include a + //! temperatures greater than or equal to this value, the + //! charging current clamp will be released. + + int8_t u8DieTempLow; + + //! \brief Units in milliamps. + //! + //! If the battery charger detects a high die temperature, it + //! will clamp the charging current at or below this value. + + uint16_t u16DieTempSafeCurrent; + + //! \brief If the battery charger is monitoring the battery + //! temperature, this field indicates the LRADC channel to + //! read. + + uint8_t u8BatteryTempChannel; + + //! \brief If the battery charger is monitoring the battery + //! temperature, and it rises to a measurement greater than or + //! equal to this value, the charging current will be clamped + //! to the corresponding safe current. + + uint16_t u16BatteryTempHigh; + + //! \brief If the charging current is being clamped because of a high + //! battery temperature, and it falls below this value, the + //! charging current clamp will be released. + + uint16_t u16BatteryTempLow; + + //! \brief Units in milliamps. + //! + //! If the battery charger detects a high battery temperature, + //! it will clamp the charging current at or below this value. + + uint16_t u16BatteryTempSafeCurrent; + + //! \brief Units in millivolts. + //! + //! In the WaitingToCharge state, if we are in DCDC + //! operating modes, if the battery voltage measurement + //! is below this value, we immediately proceed with charging. + //! the low criteria for this value is that it must be high + //! to not risk the battery voltage getting too low. The + //! upper criteria is that you do not want the IR voltage + //! drop under heavy loads to make you start charging too soon + //! because the goal in DCDC operating mode is to not be constantly + //! topping off the battery which can shorten its life + + uint16_t u16LowDcdcBatteryVoltage_mv; + + uint32_t u32StateMachineNonChargingPeriod; +} ddi_bc_Cfg_t; + +//! Status returned by Battery Charger functions. + +typedef enum _ddi_bc_Status { + //! \brief TBD + DDI_BC_STATUS_SUCCESS = 0, + //! \brief TBD + DDI_BC_STATUS_HARDWARE_DISABLED, + //! \brief TBD + DDI_BC_STATUS_BAD_BATTERY_MODE, + //! \brief TBD + DDI_BC_STATUS_CLOCK_GATE_CLOSED, + //! \brief TBD + DDI_BC_STATUS_NOT_INITIALIZED, + //! \brief TBD + DDI_BC_STATUS_ALREADY_INITIALIZED, + //! \brief TBD + DDI_BC_STATUS_BROKEN, + //! \brief TBD + DDI_BC_STATUS_NOT_BROKEN, + //! \brief TBD + DDI_BC_STATUS_NOT_DISABLED, + //! \brief TBD + DDI_BC_STATUS_BAD_ARGUMENT, + //! \brief TBD + DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL, + //! \brief TBD + DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE, +} ddi_bc_Status_t; + +///////////////////////////////////////////////////////////////////////////////// +// BCM Event Codes +// +// These are the codes that might be published to PMI Subscribers. +///////////////////////////////////////////////////////////////////////////////// + +#define DDI_BC_EVENT_GROUP (11<<10) + +//! \brief TBD +//! \todo [PUBS] Add definition(s)... +typedef enum { + // Use the error code group value to make events unique for the EOI + //! \brief TBD + ddi_bc_MinEventCode = DDI_BC_EVENT_GROUP, + //! \brief TBD + ddi_bc_WaitingToChargeCode, + //! \brief TBD + ddi_bc_State_ConditioningCode, + //! \brief TBD + ddi_bc_State_Topping_OffCode, + //! \brief TBD + ddi_bc_State_BrokenCode, + //! \brief TBD + ddi_bc_SettingChargeCode, + //! \brief TBD + ddi_bc_RaisingDieTempAlarmCode, + //! \brief TBD + ddi_bc_DroppingDieTempAlarmCode, + + //! \brief TBD + ddi_bc_MaxEventCode, + //! \brief TBD + ddi_bc_DcdcModeWaitingToChargeCode +} ddi_bc_Event_t; + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! \brief Initialize the Battery Charger. +//! +//! \fntype Function +//! +//! This function initializes the Battery Charger. +//! +//! \param[in] pCfg A pointer to the new configuration. +//! +//! \retval DDI_BC_STATUS_SUCCESS +//! If the operation succeeded. +//! \retval DDI_BC_STATUS_ALREADY_INITIALIZED +//! If the Battery Charger is already initialized. +//! \retval DDI_BC_STATUS_HARDWARE_DISABLED +//! If the Battery Charger hardware is disabled by a laser fuse. +//! \retval DDI_BC_STATUS_BAD_BATTERY_MODE +//! If the power supply is set up for a non-rechargeable battery. +//! \retval DDI_BC_STATUS_CLOCK_GATE_CLOSED +//! If the clock gate for the power supply registers is closed. +//! \retval DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE +//! If the charging voltage is not either 4100 or 4200. +//! \retval DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL +//! If the LRADC channel number for monitoring battery temperature +//! is bad. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_init.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_Init(ddi_bc_Cfg_t * pCfg); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the Battery Charger configuration. +//! +//! \fntype Function +//! +//! This function reports the Battery Charger configuration. +//! +//! Note that, if the Battery Charger has not yet been initialized, the data +//! returned by this function is unknown. +//! +//! \param[in,out] pCfg A pointer to a structure that will receive the data. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern void ddi_bc_QueryCfg(ddi_bc_Cfg_t * pCfg); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Shut down the Battery Charger. +//! +//! \fntype Function +//! +//! This function immediately shuts down the Battery Charger hardware and +//! returns the state machine to the Uninitialized state. Use this function to +//! safely “mummify” the battery charger before retiring it from memory. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern void ddi_bc_ShutDown(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Advances the state machine. +//! +//! \fntype Function +//! +//! This function advances the state machine. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_BROKEN If the battery violated a time-out +//! and has been declared broken. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_StateMachine(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Get the Battery Charger's current state. +//! +//! \fntype Function +//! +//! This function returns the current state. +//! +//! \retval The current state. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_State_t ddi_bc_GetState(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Disable the Battery Charger. +//! +//! \fntype Function +//! +//! This function forces the Battery Charger into the Disabled state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_SetDisable(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Enable the Battery Charger. +//! +//! \fntype Function +//! +//! If the Battery Charger is in the Disabled state, this function moves it to +//! the Waiting to Charge state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_NOT_DISABLED If the Battery Charger is not +//! disabled. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_SetEnable(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Declare the battery to be broken. +//! +//! \fntype Function +//! +//! This function forces the Battery Charger into the Broken state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_SetBroken(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Declare the battery to be fixed. +//! +//! \fntype Function +//! +//! If the Battery Charger is in the Broken state, this function moves it to +//! the Disabled state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_NOT_BROKEN If the Battery Charger is not broken. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_SetFixed(void); +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the current limit. +//! +//! \fntype Function +//! +//! This function applies a limit to the current that the Battery Charger can +//! draw. +//! +//! \param[in] u16Limit The maximum current the Battery Charger can draw +//! (in mA). +//! +//! \retval The expressible version of the limit. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern uint16_t ddi_bc_SetCurrentLimit(uint16_t u16Limit); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the current limit. +//! +//! \fntype Function +//! +//! This function reports the limit to the current that the Battery Charger can +//! draw. +//! +//! \retval The current limit. +//! +//! \internal +//! \see To view the function definition, see ddi_bc_api.c. +//////////////////////////////////////////////////////////////////////////////// +extern uint16_t ddi_bc_GetCurrentLimit(void); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the current threshold. +//! +//! \fntype Function +//! +//! +//! \param[in] u16Current Current threshold where charger deactivates (in mA). +//! +//! +//////////////////////////////////////////////////////////////////////////////// +extern uint16_t ddi_bc_SetCurrentThreshold(uint16_t u16Current); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the battery charger state machine period. +//! +//! \fntype Function +//! +//! This function sets a new state machine period. The Period and Slope should +//! be coordinated to achieve the minimal ramp step current which will minimize +//! transients on the system. +//! +//! \param[in] u32StateMachinePeriod (in milliseconds) +//! \param[in] u16CurrentRampSlope (in mA/s) +//! +//! \retval SUCCESS If all goes well +//! \retval ERROR_DDI_BCM_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//////////////////////////////////////////////////////////////////////////////// +extern ddi_bc_Status_t ddi_bc_SetNewPeriodAndSlope(uint32_t + u32StateMachinePeriod, + uint16_t + u16CurrentRampSlope); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the state machine period. +//! +//! \fntype Function +//! +//! This function reports the battery charger period. +//! +//! \retval The battery charger period (in milliseconds). +//! +//////////////////////////////////////////////////////////////////////////////// +extern uint32_t ddi_bc_GetStateMachinePeriod(void); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the current ramp slope. +//! +//! \fntype Function +//! +//! This function reports the current ramp slope. +//! +//! \retval The current ramp slope (in mA/s). +//! +//////////////////////////////////////////////////////////////////////////////// +extern uint32_t ddi_bc_GetCurrentRampSlope(void); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the time spent in the present state (milliseconds) +//! +//! \fntype Function +//! +//! This function reports the time spent in the present charging state. Note that +//! for the states that actually charge the battery, this time does not include the +//! time spent under alarm conditions such as die termperature alarm or battery +//! temperature alarm. +//! +//! \retval The time spent in the current state in milliseconds. +//! +//////////////////////////////////////////////////////////////////////////////// +uint32_t ddi_bc_GetStateTime(void); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the reason for being in the broken state +//! +//! \fntype Function +//! +//! +//! \retval ddi_bc_BrokenReason_t enumeration +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_BrokenReason_t ddi_bc_GetBrokenReason(void); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Restart the charge cycle +//! +//! \fntype Function +//! +//! \retval SUCCESS +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_ForceChargingToStart(void); + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +#endif // _DDI_BC_H +//! @} diff --git a/arch/arm/mach-stmp378x/include/mach/i2c.h b/arch/arm/mach-stmp378x/include/mach/i2c.h new file mode 100644 index 000000000000..05a57f6351e3 --- /dev/null +++ b/arch/arm/mach-stmp378x/include/mach/i2c.h @@ -0,0 +1,48 @@ +/* + * Freescale STMP378X I2C low-level/dma functions + * + * Author: Dmitrij Frasenyak <sed@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef _ARM_ARCH_I2C_H +#define _ARM_ARCH_I2C_H + +#include <linux/device.h> +#include <linux/module.h> + +#include <linux/completion.h> +#include <linux/i2c.h> + +#define I2C_READ 1 +#define I2C_WRITE 0 + +void hw_i2c_clear_dma_interrupt(void); +int hw_i2c_init(struct device *dev); +void hw_i2c_stop(struct device *dev); +void hw_i2c_setup_write(u8 addr, void *buff, int len, int flags); +void hw_i2c_setup_read(u8 addr, void *buff, int len, int flags); +void hw_i2c_run(int dir); +void hw_i2c_reset_dma(void); +void hw_i2c_finish_read(void *buff, int len); + +struct stmp378x_i2c_dev { + struct device *dev; + int irq_dma; + int irq_err; + struct completion cmd_complete; + u32 cmd_err; + struct i2c_adapter adapter; +}; + +#endif diff --git a/arch/arm/mach-stmp378x/include/mach/lcdif.h b/arch/arm/mach-stmp378x/include/mach/lcdif.h new file mode 100644 index 000000000000..ef5647a7a618 --- /dev/null +++ b/arch/arm/mach-stmp378x/include/mach/lcdif.h @@ -0,0 +1,497 @@ +/* + * Freescale STMP378X LCDIF interfaces + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef _ARCH_ARM_LCDIF_H +#define _ARCH_ARM_LCDIF_H + +#include <linux/types.h> +#include <linux/fb.h> +#include <linux/list.h> +#include <linux/backlight.h> +#include <linux/dma-mapping.h> +#include <linux/regulator/consumer.h> +#include <mach/dma.h> +#include <mach/platform.h> + +#include "regs-lcdif.h" +#include "regs-apbh.h" + +enum { + SPI_MOSI = 0, + SPI_SCLK, + SPI_CS, +}; + +struct stmp3xxx_lcd_dma_chain_info { + dma_addr_t *dma_addr_p; + unsigned offset; +}; + +enum { + STMP3XXX_LCD_PANEL_SYSTEM = 0, + STMP3XXX_LCD_PANEL_VSYNC, + STMP3XXX_LCD_PANEL_DOTCLK, + STMP3XXX_LCD_PANEL_DVI, +}; + +struct stmp3xxx_platform_bl_data; +struct stmp3xxx_platform_fb_entry { + char name[16]; + u16 x_res; + u16 y_res; + u16 bpp; + u32 cycle_time_ns; + int lcd_type; + int (*init_panel) (struct device * dev, dma_addr_t phys, int memsize, + struct stmp3xxx_platform_fb_entry * pentry); + void (*release_panel) (struct device * dev, + struct stmp3xxx_platform_fb_entry * pentry); + int (*blank_panel) (int blank); + void (*run_panel) (void); + void (*stop_panel) (void); + int (*pan_display) (dma_addr_t phys); + int (*update_panel) (void *p, + struct stmp3xxx_platform_fb_entry * pentry); + struct list_head link; + struct stmp3xxx_platform_bl_data *bl_data; +}; + +struct stmp3xxx_platform_fb_data { + struct list_head list; + struct stmp3xxx_platform_fb_entry *cur; +}; + +#define STMP3XXX_LCDIF_PANEL_INIT 1 +#define STMP3XXX_LCDIF_PANEL_RELEASE 2 + +struct stmp3xxx_platform_bl_data { + struct list_head list; + struct regulator *regulator; + int bl_gpio; + int bl_max_intensity; + int bl_cons_intensity; + int bl_default_intensity; + int (*init_bl) (struct stmp3xxx_platform_bl_data * data); + int (*set_bl_intensity) (struct stmp3xxx_platform_bl_data * data, + struct backlight_device * bd, int suspended); + void (*free_bl) (struct stmp3xxx_platform_bl_data * data); +}; + +static inline void stmp3xxx_lcd_register_entry(struct stmp3xxx_platform_fb_entry + *pentry, + struct stmp3xxx_platform_fb_data + *pdata) +{ + list_add_tail(&pentry->link, &pdata->list); + if (!pdata->cur) + pdata->cur = pentry; +} + +static inline void stmp3xxx_lcd_move_pentry_up(struct stmp3xxx_platform_fb_entry + *pentry, + struct stmp3xxx_platform_fb_data + *pdata) +{ + list_del(&pentry->link); + list_add(&pentry->link, &pdata->list); +} + +static inline int stmp3xxx_lcd_iterate_pdata(struct stmp3xxx_platform_fb_data + *pdata, + int (*func) (struct + stmp3xxx_platform_fb_entry + * pentry, void *data, + int ret_prev), + void *data) +{ + struct stmp3xxx_platform_fb_entry *pentry; + int ret = 0; + list_for_each_entry(pentry, &pdata->list, link) { + ret = func(pentry, data, ret); + } + return ret; +} + +static inline void stmp3xxx_lcd_set_bl_pdata(struct stmp3xxx_platform_bl_data + *pdata) +{ + extern struct platform_device stmp3xxx_backlight; + stmp3xxx_backlight.dev.platform_data = pdata; +} + +void stmp3xxx_init_lcdif(void); +int stmp3xxx_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize, + int lcd_master); +void stmp3xxx_lcdif_dma_release(void); +void stmp3xxx_lcdif_run(void); +void stmp3xxx_lcdif_stop(void); +int stmp3xxx_lcdif_pan_display(dma_addr_t addr); + +int stmp3xxx_lcdif_register_client(struct notifier_block *nb); +void stmp3xxx_lcdif_unregister_client(struct notifier_block *nb); +void stmp3xxx_lcdif_notify_clients(unsigned long event, + struct stmp3xxx_platform_fb_entry *pentry); + +#ifndef FBIO_WAITFORVSYNC +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, u_int32_t) +#endif + +#define LCD_DMA_CHANNEL 0 + +static inline void setup_dotclk_panel(u16 v_pulse_width, + u16 v_period, + u16 v_wait_cnt, + u16 v_active, + u16 h_pulse_width, + u16 h_period, + u16 h_wait_cnt, + u16 h_active, int enable_present) +{ + u32 val; + + stmp3xxx_clearl(BM_LCDIF_CTRL_DATA_SHIFT_DIR, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + stmp3xxx_clearl(BM_LCDIF_CTRL_SHIFT_NUM_BITS, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + stmp3xxx_clearl(BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + stmp3xxx_setl(BF(7, LCDIF_CTRL1_BYTE_PACKING_FORMAT) | + BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_TRANSFER_COUNT); + val &= ~(BM_LCDIF_TRANSFER_COUNT_V_COUNT | + BM_LCDIF_TRANSFER_COUNT_H_COUNT); + val |= BF(h_active, LCDIF_TRANSFER_COUNT_H_COUNT) | + BF(v_active, LCDIF_TRANSFER_COUNT_V_COUNT); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_TRANSFER_COUNT); + + stmp3xxx_clearl(BM_LCDIF_CTRL_VSYNC_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_WAIT_FOR_VSYNC_EDGE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_DVI_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BM_LCDIF_CTRL_DOTCLK_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + stmp3xxx_clearl(BM_LCDIF_CTRL_WORD_LENGTH | + BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE | + BM_LCDIF_CTRL_LCD_DATABUS_WIDTH, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BF(3, LCDIF_CTRL_WORD_LENGTH) | /* 24 bit */ + BM_LCDIF_CTRL_DATA_SELECT | /* data mode */ + BF(0, LCDIF_CTRL_INPUT_DATA_SWIZZLE) | /* no swap */ + BF(3, LCDIF_CTRL_LCD_DATABUS_WIDTH), /* 24 bit */ + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + val &= ~(BM_LCDIF_VDCTRL0_VSYNC_POL | + BM_LCDIF_VDCTRL0_HSYNC_POL | + BM_LCDIF_VDCTRL0_ENABLE_POL | + BM_LCDIF_VDCTRL0_DOTCLK_POL); + val |= BM_LCDIF_VDCTRL0_ENABLE_POL | + BM_LCDIF_VDCTRL0_DOTCLK_POL; + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + val &= ~(BM_LCDIF_VDCTRL0_VSYNC_OEB); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); /* vsync is output */ + + /* + * need enable sig for true RGB i/f. Or, if not true RGB, leave it + * zero. + */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + val |= BM_LCDIF_VDCTRL0_ENABLE_PRESENT; + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + /* + * For DOTCLK mode, count VSYNC_PERIOD in terms of complete hz lines + */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + val &= ~(BM_LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | + BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT); + val |= BM_LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | + BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT; + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + stmp3xxx_clearl(BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + stmp3xxx_setl(v_pulse_width, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + stmp3xxx_clearl(BM_LCDIF_VDCTRL1_VSYNC_PERIOD, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL1); + stmp3xxx_setl(v_period, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL1); + + stmp3xxx_clearl(BM_LCDIF_VDCTRL2_HSYNC_PERIOD | + BM_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL2); + + stmp3xxx_setl(BF(h_pulse_width, LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH) | + BF(h_period, LCDIF_VDCTRL2_HSYNC_PERIOD), + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL2); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL4); + val &= ~BM_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT; + val |= BF(h_active, LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL4); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL3); + val &= ~(BM_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT | + BM_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT); + val |= BF(h_wait_cnt, LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT) | + BF(v_wait_cnt, LCDIF_VDCTRL3_VERTICAL_WAIT_CNT); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL3); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_VDCTRL4); + val |= BM_LCDIF_VDCTRL4_SYNC_SIGNALS_ON; + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL4); +} + +static inline void release_dotclk_panel(void) +{ + stmp3xxx_clearl(BM_LCDIF_CTRL_DOTCLK_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + __raw_writel(0, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + __raw_writel(0, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL1); + __raw_writel(0, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL2); + __raw_writel(0, REGS_LCDIF_BASE + HW_LCDIF_VDCTRL3); +} + +static inline void dotclk_dma_chain_init(int memsize, dma_addr_t video_phys, + struct stmp3xxx_dma_descriptor + *video_dma_descriptor, + struct stmp3xxx_lcd_dma_chain_info + *dma_chain_info, + unsigned *dma_chain_info_pos) +{ + unsigned i, bytes_left; + dma_addr_t phys = video_phys; + bytes_left = memsize; + + for (i = 0; bytes_left > 0; ++i) { + unsigned this_chain = bytes_left < 0xff00 ? bytes_left : 0xff00; + /* Count of 0 in the DMA word means 65536 */ + unsigned xfer_count = this_chain & 65535; + stmp3xxx_dma_allocate_command(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH), + &video_dma_descriptor[i]); + if (i != 0) { + /* Chain previous command to this one */ + video_dma_descriptor[i - 1].command->next = + video_dma_descriptor[i].handle; + /* Enable DMA chaining, disable IRQ and semaphore + * on previous command + */ + video_dma_descriptor[i - 1].command->cmd &= + ~(BM_APBH_CHn_CMD_IRQONCMPLT | + BM_APBH_CHn_CMD_SEMAPHORE); + } + video_dma_descriptor[i].command->cmd = + BF(xfer_count, APBH_CHn_CMD_XFER_COUNT) | + BF(1, APBH_CHn_CMD_CMDWORDS) | + BM_APBH_CHn_CMD_CHAIN | + BF(2, APBH_CHn_CMD_COMMAND); /* DMA read */ + video_dma_descriptor[i].command->pio_words[0] = + BM_LCDIF_CTRL_RUN | + BF(1, LCDIF_CTRL_INPUT_DATA_SWIZZLE) | + BM_LCDIF_CTRL_DATA_SHIFT_DIR | + BM_LCDIF_CTRL_DOTCLK_MODE | + BM_LCDIF_CTRL_BYPASS_COUNT | BM_LCDIF_CTRL_DATA_SELECT; + video_dma_descriptor[i].command->buf_ptr = phys; + dma_chain_info[*dma_chain_info_pos].dma_addr_p = + &video_dma_descriptor[i].command->buf_ptr; + dma_chain_info[*dma_chain_info_pos].offset = phys - video_phys; + ++*dma_chain_info_pos; + phys += this_chain; + bytes_left -= this_chain; + } + video_dma_descriptor[i - 1].command->next = + video_dma_descriptor[0].handle; + pr_debug("%s: Used %u DMA chains to cover %u bytes\n", __func__, i, + memsize); +} + +static inline void setup_dvi_panel(u16 h_active, u16 v_active, + u16 h_blanking, u16 v_lines, + u16 v1_blank_start, u16 v1_blank_end, + u16 v2_blank_start, u16 v2_blank_end, + u16 f1_start, u16 f1_end, + u16 f2_start, u16 f2_end) +{ + u32 val; + /* 32bit packed format (RGB) */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + stmp3xxx_setl(BF(0x7, LCDIF_CTRL1_BYTE_PACKING_FORMAT) | + BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_TRANSFER_COUNT); + val &= ~(BM_LCDIF_TRANSFER_COUNT_V_COUNT | + BM_LCDIF_TRANSFER_COUNT_H_COUNT); + val |= BF(h_active, LCDIF_TRANSFER_COUNT_H_COUNT) | + BF(v_active, LCDIF_TRANSFER_COUNT_V_COUNT); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_TRANSFER_COUNT); + + /* set lcdif to DVI mode */ + stmp3xxx_setl(BM_LCDIF_CTRL_DVI_MODE, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_VSYNC_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_DOTCLK_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + stmp3xxx_setl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + /* convert input RGB -> YCbCr */ + stmp3xxx_setl(BM_LCDIF_CTRL_RGB_TO_YCBCR422_CSC, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + /* interlace odd and even fields */ + stmp3xxx_setl(BM_LCDIF_CTRL1_INTERLACE_FIELDS, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + stmp3xxx_clearl(BM_LCDIF_CTRL_WORD_LENGTH | + BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE | + BM_LCDIF_CTRL_LCD_DATABUS_WIDTH, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BF(3, LCDIF_CTRL_WORD_LENGTH) | /* 24 bit */ + BM_LCDIF_CTRL_DATA_SELECT | /* data mode */ + BF(0, LCDIF_CTRL_INPUT_DATA_SWIZZLE) | /* no swap */ + BF(1, LCDIF_CTRL_LCD_DATABUS_WIDTH), /* 8 bit */ + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + /* LCDIF_DVI */ + /* set frame size */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_DVICTRL0); + val &= ~(BM_LCDIF_DVICTRL0_H_ACTIVE_CNT | + BM_LCDIF_DVICTRL0_H_BLANKING_CNT | + BM_LCDIF_DVICTRL0_V_LINES_CNT); + val |= BF(1440, LCDIF_DVICTRL0_H_ACTIVE_CNT) | + BF(h_blanking, LCDIF_DVICTRL0_H_BLANKING_CNT) | + BF(v_lines, LCDIF_DVICTRL0_V_LINES_CNT); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_DVICTRL0); + + /* set start/end of field-1 and start of field-2 */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_DVICTRL1); + val &= ~(BM_LCDIF_DVICTRL1_F1_START_LINE | + BM_LCDIF_DVICTRL1_F1_END_LINE | + BM_LCDIF_DVICTRL1_F2_START_LINE); + val |= BF(f1_start, LCDIF_DVICTRL1_F1_START_LINE) | + BF(f1_end, LCDIF_DVICTRL1_F1_END_LINE) | + BF(f2_start, LCDIF_DVICTRL1_F2_START_LINE); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_DVICTRL1); + + /* set first vertical blanking interval and end of filed-2 */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_DVICTRL2); + val &= ~(BM_LCDIF_DVICTRL2_F2_END_LINE | + BM_LCDIF_DVICTRL2_V1_BLANK_START_LINE | + BM_LCDIF_DVICTRL2_V1_BLANK_END_LINE); + val |= BF(f2_end, LCDIF_DVICTRL2_F2_END_LINE) | + BF(v1_blank_start, LCDIF_DVICTRL2_V1_BLANK_START_LINE) | + BF(v1_blank_end, LCDIF_DVICTRL2_V1_BLANK_END_LINE); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_DVICTRL2); + + /* set second vertical blanking interval */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_DVICTRL3); + val &= ~(BM_LCDIF_DVICTRL3_V2_BLANK_START_LINE | + BM_LCDIF_DVICTRL3_V2_BLANK_END_LINE); + val |= BF(v2_blank_start, LCDIF_DVICTRL3_V2_BLANK_START_LINE) | + BF(v2_blank_end, LCDIF_DVICTRL3_V2_BLANK_END_LINE); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_DVICTRL3); + + /* fill the rest area black color if the input frame + * is not 720 pixels/line + */ + if (h_active != 720) { + /* the input frame can't be less then (720-256) pixels/line */ + if (720 - h_active > 0xff) + h_active = 720 - 0xff; + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_DVICTRL4); + val &= ~(BM_LCDIF_DVICTRL4_H_FILL_CNT | + BM_LCDIF_DVICTRL4_Y_FILL_VALUE | + BM_LCDIF_DVICTRL4_CB_FILL_VALUE | + BM_LCDIF_DVICTRL4_CR_FILL_VALUE); + val |= BF(720 - h_active, LCDIF_DVICTRL4_H_FILL_CNT) | + BF(16, LCDIF_DVICTRL4_Y_FILL_VALUE) | + BF(128, LCDIF_DVICTRL4_CB_FILL_VALUE) | + BF(128, LCDIF_DVICTRL4_CR_FILL_VALUE); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_DVICTRL4); + } + + /* Color Space Conversion RGB->YCbCr */ + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF0); + val &= ~(BM_LCDIF_CSC_COEFF0_C0 | + BM_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER); + val |= BF(0x41, LCDIF_CSC_COEFF0_C0) | + BF(3, LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF0); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF1); + val &= ~(BM_LCDIF_CSC_COEFF1_C1 | BM_LCDIF_CSC_COEFF1_C2); + val |= BF(0x81, LCDIF_CSC_COEFF1_C1) | + BF(0x19, LCDIF_CSC_COEFF1_C2); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF1); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF2); + val &= ~(BM_LCDIF_CSC_COEFF2_C3 | BM_LCDIF_CSC_COEFF2_C4); + val |= BF(0x3DB, LCDIF_CSC_COEFF2_C3) | + BF(0x3B6, LCDIF_CSC_COEFF2_C4); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF2); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF3); + val &= ~(BM_LCDIF_CSC_COEFF3_C5 | BM_LCDIF_CSC_COEFF3_C6); + val |= BF(0x70, LCDIF_CSC_COEFF3_C5) | + BF(0x70, LCDIF_CSC_COEFF3_C6); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF3); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF4); + val &= ~(BM_LCDIF_CSC_COEFF4_C7 | BM_LCDIF_CSC_COEFF4_C8); + val |= BF(0x3A2, LCDIF_CSC_COEFF4_C7) | BF(0x3EE, LCDIF_CSC_COEFF4_C8); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_COEFF4); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_OFFSET); + val &= ~(BM_LCDIF_CSC_OFFSET_CBCR_OFFSET | BM_LCDIF_CSC_OFFSET_Y_OFFSET); + val |= BF(0x80, LCDIF_CSC_OFFSET_CBCR_OFFSET) | + BF(0x10, LCDIF_CSC_OFFSET_Y_OFFSET); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_OFFSET); + + val = __raw_readl(REGS_LCDIF_BASE + HW_LCDIF_CSC_LIMIT); + val &= ~(BM_LCDIF_CSC_LIMIT_CBCR_MIN | + BM_LCDIF_CSC_LIMIT_CBCR_MAX | + BM_LCDIF_CSC_LIMIT_Y_MIN | + BM_LCDIF_CSC_LIMIT_Y_MAX); + val |= BF(16, LCDIF_CSC_LIMIT_CBCR_MIN) | + BF(240, LCDIF_CSC_LIMIT_CBCR_MAX) | + BF(16, LCDIF_CSC_LIMIT_Y_MIN) | + BF(235, LCDIF_CSC_LIMIT_Y_MAX); + __raw_writel(val, REGS_LCDIF_BASE + HW_LCDIF_CSC_LIMIT); +} + +static inline void release_dvi_panel(void) +{ + stmp3xxx_clearl(BM_LCDIF_CTRL_DVI_MODE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); +} + +#endif /* _ARCH_ARM_LCDIF_H */ diff --git a/arch/arm/mach-stmp378x/include/mach/pins.h b/arch/arm/mach-stmp378x/include/mach/pins.h index 93f952d35969..f36296a9a2c0 100644 --- a/arch/arm/mach-stmp378x/include/mach/pins.h +++ b/arch/arm/mach-stmp378x/include/mach/pins.h @@ -19,6 +19,32 @@ #define __ASM_ARCH_PINS_H /* + * The number of pin banks and pins per a bank on STMP378x + */ +#define STMP3XXX_PINMUX_NR_BANKS 4 +#define STMP3XXX_PINMUX_BANK_SIZE 32 + +/* + * Macro to convert a pin bank/number pair to a raw pin number + * STMP3XXX_PINMUX_BANK_SIZE and STMP3XXX_PINMUX_NR_BANKS should be + * defined before including this header. + */ +#define STMP3XXX_PINID(bank, pin) (bank * STMP3XXX_PINMUX_BANK_SIZE + pin) +#define STMP3XXX_PINID_TO_BANK(pinid) (pinid / STMP3XXX_PINMUX_BANK_SIZE) +#define STMP3XXX_PINID_TO_PINNUM(pinid) (pinid % STMP3XXX_PINMUX_BANK_SIZE) + +/* + * Special invalid pin identificator to show a pin doesn't exist + */ +#define PINID_NO_PIN STMP3XXX_PINID(STMP3XXX_PINMUX_NR_BANKS, 0) + +static inline int stmp3xxx_valid_pin(unsigned pin) +{ + return STMP3XXX_PINID_TO_BANK(pin) < STMP3XXX_PINMUX_NR_BANKS && + STMP3XXX_PINID_TO_PINNUM(pin) < STMP3XXX_PINMUX_BANK_SIZE; +} + +/* * Define all STMP378x pins, a pin name corresponds to a STMP378x hardware * interface this pin belongs to. */ diff --git a/arch/arm/mach-stmp378x/include/mach/regs-apbh.h b/arch/arm/mach-stmp378x/include/mach/regs-apbh.h index dbcf85b6ac2a..af6371168a4b 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-apbh.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-apbh.h @@ -56,7 +56,7 @@ #define HW_APBH_CH14_NXTCMDAR (0x50 + 14 * 0x70) #define HW_APBH_CH15_NXTCMDAR (0x50 + 15 * 0x70) -#define HW_APBH_CHn_NXTCMDAR 0x50 +#define HW_APBH_CHn_NXTCMDAR(n) (0x50 + n * 0x70) #define BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER 0 #define BV_APBH_CHn_CMD_COMMAND__DMA_WRITE 1 @@ -92,10 +92,24 @@ #define HW_APBH_CH14_SEMA (0x80 + 14 * 0x70) #define HW_APBH_CH15_SEMA (0x80 + 15 * 0x70) -#define HW_APBH_CHn_SEMA 0x80 +#define HW_APBH_CHn_SEMA(n) (0x80 + n * 0x70) #define BM_APBH_CHn_SEMA_INCREMENT_SEMA 0x000000FF #define BP_APBH_CHn_SEMA_INCREMENT_SEMA 0 #define BM_APBH_CHn_SEMA_PHORE 0x00FF0000 #define BP_APBH_CHn_SEMA_PHORE 16 +#define BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT 0x0 +#define BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT 0x1 +#define BV_SSP_CTRL0_BUS_WIDTH__EIGHT_BIT 0x2 + +#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3 +#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7 +#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF + +#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0 +#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1 +#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3 +#define BV_SSP_CTRL1_SSP_MODE__MS 0x4 +#define BV_SSP_CTRL1_SSP_MODE__CE_ATA 0x7 + #endif diff --git a/arch/arm/mach-stmp378x/include/mach/regs-apbx.h b/arch/arm/mach-stmp378x/include/mach/regs-apbx.h index 3b934a4d27f0..06d5f8374e00 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-apbx.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-apbx.h @@ -56,7 +56,7 @@ #define HW_APBX_CH14_NXTCMDAR (0x110 + 14 * 0x70) #define HW_APBX_CH15_NXTCMDAR (0x110 + 15 * 0x70) -#define HW_APBX_CHn_NXTCMDAR 0x110 +#define HW_APBX_CHn_NXTCMDAR(n) (0x110 + n * 0x70) #define BM_APBX_CHn_CMD_COMMAND 0x00000003 #define BP_APBX_CHn_CMD_COMMAND 0 #define BV_APBX_CHn_CMD_COMMAND__NO_DMA_XFER 0 @@ -90,7 +90,7 @@ #define HW_APBX_CH14_BAR (0x130 + 14 * 0x70) #define HW_APBX_CH15_BAR (0x130 + 15 * 0x70) -#define HW_APBX_CHn_BAR 0x130 +#define HW_APBX_CHn_BAR(n) (0x130 + n * 0x70) #define HW_APBX_CH0_SEMA (0x140 + 0 * 0x70) #define HW_APBX_CH1_SEMA (0x140 + 1 * 0x70) @@ -109,7 +109,7 @@ #define HW_APBX_CH14_SEMA (0x140 + 14 * 0x70) #define HW_APBX_CH15_SEMA (0x140 + 15 * 0x70) -#define HW_APBX_CHn_SEMA 0x140 +#define HW_APBX_CHn_SEMA(n) (0x140 + n * 0x70) #define BM_APBX_CHn_SEMA_INCREMENT_SEMA 0x000000FF #define BP_APBX_CHn_SEMA_INCREMENT_SEMA 0 #define BM_APBX_CHn_SEMA_PHORE 0x00FF0000 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-audioin.h b/arch/arm/mach-stmp378x/include/mach/regs-audioin.h index 641ac6126f83..a62a6f33f4ed 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-audioin.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-audioin.h @@ -55,9 +55,19 @@ #define BP_AUDIOIN_ADCVOL_SELECT_LEFT 12 #define BM_AUDIOIN_ADCVOL_MUTE 0x01000000 +#define BP_AUDIOIN_ADCVOLUME_VOLUME_LEFT 16 +#define BM_AUDIOIN_ADCVOLUME_VOLUME_LEFT 0x00FF0000 +#define BM_AUDIOIN_ADCVOLUME_VOLUME_UPDATE_RIGHT 0x00001000 +#define BP_AUDIOIN_ADCVOLUME_VOLUME_RIGHT 0 +#define BM_AUDIOIN_ADCVOLUME_VOLUME_RIGHT 0x000000FF + #define HW_AUDIOIN_MICLINE 0x60 #define HW_AUDIOIN_ANACLKCTRL 0x70 #define BM_AUDIOIN_ANACLKCTRL_CLKGATE 0x80000000 #define HW_AUDIOIN_DATA 0x80 + +#define BM_AUDIOIN_MICLINE_MIC_SELECT 0x01000000 +#define BP_AUDIOIN_MICLINE_MIC_RESISTOR 20 +#define BM_AUDIOIN_MICLINE_MIC_RESISTOR 0x00300000 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-audioout.h b/arch/arm/mach-stmp378x/include/mach/regs-audioout.h index f533e23694a0..59c5328e9577 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-audioout.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-audioout.h @@ -51,6 +51,13 @@ #define HW_AUDIOOUT_DACDEBUG 0x40 +#define BP_AUDIOOUT_DACVOLUME_VOLUME_LEFT 16 +#define BM_AUDIOOUT_DACVOLUME_VOLUME_LEFT 0x00FF0000 +#define BM_AUDIOOUT_DACVOLUME_VOLUME_UPDATE_RIGHT 0x00001000 +#define BM_AUDIOOUT_DACVOLUME_MUTE_RIGHT 0x00000100 +#define BP_AUDIOOUT_DACVOLUME_VOLUME_RIGHT 0 +#define BM_AUDIOOUT_DACVOLUME_VOLUME_RIGHT 0x000000FF + #define HW_AUDIOOUT_HPVOL 0x50 #define BM_AUDIOOUT_HPVOL_MUTE 0x01000000 #define BM_AUDIOOUT_HPVOL_EN_MSTR_ZCD 0x02000000 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-dcp.h b/arch/arm/mach-stmp378x/include/mach/regs-dcp.h index fdedd00c0e28..ac53087f39c2 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-dcp.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-dcp.h @@ -63,19 +63,22 @@ #define BM_DCP_PACKET2_CIPHER_CFG 0xFF000000 #define BP_DCP_PACKET2_CIPHER_CFG 24 +#define BV_DCP_PACKET2_CIPHER_MODE__ECB 0x00 +#define BV_DCP_PACKET2_CIPHER_SELECT__AES128 0x00 + #define HW_DCP_CH0CMDPTR (0x100 + 0 * 0x40) #define HW_DCP_CH1CMDPTR (0x100 + 1 * 0x40) #define HW_DCP_CH2CMDPTR (0x100 + 2 * 0x40) #define HW_DCP_CH3CMDPTR (0x100 + 3 * 0x40) -#define HW_DCP_CHnCMDPTR 0x100 +#define HW_DCP_CHnCMDPTR(n) (0x100 + n * 0x40) #define HW_DCP_CH0SEMA (0x110 + 0 * 0x40) #define HW_DCP_CH1SEMA (0x110 + 1 * 0x40) #define HW_DCP_CH2SEMA (0x110 + 2 * 0x40) #define HW_DCP_CH3SEMA (0x110 + 3 * 0x40) -#define HW_DCP_CHnSEMA 0x110 +#define HW_DCP_CHnSEMA(n) (0x110 + n * 0x40) #define BM_DCP_CHnSEMA_INCREMENT 0x000000FF #define BP_DCP_CHnSEMA_INCREMENT 0 @@ -84,4 +87,24 @@ #define HW_DCP_CH2STAT (0x120 + 2 * 0x40) #define HW_DCP_CH3STAT (0x120 + 3 * 0x40) -#define HW_DCP_CHnSTAT 0x120 +#define HW_DCP_CHnSTAT(n) (0x120 + n * 0x40) + +#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH0 0x01 +#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH1 0x02 +#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH2 0x04 +#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH3 0x08 + +#define HW_DCP_PACKET2_ADDR (REGS_DCP_BASE + 0x000000a0) +#define BP_DCP_PACKET2_CIPHER_CFG 24 +#define BM_DCP_PACKET2_CIPHER_CFG 0xFF000000 +#define BP_DCP_PACKET2_HASH_SELECT 16 +#define BM_DCP_PACKET2_HASH_SELECT 0x000F0000 +#define BV_DCP_PACKET2_HASH_SELECT__SHA1 0x00 +#define BV_DCP_PACKET2_HASH_SELECT__CRC32 0x01 +#define BP_DCP_PACKET2_KEY_SELECT 8 + +#define BV_DCP_PACKET2_CIPHER_MODE__ECB 0x00 +#define BV_DCP_PACKET2_CIPHER_MODE__CBC 0x01 +#define BP_DCP_PACKET2_CIPHER_SELECT 0 +#define BM_DCP_PACKET2_CIPHER_SELECT 0x0000000F +#define BV_DCP_PACKET2_CIPHER_SELECT__AES128 0x00 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-gpmi.h b/arch/arm/mach-stmp378x/include/mach/regs-gpmi.h index 2cc8bbe91687..47a150bbb582 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-gpmi.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-gpmi.h @@ -53,6 +53,9 @@ #define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT 1 #define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT 2 #define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT 3 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF +#define BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY 0x100 #define HW_GPMI_CTRL1 0x60 #define BM_GPMI_CTRL1_GPMI_MODE 0x00000001 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-icoll.h b/arch/arm/mach-stmp378x/include/mach/regs-icoll.h index f996e80f40e7..57febd8559e7 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-icoll.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-icoll.h @@ -37,9 +37,14 @@ #define HW_ICOLL_STAT 0x70 -#define HW_ICOLL_INTERRUPTn 0x120 - -#define HW_ICOLL_INTERRUPTn 0x120 +#define HW_ICOLL_INTERRUPTn(n) (0x120 + n * 0x10) #define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004 +#define HW_ICOLL_PRIORITYn(n) (0x60 + n * 0x10) + +#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1 +#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL1 0x2 +#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL2 0x4 +#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL3 0x8 + #endif diff --git a/arch/arm/mach-stmp378x/include/mach/regs-lradc.h b/arch/arm/mach-stmp378x/include/mach/regs-lradc.h index cb8cb06f8277..3b76d5f6bed8 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-lradc.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-lradc.h @@ -51,6 +51,9 @@ #define BM_LRADC_CTRL2_BL_ENABLE 0x00400000 #define BM_LRADC_CTRL2_DIVIDE_BY_TWO 0xFF000000 #define BP_LRADC_CTRL2_DIVIDE_BY_TWO 24 +#define BM_LRADC_CTRL2_TEMPSENSE_PWD 0x00008000 +#define BV_LRADC_CTRL2_TEMPSENSE_PWD__ENABLE 0x0 +#define BV_LRADC_CTRL2_TEMPSENSE_PWD__DISABLE 0x1 #define HW_LRADC_CTRL3 0x30 #define BM_LRADC_CTRL3_CYCLE_TIME 0x00000300 @@ -69,7 +72,7 @@ #define HW_LRADC_CH6 (0x50 + 6 * 0x10) #define HW_LRADC_CH7 (0x50 + 7 * 0x10) -#define HW_LRADC_CHn 0x50 +#define HW_LRADC_CHn(chn) (0x50 + chn * 0x10) #define BM_LRADC_CHn_VALUE 0x0003FFFF #define BP_LRADC_CHn_VALUE 0 #define BM_LRADC_CHn_NUM_SAMPLES 0x1F000000 @@ -81,7 +84,7 @@ #define HW_LRADC_DELAY2 (0xD0 + 2 * 0x10) #define HW_LRADC_DELAY3 (0xD0 + 3 * 0x10) -#define HW_LRADC_DELAYn 0xD0 +#define HW_LRADC_DELAYn(chn) (0xD0 + chn * 0x10) #define BM_LRADC_DELAYn_DELAY 0x000007FF #define BP_LRADC_DELAYn_DELAY 0 #define BM_LRADC_DELAYn_LOOP_COUNT 0x0000F800 @@ -97,3 +100,17 @@ #define BP_LRADC_CTRL4_LRADC6SELECT 24 #define BM_LRADC_CTRL4_LRADC7SELECT 0xF0000000 #define BP_LRADC_CTRL4_LRADC7SELECT 28 + +#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA 0x6 +#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYB 0x7 + +#define HW_LRADC_CONVERSION 0x130 +#define BM_LRADC_CONVERSION_AUTOMATIC 0x00100000 +#define BV_LRADC_CONVERSION_AUTOMATIC__DISABLE 0x0 +#define BV_LRADC_CONVERSION_AUTOMATIC__ENABLE 0x1 +#define BP_LRADC_CONVERSION_SCALE_FACTOR 16 +#define BM_LRADC_CONVERSION_SCALE_FACTOR 0x00030000 + +#define BM_LRADC_CTRL1_LRADC7_IRQ 0x00000080 +#define BV_LRADC_CTRL1_LRADC7_IRQ__CLEAR 0x0 +#define BV_LRADC_CTRL1_LRADC7_IRQ__PENDING 0x1 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-ocotp.h b/arch/arm/mach-stmp378x/include/mach/regs-ocotp.h index f0af64d9937e..06f151f9e55d 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-ocotp.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-ocotp.h @@ -29,6 +29,8 @@ #define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00002000 #define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000 #define BP_OCOTP_CTRL_WR_UNLOCK 16 +#define BP_OCOTP_CTRL_ADDR 0 +#define BM_OCOTP_CTRL_ADDR 0x0000001F #define HW_OCOTP_DATA 0x10 @@ -37,4 +39,6 @@ #define HW_OCOTP_CUST2 (0x20 + 2 * 0x10) #define HW_OCOTP_CUST3 (0x20 + 3 * 0x10) -#define HW_OCOTP_CUSTn 0x20 +#define HW_OCOTP_CUSTn(n) (0x20 + n * 0x10) + +#define HW_OCOTP_CUSTCAP 0x110 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-power.h b/arch/arm/mach-stmp378x/include/mach/regs-power.h index e454c830f076..e3438ecb6eab 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-power.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-power.h @@ -35,6 +35,8 @@ #define HW_POWER_5VCTRL 0x10 #define BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT 0x00000040 +#define HW_POWER_LOOPCTRL 0xb0 + #define HW_POWER_MINPWR 0x20 #define HW_POWER_CHARGE 0x30 @@ -60,4 +62,235 @@ #define BM_POWER_DEBUG_AVALIDPIOLOCK 0x00000004 #define BM_POWER_DEBUG_VBUSVALIDPIOLOCK 0x00000008 +#define BP_POWER_STS_PSWITCH 20 +#define BM_POWER_STS_PSWITCH 0x00300000 +#define BM_POWER_CTRL_POLARITY_PSWITCH 0x00040000 + +#define BM_POWER_CTRL_CLKGATE 0x40000000 +#define BM_POWER_CTRL_PSWITCH_MID_TRAN 0x08000000 +#define BM_POWER_CTRL_DCDC4P2_BO_IRQ 0x01000000 +#define BM_POWER_CTRL_ENIRQ_DCDC4P2_BO 0x00800000 +#define BM_POWER_CTRL_VDD5V_DROOP_IRQ 0x00400000 +#define BM_POWER_CTRL_ENIRQ_VDD5V_DROOP 0x00200000 +#define BM_POWER_CTRL_PSWITCH_IRQ 0x00100000 +#define BM_POWER_CTRL_PSWITCH_IRQ_SRC 0x00080000 +#define BM_POWER_CTRL_POLARITY_PSWITCH 0x00040000 +#define BM_POWER_CTRL_ENIRQ_PSWITCH 0x00020000 +#define BM_POWER_CTRL_POLARITY_DC_OK 0x00010000 +#define BM_POWER_CTRL_DC_OK_IRQ 0x00008000 +#define BM_POWER_CTRL_ENIRQ_DC_OK 0x00004000 +#define BM_POWER_CTRL_BATT_BO_IRQ 0x00002000 +#define BM_POWER_CTRL_ENIRQBATT_BO 0x00001000 +#define BM_POWER_CTRL_VDDIO_BO_IRQ 0x00000800 +#define BM_POWER_CTRL_ENIRQ_VDDIO_BO 0x00000400 +#define BM_POWER_CTRL_VDDA_BO_IRQ 0x00000200 +#define BM_POWER_CTRL_ENIRQ_VDDA_BO 0x00000100 +#define BM_POWER_CTRL_VDDD_BO_IRQ 0x00000080 +#define BM_POWER_CTRL_ENIRQ_VDDD_BO 0x00000040 +#define BM_POWER_CTRL_POLARITY_VBUSVALID 0x00000020 +#define BM_POWER_CTRL_VBUSVALID_IRQ 0x00000010 +#define BM_POWER_CTRL_ENIRQ_VBUS_VALID 0x00000008 +#define BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO 0x00000004 +#define BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ 0x00000002 +#define BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO 0x00000001 + +#define BP_POWER_5VCTRL_VBUSDROOP_TRSH 28 +#define BM_POWER_5VCTRL_VBUSDROOP_TRSH 0x30000000 +#define BP_POWER_5VCTRL_HEADROOM_ADJ 24 +#define BM_POWER_5VCTRL_HEADROOM_ADJ 0x07000000 +#define BM_POWER_5VCTRL_PWD_CHARGE_4P2 0x00100000 +#define BP_POWER_5VCTRL_CHARGE_4P2_ILIMIT 12 +#define BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT 0x0003F000 +#define BP_POWER_5VCTRL_VBUSVALID_TRSH 8 +#define BM_POWER_5VCTRL_VBUSVALID_TRSH 0x00000700 +#define BM_POWER_5VCTRL_PWDN_5VBRNOUT 0x00000080 +#define BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT 0x00000040 +#define BM_POWER_5VCTRL_DCDC_XFER 0x00000020 +#define BM_POWER_5VCTRL_VBUSVALID_5VDETECT 0x00000010 +#define BM_POWER_5VCTRL_VBUSVALID_TO_B 0x00000008 +#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO 0x00000004 +#define BM_POWER_5VCTRL_PWRUP_VBUS_CMPS 0x00000002 +#define BM_POWER_5VCTRL_ENABLE_DCDC 0x00000001 + +#define BM_POWER_MINPWR_LOWPWR_4P2 0x00004000 +#define BM_POWER_MINPWR_VDAC_DUMP_CTRL 0x00002000 +#define BM_POWER_MINPWR_PWD_BO 0x00001000 +#define BM_POWER_MINPWR_USE_VDDXTAL_VBG 0x00000800 +#define BM_POWER_MINPWR_PWD_ANA_CMPS 0x00000400 +#define BM_POWER_MINPWR_ENABLE_OSC 0x00000200 +#define BM_POWER_MINPWR_SELECT_OSC 0x00000100 +#define BM_POWER_MINPWR_VBG_OFF 0x00000080 +#define BM_POWER_MINPWR_DOUBLE_FETS 0x00000040 +#define BM_POWER_MINPWR_HALF_FETS 0x00000020 +#define BM_POWER_MINPWR_LESSANA_I 0x00000010 +#define BM_POWER_MINPWR_PWD_XTAL24 0x00000008 +#define BM_POWER_MINPWR_DC_STOPCLK 0x00000004 +#define BM_POWER_MINPWR_EN_DC_PFM 0x00000002 +#define BM_POWER_MINPWR_DC_HALFCLK 0x00000001 + +#define BP_POWER_CHARGE_ADJ_VOLT 24 +#define BM_POWER_CHARGE_ADJ_VOLT 0x07000000 +#define BM_POWER_CHARGE_RSRVD3 0x00800000 +#define BM_POWER_CHARGE_ENABLE_LOAD 0x00400000 +#define BM_POWER_CHARGE_ENABLE_CHARGER_RESISTORS 0x00200000 +#define BM_POWER_CHARGE_ENABLE_FAULT_DETECT 0x00100000 +#define BM_POWER_CHARGE_CHRG_STS_OFF 0x00080000 +#define BM_POWER_CHARGE_USE_EXTERN_R 0x00020000 +#define BM_POWER_CHARGE_PWD_BATTCHRG 0x00010000 +#define BP_POWER_CHARGE_STOP_ILIMIT 8 +#define BM_POWER_CHARGE_STOP_ILIMIT 0x00000F00 +#define BP_POWER_CHARGE_BATTCHRG_I 0 +#define BM_POWER_CHARGE_BATTCHRG_I 0x0000003F + +#define BP_POWER_VDDDCTRL_ADJTN 28 +#define BM_POWER_VDDDCTRL_ADJTN 0xF0000000 +#define BM_POWER_VDDDCTRL_PWDN_BRNOUT 0x00800000 +#define BM_POWER_VDDDCTRL_DISABLE_STEPPING 0x00400000 +#define BM_POWER_VDDDCTRL_ENABLE_LINREG 0x00200000 +#define BM_POWER_VDDDCTRL_DISABLE_FET 0x00100000 +#define BP_POWER_VDDDCTRL_LINREG_OFFSET 16 +#define BM_POWER_VDDDCTRL_LINREG_OFFSET 0x00030000 +#define BP_POWER_VDDDCTRL_BO_OFFSET 8 +#define BM_POWER_VDDDCTRL_BO_OFFSET 0x00000700 +#define BP_POWER_VDDDCTRL_TRG 0 +#define BM_POWER_VDDDCTRL_TRG 0x0000001F + +#define BM_POWER_VDDACTRL_PWDN_BRNOUT 0x00080000 +#define BM_POWER_VDDACTRL_DISABLE_STEPPING 0x00040000 +#define BM_POWER_VDDACTRL_ENABLE_LINREG 0x00020000 +#define BM_POWER_VDDACTRL_DISABLE_FET 0x00010000 +#define BP_POWER_VDDACTRL_LINREG_OFFSET 12 +#define BM_POWER_VDDACTRL_LINREG_OFFSET 0x00003000 +#define BP_POWER_VDDACTRL_BO_OFFSET 8 +#define BM_POWER_VDDACTRL_BO_OFFSET 0x00000700 +#define BP_POWER_VDDACTRL_TRG 0 +#define BM_POWER_VDDACTRL_TRG 0x0000001F + +#define BP_POWER_VDDIOCTRL_ADJTN 20 +#define BM_POWER_VDDIOCTRL_ADJTN 0x00F00000 +#define BM_POWER_VDDIOCTRL_PWDN_BRNOUT 0x00040000 +#define BM_POWER_VDDIOCTRL_DISABLE_STEPPING 0x00020000 +#define BM_POWER_VDDIOCTRL_DISABLE_FET 0x00010000 +#define BP_POWER_VDDIOCTRL_LINREG_OFFSET 12 +#define BM_POWER_VDDIOCTRL_LINREG_OFFSET 0x00003000 +#define BP_POWER_VDDIOCTRL_BO_OFFSET 8 +#define BM_POWER_VDDIOCTRL_BO_OFFSET 0x00000700 +#define BP_POWER_VDDIOCTRL_TRG 0 +#define BM_POWER_VDDIOCTRL_TRG 0x0000001F + +#define HW_POWER_VDDMEMCTRL (REGS_POWER_BASE + 0x00000070) +#define BM_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE 0x00000400 +#define BM_POWER_VDDMEMCTRL_ENABLE_ILIMIT 0x00000200 +#define BM_POWER_VDDMEMCTRL_ENABLE_LINREG 0x00000100 +#define BP_POWER_VDDMEMCTRL_TRG 0 +#define BM_POWER_VDDMEMCTRL_TRG 0x0000001F + +#define HW_POWER_DCDC4P2 (REGS_POWER_BASE + 0x00000080) +#define BP_POWER_DCDC4P2_DROPOUT_CTRL 28 +#define BM_POWER_DCDC4P2_DROPOUT_CTRL 0xF0000000 + +#define BP_POWER_DCDC4P2_ISTEAL_THRESH 24 +#define BM_POWER_DCDC4P2_ISTEAL_THRESH 0x03000000 +#define BM_POWER_DCDC4P2_ENABLE_4P2 0x00800000 +#define BM_POWER_DCDC4P2_ENABLE_DCDC 0x00400000 +#define BM_POWER_DCDC4P2_HYST_DIR 0x00200000 +#define BM_POWER_DCDC4P2_HYST_THRESH 0x00100000 +#define BP_POWER_DCDC4P2_TRG 16 +#define BM_POWER_DCDC4P2_TRG 0x00070000 +#define BP_POWER_DCDC4P2_BO 8 +#define BM_POWER_DCDC4P2_BO 0x00001F00 +#define BP_POWER_DCDC4P2_CMPTRIP 0 +#define BM_POWER_DCDC4P2_CMPTRIP 0x0000001F + +#define HW_POWER_MISC (REGS_POWER_BASE + 0x00000090) +#define BP_POWER_MISC_FREQSEL 4 +#define BM_POWER_MISC_FREQSEL 0x00000070 +#define BM_POWER_MISC_RSRVD1 0x00000008 +#define BM_POWER_MISC_DELAY_TIMING 0x00000004 +#define BM_POWER_MISC_TEST 0x00000002 +#define BM_POWER_MISC_SEL_PLLCLK 0x00000001 + +#define HW_POWER_DCLIMITS (REGS_POWER_BASE + 0x000000a0) +#define BP_POWER_DCLIMITS_POSLIMIT_BUCK 8 +#define BM_POWER_DCLIMITS_POSLIMIT_BUCK 0x00007F00 +#define BP_POWER_DCLIMITS_NEGLIMIT 0 +#define BM_POWER_DCLIMITS_NEGLIMIT 0x0000007F + +#define BM_POWER_LOOPCTRL_TOGGLE_DIF 0x00100000 +#define BM_POWER_LOOPCTRL_HYST_SIGN 0x00080000 +#define BM_POWER_LOOPCTRL_EN_CM_HYST 0x00040000 +#define BM_POWER_LOOPCTRL_EN_DF_HYST 0x00020000 +#define BM_POWER_LOOPCTRL_CM_HYST_THRESH 0x00010000 +#define BM_POWER_LOOPCTRL_DF_HYST_THRESH 0x00008000 +#define BM_POWER_LOOPCTRL_RCSCALE_THRESH 0x00004000 +#define BP_POWER_LOOPCTRL_EN_RCSCALE 12 +#define BM_POWER_LOOPCTRL_EN_RCSCALE 0x00003000 +#define BP_POWER_LOOPCTRL_DC_FF 8 +#define BM_POWER_LOOPCTRL_DC_FF 0x00000700 +#define BP_POWER_LOOPCTRL_DC_R 4 +#define BM_POWER_LOOPCTRL_DC_R 0x000000F0 +#define BP_POWER_LOOPCTRL_DC_C 0 +#define BM_POWER_LOOPCTRL_DC_C 0x00000003 + +#define BP_POWER_STS_PWRUP_SOURCE 24 +#define BM_POWER_STS_PWRUP_SOURCE 0x3F000000 +#define BP_POWER_STS_PSWITCH 20 +#define BM_POWER_STS_PSWITCH 0x00300000 +#define BM_POWER_STS_AVALID_STATUS 0x00020000 +#define BM_POWER_STS_BVALID_STATUS 0x00010000 +#define BM_POWER_STS_VBUSVALID_STATUS 0x00008000 +#define BM_POWER_STS_SESSEND_STATUS 0x00004000 +#define BM_POWER_STS_BATT_BO 0x00002000 +#define BM_POWER_STS_VDD5V_FAULT 0x00001000 +#define BM_POWER_STS_CHRGSTS 0x00000800 +#define BM_POWER_STS_DCDC_4P2_BO 0x00000400 +#define BM_POWER_STS_DC_OK 0x00000200 +#define BM_POWER_STS_VDDIO_BO 0x00000100 +#define BM_POWER_STS_VDDA_BO 0x00000080 +#define BM_POWER_STS_VDDD_BO 0x00000040 +#define BM_POWER_STS_VDD5V_GT_VDDIO 0x00000020 +#define BM_POWER_STS_VDD5V_DROOP 0x00000010 +#define BM_POWER_STS_AVALID 0x00000008 +#define BM_POWER_STS_BVALID 0x00000004 +#define BM_POWER_STS_VBUSVALID 0x00000002 +#define BM_POWER_STS_SESSEND 0x00000001 + +#define HW_POWER_SPEED (REGS_POWER_BASE + 0x000000d0) +#define BP_POWER_SPEED_STATUS 16 +#define BM_POWER_SPEED_STATUS 0x00FF0000 +#define BP_POWER_SPEED_CTRL 0 +#define BM_POWER_SPEED_CTRL 0x00000003 + +#define HW_POWER_BATTMONITOR 0xe0 +#define BP_POWER_BATTMONITOR_BATT_VAL 16 +#define BM_POWER_BATTMONITOR_BATT_VAL 0x03FF0000 +#define BM_POWER_BATTMONITOR_EN_BATADJ 0x00000400 +#define BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT 0x00000200 +#define BM_POWER_BATTMONITOR_BRWNOUT_PWD 0x00000100 +#define BP_POWER_BATTMONITOR_BRWNOUT_LVL 0 +#define BM_POWER_BATTMONITOR_BRWNOUT_LVL 0x0000001F + +#define BP_POWER_RESET_UNLOCK 16 +#define BM_POWER_RESET_UNLOCK 0xFFFF0000 +#define BV_POWER_RESET_UNLOCK__KEY 0x3E77 +#define BM_POWER_RESET_PWD_OFF 0x00000002 +#define BM_POWER_RESET_PWD 0x00000001 + +#define BM_POWER_DEBUG_VBUSVALIDPIOLOCK 0x00000008 +#define BM_POWER_DEBUG_AVALIDPIOLOCK 0x00000004 +#define BM_POWER_DEBUG_BVALIDPIOLOCK 0x00000002 +#define BM_POWER_DEBUG_SESSENDPIOLOCK 0x00000001 + +#define HW_POWER_SPECIAL (REGS_POWER_BASE + 0x00000120) +#define BP_POWER_SPECIAL_TEST 0 +#define BM_POWER_SPECIAL_TEST 0xFFFFFFFF + +#define HW_POWER_VERSION (REGS_POWER_BASE + 0x00000130) +#define BP_POWER_VERSION_MAJOR 24 +#define BM_POWER_VERSION_MAJOR 0xFF000000 +#define BP_POWER_VERSION_MINOR 16 +#define BM_POWER_VERSION_MINOR 0x00FF0000 +#define BP_POWER_VERSION_STEP 0 +#define BM_POWER_VERSION_STEP 0x0000FFFF + #endif diff --git a/arch/arm/mach-stmp378x/include/mach/regs-pwm.h b/arch/arm/mach-stmp378x/include/mach/regs-pwm.h index 0d0f9e56ec77..2b653c20e302 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-pwm.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-pwm.h @@ -31,7 +31,7 @@ #define HW_PWM_ACTIVE2 (0x10 + 2 * 0x20) #define HW_PWM_ACTIVE3 (0x10 + 3 * 0x20) -#define HW_PWM_ACTIVEn 0x10 +#define HW_PWM_ACTIVEn(n) (0x10 + n * 0x20) #define BM_PWM_ACTIVEn_ACTIVE 0x0000FFFF #define BP_PWM_ACTIVEn_ACTIVE 0 #define BM_PWM_ACTIVEn_INACTIVE 0xFFFF0000 @@ -42,7 +42,7 @@ #define HW_PWM_PERIOD2 (0x20 + 2 * 0x20) #define HW_PWM_PERIOD3 (0x20 + 3 * 0x20) -#define HW_PWM_PERIODn 0x20 +#define HW_PWM_PERIODn(n) (0x20 + n * 0x20) #define BM_PWM_PERIODn_PERIOD 0x0000FFFF #define BP_PWM_PERIODn_PERIOD 0 #define BM_PWM_PERIODn_ACTIVE_STATE 0x00030000 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-pxp.h b/arch/arm/mach-stmp378x/include/mach/regs-pxp.h index 54d297896de8..321467d90173 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-pxp.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-pxp.h @@ -26,10 +26,26 @@ #define BM_PXP_CTRL_ENABLE 0x00000001 #define BP_PXP_CTRL_ENABLE 0 #define BM_PXP_CTRL_IRQ_ENABLE 0x00000002 + #define BM_PXP_CTRL_OUTPUT_RGB_FORMAT 0x000000F0 #define BP_PXP_CTRL_OUTPUT_RGB_FORMAT 4 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB8888 0x0 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888 0x1 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888P 0x2 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB1555 0x3 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB565 0x4 +#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB555 0x5 + #define BM_PXP_CTRL_ROTATE 0x00000300 #define BP_PXP_CTRL_ROTATE 8 + +#define BM_PXP_CTRL_S0_FORMAT 0x0000F000 +#define BV_PXP_CTRL_S0_FORMAT__RGB888 0x1 +#define BV_PXP_CTRL_S0_FORMAT__RGB565 0x4 +#define BV_PXP_CTRL_S0_FORMAT__RGB555 0x5 +#define BV_PXP_CTRL_S0_FORMAT__YUV422 0x8 +#define BV_PXP_CTRL_S0_FORMAT__YUV420 0x9 + #define BM_PXP_CTRL_HFLIP 0x00000400 #define BM_PXP_CTRL_VFLIP 0x00000800 #define BM_PXP_CTRL_S0_FORMAT 0x0000F000 @@ -138,3 +154,13 @@ #define BP_PXP_OLnPARAM_FORMAT 4 #define BM_PXP_OLnPARAM_ALPHA 0x0000FF00 #define BP_PXP_OLnPARAM_ALPHA 8 +#define BV_PXP_OLnPARAM_FORMAT__ARGB8888 0x0 +#define BV_PXP_OLnPARAM_FORMAT__RGB888 0x1 +#define BV_PXP_OLnPARAM_FORMAT__ARGB1555 0x3 +#define BV_PXP_OLnPARAM_FORMAT__RGB565 0x4 +#define BV_PXP_OLnPARAM_FORMAT__RGB555 0x5 +#define BM_PXP_OLnPARAM_ENABLE_COLORKEY 0x00000008 +#define BV_PXP_OLnPARAM_ALPHA_CNTL__Embedded 0x0 +#define BV_PXP_OLnPARAM_ALPHA_CNTL__Override 0x1 +#define BV_PXP_OLnPARAM_ALPHA_CNTL__Multiply 0x2 +#define BV_PXP_OLnPARAM_ALPHA_CNTL__ROPs 0x3 diff --git a/arch/arm/mach-stmp378x/include/mach/regs-timrot.h b/arch/arm/mach-stmp378x/include/mach/regs-timrot.h index b5527957c67f..8a927c4821e4 100644 --- a/arch/arm/mach-stmp378x/include/mach/regs-timrot.h +++ b/arch/arm/mach-stmp378x/include/mach/regs-timrot.h @@ -65,4 +65,17 @@ #define HW_TIMROT_TIMCOUNTn 0x30 +#define BV_TIMROT_ROTCTRL_OVERSAMPLE__8X 0x0 +#define BV_TIMROT_ROTCTRL_OVERSAMPLE__4X 0x1 +#define BV_TIMROT_ROTCTRL_OVERSAMPLE__2X 0x2 +#define BV_TIMROT_ROTCTRL_OVERSAMPLE__1X 0x3 +#define BM_TIMROT_ROTCTRL_POLARITY_B 0x00000200 +#define BM_TIMROT_ROTCTRL_POLARITY_A 0x00000100 +#define BP_TIMROT_ROTCTRL_SELECT_B 4 +#define BM_TIMROT_ROTCTRL_SELECT_B 0x00000070 +#define BV_TIMROT_ROTCTRL_SELECT_B__ROTARYA 0x6 +#define BV_TIMROT_ROTCTRL_SELECT_B__ROTARYB 0x7 +#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA 0x6 +#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYB 0x7 + #endif diff --git a/arch/arm/mach-stmp378x/lcd_hx8238a.c b/arch/arm/mach-stmp378x/lcd_hx8238a.c new file mode 100644 index 000000000000..51a06e7e7083 --- /dev/null +++ b/arch/arm/mach-stmp378x/lcd_hx8238a.c @@ -0,0 +1,350 @@ +/* + * Freescale STMP37XX/STMP378X dotclk panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> + +#include <mach/regs-lcdif.h> +#include <mach/regs-lradc.h> +#include <mach/regs-pinctrl.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-pwm.h> +#include <mach/regs-apbh.h> +#include <mach/gpio.h> +#include <mach/pins.h> +#include <mach/lcdif.h> +#include <mach/cpu.h> +#include <mach/stmp3xxx.h> + +#include "common.h" + +#define MAX_CHAIN_LEN 10 + +#define DOTCLK_H_ACTIVE 960 +#define DOTCLK_H_PULSE_WIDTH 2 +#define DOTCLK_HF_PORCH 1 +#define DOTCLK_HB_PORCH 67 +#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH)) +#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE) + +#define DOTCLK_V_PULSE_WIDTH 2 +#define DOTCLK_V_ACTIVE 240 +#define DOTCLK_VF_PORCH 1 +#define DOTCLK_VB_PORCH 16 +#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH) +#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT) + +static struct stmp3xxx_platform_bl_data bl_data; +extern struct pin_group lcd_pins; +extern unsigned lcd_spi_pins[]; + +static void spi_write(u32 val) +{ + u32 mask; + + gpio_set_value(lcd_spi_pins[SPI_MOSI], 0); + gpio_set_value(lcd_spi_pins[SPI_SCLK], 0); + gpio_set_value(lcd_spi_pins[SPI_CS], 0); + + for (mask = 0x00800000; mask != 0; mask >>= 1) { + gpio_set_value(lcd_spi_pins[SPI_SCLK], 0); + if (val & mask) + gpio_set_value(lcd_spi_pins[SPI_MOSI], 1); + else + gpio_set_value(lcd_spi_pins[SPI_MOSI], 0); + + gpio_set_value(lcd_spi_pins[SPI_SCLK], 1); + } + + udelay(10); + gpio_set_value(lcd_spi_pins[SPI_MOSI], 0); + gpio_set_value(lcd_spi_pins[SPI_SCLK], 0); + gpio_set_value(lcd_spi_pins[SPI_CS], 1); +} + +static void write_reg(u16 reg, u16 val) +{ + pr_debug("%s: writing %x to %x\n", __func__, reg, val); + spi_write(0x00700000 | reg); + spi_write(0x00720000 | val); +} + +static void init_panel_hw(void) +{ + int i; + const unsigned short seq[] = { + 0x02, 0x0200, + 0x03, 0x6164, + 0x0E, 0x3380, + 0x1E, 0x00D2, + 0x01, 0x733F, + 0x04, 0x0448, + 0x05, 0xBC54, + 0x0A, 0x4008, + 0x0B, 0xD400, + 0x0D, 0x3229, + 0x0F, 0x0000, + 0x30, 0x0000, + 0x31, 0x0407, + 0x32, 0x0202, + 0x33, 0x0000, + 0x34, 0x0505, + 0x35, 0x0003, + 0x36, 0x0707, + 0x37, 0x0000, + 0x3A, 0x0904, + 0x3B, 0x0904, + }; + + for (i = 0; i < sizeof(seq) / sizeof(seq[0]); i += 2) + write_reg(seq[i], seq[i + 1]); +} + +static int init_pinmux(void) +{ + return stmp3xxx_request_pin_group(&lcd_pins, "lcd_hx8238a"); +} + +static int init_pinmux_spi(void) +{ + int ret = -EINVAL; + + ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_hx8238a"); + if (ret) + goto out_1; + + ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_hx8238a"); + if (ret) + goto out_2; + ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_hx8238a"); + if (ret) + goto out_3; + + /* Enable these pins as outputs */ + gpio_direction_output(lcd_spi_pins[SPI_MOSI], 0); + gpio_direction_output(lcd_spi_pins[SPI_SCLK], 0); + gpio_direction_output(lcd_spi_pins[SPI_CS], 1); + + return 0; + +out_3: + gpio_free(lcd_spi_pins[SPI_SCLK]); +out_2: + gpio_free(lcd_spi_pins[SPI_MOSI]); +out_1: + return ret; +} + +static void uninit_pinmux(void) +{ + stmp3xxx_release_pin_group(&lcd_pins, "lcd_hx8238a"); +} + +static void uninit_pinmux_spi(void) +{ + gpio_free(lcd_spi_pins[SPI_MOSI]); + gpio_free(lcd_spi_pins[SPI_SCLK]); + gpio_free(lcd_spi_pins[SPI_CS]); +} + +static struct clk *lcd_clk; + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct stmp3xxx_platform_fb_entry *pentry) +{ + int ret = 0; + + lcd_clk = clk_get(dev, "lcdif"); + if (IS_ERR(lcd_clk)) { + ret = PTR_ERR(lcd_clk); + goto out_1; + } + ret = clk_enable(lcd_clk); + if (ret) { + clk_put(lcd_clk); + goto out_1; + } + ret = clk_set_rate(lcd_clk, + 1000000/pentry->cycle_time_ns); /* kHz */ + if (ret) { + clk_disable(lcd_clk); + clk_put(lcd_clk); + goto out_1; + } + + ret = init_pinmux(); + if (ret) + goto out_1; + ret = init_pinmux_spi(); + if (ret) + goto out_2; + init_panel_hw(); + + ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 0); + if (ret) + goto out_3; + + setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE, + DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_H_ACTIVE, 1); + + stmp3xxx_lcd_set_bl_pdata(pentry->bl_data); + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry); + return 0; +out_3: + uninit_pinmux_spi(); +out_2: + uninit_pinmux(); +out_1: + return ret; +} + +static void release_panel(struct device *dev, + struct stmp3xxx_platform_fb_entry *pentry) +{ + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry); + uninit_pinmux_spi(); + uninit_pinmux(); + release_dotclk_panel(); + stmp3xxx_lcdif_dma_release(); + clk_disable(lcd_clk); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + stmp3xxx_clearl(BM_LCDIF_CTRL_RUN, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + break; + + case FB_BLANK_UNBLANK: + stmp3xxx_setl(BM_LCDIF_CTRL_RUN, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static struct stmp3xxx_platform_fb_entry fb_entry = { + .name = "hx8238a", + .x_res = 240, + .y_res = 320, + .bpp = 32, + .cycle_time_ns = 150, + .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = stmp3xxx_lcdif_run, + .stop_panel = stmp3xxx_lcdif_stop, + .pan_display = stmp3xxx_lcdif_pan_display, + .bl_data = &bl_data, +}; + +static struct clk *pwm_clk; +static int init_bl(struct stmp3xxx_platform_bl_data *data) +{ + int ret = 0; + + pwm_clk = clk_get(NULL, "pwm"); + if (IS_ERR(pwm_clk)) { + ret = PTR_ERR(pwm_clk); + goto out; + } + clk_enable(pwm_clk); + stmp3xxx_reset_block(REGS_PWM_BASE, 1); + + ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_hx8238a"); + if (ret) + goto out_mux; + stmp3xxx_pin_voltage(PINID_PWM2, PIN_12MA, "lcd_hx8238a"); + stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_hx8238a"); + + stmp3xxx_clearl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + stmp3xxx_setl(BM_PWM_CTRL_PWM2_ANA_CTRL_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + __raw_writel(BF(10, PWM_ACTIVEn_INACTIVE) | + BF(5, PWM_ACTIVEn_ACTIVE), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(1, PWM_PERIODn_CDIV) | /* divide by 2 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(14, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + return 0; + +out_mux: + clk_put(pwm_clk); +out: + return ret; +} + +static void free_bl(struct stmp3xxx_platform_bl_data *data) +{ + stmp3xxx_clearl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + stmp3xxx_release_pin(PINID_PWM2, "lcd_hx8238a"); + clk_disable(pwm_clk); + clk_put(pwm_clk); +} + +static void set_bl_intensity(struct stmp3xxx_platform_bl_data *data, + struct backlight_device *bd, int suspended) +{ + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (suspended) + intensity = 0; + + stmp3xxx_clearl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + if (intensity) { + HW_LRADC_CTRL2_CLR(BM_LRADC_CTRL2_BL_BRIGHTNESS); + HW_LRADC_CTRL2_SET(BM_LRADC_CTRL2_BL_ENABLE | + BM_LRADC_CTRL2_BL_MUX_SELECT | + BF(intensity - 1, LRADC_CTRL2_BL_BRIGHTNESS)); + stmp3xxx_setl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + } +} + +static struct stmp3xxx_platform_bl_data bl_data = { + .bl_max_intensity = (BM_LRADC_CTRL2_BL_BRIGHTNESS >> + BP_LRADC_CTRL2_BL_BRIGHTNESS) + 1, + .bl_default_intensity = 0x10, + .init_bl = init_bl, + .free_bl = free_bl, + .set_bl_intensity = set_bl_intensity, +}; + +static int __init register_devices(void) +{ + stmp3xxx_lcd_register_entry(&fb_entry, + stmp3xxx_framebuffer.dev.platform_data); + return 0; +} +subsys_initcall(register_devices); diff --git a/arch/arm/mach-stmp378x/lcd_lms350.c b/arch/arm/mach-stmp378x/lcd_lms350.c new file mode 100644 index 000000000000..c24a9b67c0da --- /dev/null +++ b/arch/arm/mach-stmp378x/lcd_lms350.c @@ -0,0 +1,520 @@ +/* + * Freescale STMP378X Samsung LMS350 LCD panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> + +#include <mach/regs-lcdif.h> +#include <mach/regs-lradc.h> +#include <mach/regs-pinctrl.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-pwm.h> +#include <mach/regs-apbh.h> +#include <mach/gpio.h> +#include <mach/pins.h> +#include <mach/pinmux.h> +#include <mach/lcdif.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> +#include <mach/cputype.h> + + +#define DOTCLK_H_ACTIVE 320 +#define DOTCLK_H_PULSE_WIDTH 3 +#define DOTCLK_HF_PORCH 5 +#define DOTCLK_HB_PORCH 4 +#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH)) +#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE) + +#define DOTCLK_V_PULSE_WIDTH 2 +#define DOTCLK_V_ACTIVE 240 +#define DOTCLK_VF_PORCH 2 +#define DOTCLK_VB_PORCH 5 +#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH) +#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT) + +static struct stmp3xxx_platform_bl_data bl_data; +extern struct pin_group lcd_pins; +extern unsigned lcd_spi_pins[]; + +static void spi_write(u32 val) +{ + u32 mask; + + gpio_set_value(lcd_spi_pins[SPI_MOSI], 0); + gpio_set_value(lcd_spi_pins[SPI_SCLK], 1); + gpio_set_value(lcd_spi_pins[SPI_CS], 0); + + for (mask = 0x00800000; mask != 0; mask >>= 1) { + gpio_set_value(lcd_spi_pins[SPI_SCLK], 0); + if (val & mask) + gpio_set_value(lcd_spi_pins[SPI_MOSI], 1); + else + gpio_set_value(lcd_spi_pins[SPI_MOSI], 0); + + gpio_set_value(lcd_spi_pins[SPI_SCLK], 1); + } + + udelay(10); + gpio_set_value(lcd_spi_pins[SPI_MOSI], 1); + gpio_set_value(lcd_spi_pins[SPI_SCLK], 1); + gpio_set_value(lcd_spi_pins[SPI_CS], 1); +} + +static void write_reg(u16 reg, u16 val) +{ + pr_debug("%s: writing %x to %x\n", __func__, reg, val); + spi_write(0x00740000 | reg); + spi_write(0x00760000 | val); +} + +static const unsigned short pon_seq[] = { + /* power on */ + 0x07, 0x0000, 20, + 0x12, 0x1618, 0, + 0x11, 0x222f, 0, + 0x13, 0x40ca, 0, + 0x10, 0x3108, 300, + 0x12, 0x1658, 250, + 0x01, 0x2b1d, 0, + 0x02, 0x0300, 0, + 0x03, 0xD040, 0, + 0x08, (DOTCLK_VB_PORCH + DOTCLK_V_PULSE_WIDTH) - 2, 0, + 0x09, ((DOTCLK_H_PULSE_WIDTH / 3) + DOTCLK_HB_PORCH) - 2, 0, + 0x76, 0x2213, 0, + 0x0b, 0x33e1, 0, + 0x0c, 0x0020, 0, + 0x76, 0x0000, 0, + 0x0d, 0x0000, 0, + 0x0e, 0x0000, 0, + 0x14, 0x0000, 0, + 0x15, 0x0803, 0, + 0x16, 0x0000, 0, + 0x30, 0x0209, 0, + 0x31, 0x0404, 0, + 0x32, 0x0e07, 0, + 0x33, 0x0602, 0, + 0x34, 0x0707, 0, + 0x35, 0x0707, 0, + 0x36, 0x0707, 0, + 0x37, 0x0206, 0, + 0x38, 0x0f06, 0, + 0x39, 0x0611, 20, +}; + +static const unsigned short don_seq[] = { + /* display on */ + 0x07, 0x0001, 150, + 0x07, 0x0101, 150, + 0x76, 0x2213, 0, + 0x1c, 0x6650, 0, + 0x0b, 0x33e0, 0, + 0x76, 0x0000, 0, + 0x07, 0x0103, 0, +}; + + +static const unsigned short doff_seq[] = { + /* display off */ + 0x0b, 0x33e1, 0, + 0x07, 0x0102, 150, + 0x07, 0x0100, 150, + 0x12, 0x0000, 0, + 0x10, 0x0000, 0, +}; + +static const unsigned short poff_seq[] = { + /* power off */ + /* called after display off */ + 0x07, 0x0000, 0, + 0x10, 0x0000, 0, + 0x11, 0x0000, 0, +}; + +static const unsigned short sby_seq[] = { + /* standby */ + /* called after display off */ + 0x10, 0x0001, 0 +}; + +static const unsigned short csby_seq[] = { + /* cancel standby */ + /* called after display on */ + 0x10, 0x0000, 0 +}; + +static void display_off(void) +{ + int i; + const unsigned short *seq; + + seq = doff_seq; + for (i = 0; i < ARRAY_SIZE(doff_seq); i += 3) { + write_reg(seq[i], seq[i + 1]); + if (seq[i + 2]) + udelay(seq[i + 2]); + } +} + +static void display_on(void) +{ + int i; + const unsigned short *seq; + + seq = don_seq; + for (i = 0; i < ARRAY_SIZE(don_seq); i += 3) { + write_reg(seq[i], seq[i + 1]); + if (seq[i + 2]) + udelay(seq[i + 2]); + } +} + +static void init_panel_hw(void) +{ + int i; + const unsigned short *seq; + + seq = pon_seq; + for (i = 0; i < ARRAY_SIZE(pon_seq); i += 3) { + write_reg(seq[i], seq[i + 1]); + if (seq[i + 2]) + udelay(seq[i + 2]); + } + display_on(); +} + +static int init_pinmux(void) +{ + return stmp3xxx_request_pin_group(&lcd_pins, "lcd_lms350"); +} + +static int init_pinmux_spi(void) +{ + int ret = -EINVAL; + + ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_lms350"); + if (ret) + goto out_1; + + ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_lms350"); + if (ret) + goto out_2; + ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_lms350"); + if (ret) + goto out_3; + + /* Enable these pins as outputs */ + gpio_direction_output(lcd_spi_pins[SPI_MOSI], 1); + gpio_direction_output(lcd_spi_pins[SPI_SCLK], 1); + gpio_direction_output(lcd_spi_pins[SPI_CS], 1); + + return 0; +out_3: + gpio_free(lcd_spi_pins[SPI_SCLK]); +out_2: + gpio_free(lcd_spi_pins[SPI_MOSI]); +out_1: + return ret; +} + +static void uninit_pinmux(void) +{ + stmp3xxx_release_pin_group(&lcd_pins, "lcd_lms350"); +} + +static void uninit_pinmux_spi(void) +{ + gpio_free(lcd_spi_pins[SPI_MOSI]); + gpio_free(lcd_spi_pins[SPI_SCLK]); + gpio_free(lcd_spi_pins[SPI_CS]); +} + +static struct clk *lcd_clk; + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct stmp3xxx_platform_fb_entry *pentry) +{ + int ret = 0; + + lcd_clk = clk_get(dev, "lcdif"); + if (IS_ERR(lcd_clk)) { + ret = PTR_ERR(lcd_clk); + goto out_1; + } + ret = clk_enable(lcd_clk); + if (ret) { + clk_put(lcd_clk); + goto out_1; + } + ret = clk_set_rate(lcd_clk, + 1000000/pentry->cycle_time_ns); /* kHz */ + if (ret) { + clk_disable(lcd_clk); + clk_put(lcd_clk); + goto out_1; + } + + /* + * Make sure we do a high-to-low transition to reset the panel. + * First make it low for 100 msec, hi for 10 msec, low for 10 msec, + * then hi. + */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* low */ + mdelay(100); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* high */ + mdelay(10); + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* low */ + + /* For the Samsung, Reset must be held low at least 30 uSec + * Therefore, we'll hold it low for about 10 mSec just to be sure. + * Then we'll wait 1 mSec afterwards. + */ + mdelay(10); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* high */ + mdelay(1); + + ret = init_pinmux(); + if (ret) + goto out_1; + ret = init_pinmux_spi(); + if (ret) + goto out_2; + init_panel_hw(); + + setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE, + DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD, + DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0); + + ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1); + if (ret) + goto out_3; + + stmp3xxx_lcd_set_bl_pdata(pentry->bl_data); + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry); + return 0; +out_3: + uninit_pinmux_spi(); +out_2: + uninit_pinmux(); +out_1: + return ret; +} + +static void release_panel(struct device *dev, + struct stmp3xxx_platform_fb_entry *pentry) +{ + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry); + display_off(); + uninit_pinmux_spi(); + uninit_pinmux(); + release_dotclk_panel(); + stmp3xxx_lcdif_dma_release(); + clk_disable(lcd_clk); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + stmp3xxx_clearl(BM_LCDIF_CTRL_BYPASS_COUNT, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + for (count = 10000; count; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + stmp3xxx_setl(BM_LCDIF_CTRL_BYPASS_COUNT, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static void stop_panel(void) +{ + stmp3xxx_lcdif_stop(); + display_off(); +} + +static void run_panel(void) +{ + display_on(); + stmp3xxx_lcdif_run(); +} + +static struct stmp3xxx_platform_fb_entry fb_entry = { + .name = "lms350", + .x_res = 240, + .y_res = 320, + .bpp = 32, + .cycle_time_ns = 200, + .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = run_panel, + .stop_panel = stop_panel, + .pan_display = stmp3xxx_lcdif_pan_display, + .bl_data = &bl_data, +}; + +static struct clk *pwm_clk; + +static int init_bl(struct stmp3xxx_platform_bl_data *data) +{ + int ret = 0; + + pwm_clk = clk_get(NULL, "pwm"); + if (IS_ERR(pwm_clk)) { + ret = PTR_ERR(pwm_clk); + goto out; + } + clk_enable(pwm_clk); + stmp3xxx_reset_block(REGS_PWM_BASE, 1); + + ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_lms350"); + if (ret) + goto out_mux; + + stmp3xxx_pin_voltage(PINID_PWM2, PIN_8MA, "lcd_lms350"); + stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_lms350"); + + __raw_writel(BF(0, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(599, PWM_PERIODn_PERIOD), REGS_PWM_BASE + HW_PWM_PERIODn(2)); + stmp3xxx_setl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + + return 0; + +out_mux: + clk_put(pwm_clk); +out: + return ret; +} + +static void free_bl(struct stmp3xxx_platform_bl_data *data) +{ + __raw_writel(BF(0, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(599, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + stmp3xxx_clearl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + stmp3xxx_pin_voltage(PINID_PWM2, PIN_4MA, "lcd_lms350"); + stmp3xxx_pin_strength(PINID_PWM2, PIN_1_8V, "lcd_lms350"); + + stmp3xxx_release_pin(PINID_PWM2, "lcd_lms350"); + clk_disable(pwm_clk); + clk_put(pwm_clk); +} + +static int values[] = { 6, 9, 12, 15, 19, 24, 30, 40, 55, 75, 100 }; +static int power[] = { + 0, 1500, 3600, 6100, 10300, + 15500, 74200, 114200, 155200, + 190100, 191000 +}; + +static int bl_to_power(int br) +{ + int base; + int rem; + + if (br > 100) + br = 100; + base = power[br/10]; + rem = br % 10; + if (!rem) + return base; + else + return base + (rem * (power[br/10 + 1]) - base) / 10; +} + +static int set_bl_intensity(struct stmp3xxx_platform_bl_data *data, + struct backlight_device *bd, int suspended) +{ + int intensity = bd->props.brightness; + int scaled_int; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (suspended) + intensity = 0; + + /* + * This is not too cool but what can we do? + * Luminance changes non-linearly... + */ + if (regulator_set_current_limit(data->regulator, bl_to_power(intensity), + bl_to_power(intensity))) + return -EBUSY; + + scaled_int = values[intensity/10]; + if (scaled_int < 100) { + int rem = intensity - 10 * (intensity/10); /* r = i % 10;*/ + scaled_int += rem*(values[intensity/10 + 1] - + values[intensity/10])/10; + } + __raw_writel(BF(scaled_int, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(599, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + return 0; +} + +static struct stmp3xxx_platform_bl_data bl_data = { + .bl_max_intensity = 100, + .bl_default_intensity = 50, + .bl_cons_intensity = 50, + .init_bl = init_bl, + .free_bl = free_bl, + .set_bl_intensity = set_bl_intensity, +}; + +static int __init register_devices(void) +{ + stmp3xxx_lcd_register_entry(&fb_entry, + stmp3xxx_framebuffer.dev.platform_data); + return 0; +} +subsys_initcall(register_devices); diff --git a/arch/arm/mach-stmp378x/lcd_lms430.c b/arch/arm/mach-stmp378x/lcd_lms430.c new file mode 100644 index 000000000000..bc30ff40f850 --- /dev/null +++ b/arch/arm/mach-stmp378x/lcd_lms430.c @@ -0,0 +1,363 @@ +/* + * Freescale STMP378X Samsung LMS430 LCD panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> + +#include <mach/regs-lcdif.h> +#include <mach/regs-lradc.h> +#include <mach/regs-pinctrl.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-pwm.h> +#include <mach/regs-apbh.h> +#include <mach/gpio.h> +#include <mach/pins.h> +#include <mach/lcdif.h> +#include <mach/pinmux.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> + +#define DOTCLK_H_ACTIVE 480 +#define DOTCLK_H_PULSE_WIDTH 1 +#define DOTCLK_HF_PORCH 8 +#define DOTCLK_HB_PORCH 15 +#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH)) +#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE) + +#define DOTCLK_V_PULSE_WIDTH 1 +#define DOTCLK_V_ACTIVE 272 +#define DOTCLK_VF_PORCH 4 +#define DOTCLK_VB_PORCH 12 +#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH) +#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT) + +static struct stmp3xxx_platform_bl_data bl_data; +extern struct pin_group lcd_pins; +extern unsigned lcd_spi_pins[]; + +static int init_pinmux(void) +{ + return stmp3xxx_request_pin_group(&lcd_pins, "lcd_lms430"); +} + +static int init_pinmux_spi(void) +{ + int ret = -EINVAL; + + ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_lms430"); + if (ret) + goto out_1; + + ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_lms430"); + if (ret) + goto out_2; + ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_lms430"); + if (ret) + goto out_3; + + /* Enable these pins as outputs */ + gpio_direction_output(lcd_spi_pins[SPI_MOSI], 1); + gpio_direction_output(lcd_spi_pins[SPI_SCLK], 1); + gpio_direction_output(lcd_spi_pins[SPI_CS], 1); + + return 0; +out_3: + gpio_free(lcd_spi_pins[SPI_SCLK]); +out_2: + gpio_free(lcd_spi_pins[SPI_MOSI]); +out_1: + return ret; +} + +static void uninit_pinmux(void) +{ + stmp3xxx_release_pin_group(&lcd_pins, "lcd_lms430"); +} + +static void uninit_pinmux_spi(void) +{ + gpio_free(lcd_spi_pins[SPI_MOSI]); + gpio_free(lcd_spi_pins[SPI_SCLK]); + gpio_free(lcd_spi_pins[SPI_CS]); +} + +static struct clk *lcd_clk; + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct stmp3xxx_platform_fb_entry *pentry) +{ + int ret = 0; + + lcd_clk = clk_get(dev, "lcdif"); + if (IS_ERR(lcd_clk)) { + ret = PTR_ERR(lcd_clk); + goto out_1; + } + ret = clk_enable(lcd_clk); + if (ret) { + clk_put(lcd_clk); + goto out_1; + } + ret = clk_set_rate(lcd_clk, 1000000 / pentry->cycle_time_ns); /* kHz */ + if (ret) { + clk_disable(lcd_clk); + clk_put(lcd_clk); + goto out_1; + } + + /* + * Make sure we do a high-to-low transition to reset the panel. + * First make it low for 100 msec, hi for 10 msec, low for 10 msec, + * then hi. + */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* low */ + mdelay(100); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* high */ + mdelay(10); + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* low */ + + /* For the Samsung, Reset must be held low at least 30 uSec + * Therefore, we'll hold it low for about 10 mSec just to be sure. + * Then we'll wait 1 mSec afterwards. + */ + mdelay(10); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* high */ + mdelay(1); + + ret = init_pinmux(); + if (ret) + goto out_1; + ret = init_pinmux_spi(); + if (ret) + goto out_2; + + setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE, + DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD, + DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0); + + ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1); + if (ret) + goto out_3; + + stmp3xxx_lcd_set_bl_pdata(pentry->bl_data); + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry); + return 0; +out_3: + uninit_pinmux_spi(); +out_2: + uninit_pinmux(); +out_1: + return ret; +} + +static void release_panel(struct device *dev, + struct stmp3xxx_platform_fb_entry *pentry) +{ + stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry); + uninit_pinmux_spi(); + uninit_pinmux(); + release_dotclk_panel(); + stmp3xxx_lcdif_dma_release(); + clk_disable(lcd_clk); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + stmp3xxx_clearl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + for (count = 10000; count; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & + BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + stmp3xxx_setl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static struct stmp3xxx_platform_fb_entry fb_entry = { + .name = "lms430", + .x_res = 272, + .y_res = 480, + .bpp = 32, + .cycle_time_ns = 150, + .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = stmp3xxx_lcdif_run, + .stop_panel = stmp3xxx_lcdif_stop, + .pan_display = stmp3xxx_lcdif_pan_display, + .bl_data = &bl_data, +}; + +static struct clk *pwm_clk; + +static int init_bl(struct stmp3xxx_platform_bl_data *data) +{ + int ret = 0; + + pwm_clk = clk_get(NULL, "pwm"); + if (IS_ERR(pwm_clk)) { + ret = PTR_ERR(pwm_clk); + goto out; + } + clk_enable(pwm_clk); + stmp3xxx_reset_block(REGS_PWM_BASE, 1); + + ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_lms430"); + if (ret) + goto out_mux; + + stmp3xxx_pin_voltage(PINID_PWM2, PIN_8MA, "lcd_lms430"); + stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_lms430"); + + __raw_writel(BF(0, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(599, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + stmp3xxx_setl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + + return 0; + +out_mux: + clk_put(pwm_clk); +out: + return ret; +} + +static void free_bl(struct stmp3xxx_platform_bl_data *data) +{ + __raw_writel(BF(0, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(599, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + stmp3xxx_clearl(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL); + stmp3xxx_pin_voltage(PINID_PWM2, PIN_4MA, "lcd_lms430"); + stmp3xxx_pin_strength(PINID_PWM2, PIN_1_8V, "lcd_lms430"); + + stmp3xxx_release_pin(PINID_PWM2, "lcd_lms430"); + clk_disable(pwm_clk); + clk_put(pwm_clk); +} + +static int values[] = { 0, 4, 9, 14, 20, 27, 35, 45, 57, 75, 100 }; + +static int power[] = { + 0, 1500, 3600, 6100, 10300, + 15500, 74200, 114200, 155200, + 190100, 191000 +}; + +static int bl_to_power(int br) +{ + int base; + int rem; + + if (br > 100) + br = 100; + base = power[br / 10]; + rem = br % 10; + if (!rem) + return base; + else + return base + (rem * (power[br / 10 + 1]) - base) / 10; +} + +static int set_bl_intensity(struct stmp3xxx_platform_bl_data *data, + struct backlight_device *bd, int suspended) +{ + int intensity = bd->props.brightness; + int scaled_int; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (suspended) + intensity = 0; + + /* + * This is not too cool but what can we do? + * Luminance changes non-linearly... + */ + if (regulator_set_current_limit + (data->regulator, bl_to_power(intensity), bl_to_power(intensity))) + return -EBUSY; + + scaled_int = values[intensity / 10]; + if (scaled_int < 100) { + int rem = intensity - 10 * (intensity / 10); /* r = i % 10; */ + scaled_int += rem * (values[intensity / 10 + 1] - + values[intensity / 10]) / 10; + } + __raw_writel(BF(scaled_int, PWM_ACTIVEn_INACTIVE) | + BF(0, PWM_ACTIVEn_ACTIVE), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF(6, PWM_PERIODn_CDIV) | /* divide by 64 */ + BF(2, PWM_PERIODn_INACTIVE_STATE) | /* low */ + BF(3, PWM_PERIODn_ACTIVE_STATE) | /* high */ + BF(399, PWM_PERIODn_PERIOD), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + return 0; +} + +static struct stmp3xxx_platform_bl_data bl_data = { + .bl_max_intensity = 100, + .bl_default_intensity = 50, + .bl_cons_intensity = 50, + .init_bl = init_bl, + .free_bl = free_bl, + .set_bl_intensity = set_bl_intensity, +}; + +static int __init register_devices(void) +{ + stmp3xxx_lcd_register_entry(&fb_entry, + stmp3xxx_framebuffer.dev.platform_data); + return 0; +} + +subsys_initcall(register_devices); diff --git a/arch/arm/mach-stmp378x/otp.c b/arch/arm/mach-stmp378x/otp.c new file mode 100644 index 000000000000..3dba6bb1a51b --- /dev/null +++ b/arch/arm/mach-stmp378x/otp.c @@ -0,0 +1,434 @@ +/* + * Unique ID manipulation: Freescale STMP378X OTP bits read/write procedures + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kobject.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/fcntl.h> +#include <linux/mutex.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <mach/unique-id.h> +#include <mach/regs-ocotp.h> +#include <mach/regs-power.h> +#include <mach/platform.h> + +static DEFINE_MUTEX(otp_mutex); +static unsigned otp_mode; +static unsigned long otp_hclk_saved; +static u32 otp_voltage_saved; + +static int otp_full; /* = 0. By default, show/set only customer bits */ +#define OTP_USER_OFFSET 0 +#define OTP_USER_SIZE 4 + +/** + * otp_wait_busy - wait for completion of operation + * + * @flags: flags that should be clear in addition to _BUSY and _ERROR + * + * Returns 0 on success or -ETIMEDOUT on error + **/ +static int otp_wait_busy(u32 flags) +{ + int count; + u32 c; + + for (count = 10000; count >= 0; count--) { + c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL); + if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags))) + break; + cpu_relax(); + } + if (count < 0) + return -ETIMEDOUT; + return 0; +} + +/** + * otp_open - open OTP bits for read or write access + * + * @mode: either O_RDONLY or O_WRONLY + * + * Returns 0 on success, error code otherwise + **/ +static int otp_open(int mode) +{ + int r; + struct clk *hclk; + int err; + + if (!mutex_trylock(&otp_mutex)) { + printk(KERN_ERR"%s: already opened\n", __func__); + return -EAGAIN; + } + + if (mode == O_RDONLY) { + pr_debug("%s: read-only mode\n", __func__); + + r = otp_wait_busy(0); + if (r) { + err = -ETIMEDOUT; + goto out; + } + + /* 2. Set RD_BANK_OPEN */ + stmp3xxx_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, REGS_OCOTP_BASE + HW_OCOTP_CTRL); + udelay(10); + + otp_wait_busy(0); + } + + else if (mode == O_WRONLY) { + pr_debug("%s: write-only mode\n", __func__); + hclk = clk_get(NULL, "hclk"); + if (IS_ERR(hclk)) { + err = PTR_ERR(hclk); + goto out; + } + + /* + WARNING ACHTUNG UWAGA + + the code below changes HCLK clock rate to 24M. This is + required to write OTP bits (7.2.2 in STMP378x Target + Specification), and might affect LCD operation, for example. + Moreover, this hacky code changes VDDIO to 2.8V; and resto- + res it only on otp_close(). This may affect... anything. + + You are warned now. + */ + otp_hclk_saved = clk_get_rate(hclk); + clk_set_rate(hclk, 24000); + /* Set the voltage to 2.8V */ + otp_voltage_saved = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + __raw_writel( + (otp_voltage_saved & ~BM_POWER_VDDIOCTRL_TRG) | 0x00, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + r = otp_wait_busy(BM_OCOTP_CTRL_RD_BANK_OPEN); + if (r < 0) { + __raw_writel(otp_voltage_saved, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + clk_set_rate(hclk, otp_hclk_saved); + clk_put(hclk); + err = -ETIMEDOUT; + goto out; + } + + clk_put(hclk); + } + + else { + pr_debug("%s: unknown mode '%d'\n", __func__, mode); + err = -EINVAL; + goto out; + } + + otp_mode = mode; + return 0; +out: + mutex_unlock(&otp_mutex); + pr_debug("%s: status %d\n", __func__, err); + return err; +} + +/** + * otp_close - close the OTP bits after opening by otp_open + **/ +static void otp_close(void) +{ + struct clk *hclk; + + if (!mutex_is_locked(&otp_mutex)) { + printk(KERN_ERR"%s: wasn't opened\n", __func__); + return; + } + + if (otp_mode == O_RDONLY) { + /* 5. clear RD_BANK_OPEN */ + stmp3xxx_clearl(BM_OCOTP_CTRL_RD_BANK_OPEN, REGS_OCOTP_BASE + HW_OCOTP_CTRL); + } + + else if (otp_mode == O_WRONLY) { + hclk = clk_get(NULL, "hclk"); + clk_set_rate(hclk, otp_hclk_saved); + __raw_writel(otp_voltage_saved, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + otp_wait_busy(0); + stmp3xxx_setl(BM_OCOTP_CTRL_RELOAD_SHADOWS, REGS_OCOTP_BASE + HW_OCOTP_CTRL); + otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS); + } + + else { + return; /* -EINVAL. Who does really check close? */ + } + + otp_mode = 0; + mutex_unlock(&otp_mutex); +} + +/** + * otp_read_bits - read the content of OTP + * + * @start: offset from 0, in u32's + * @len: number of OTP u32's to read + * @bits: caller-allocated buffer to save bits + * @size: size of @bits + * + * Returns number of u32's saved to buffer + **/ +static size_t otp_read_bits(int start, int len, u32 *bits, size_t size) +{ + int ofs; + + BUG_ON(!mutex_is_locked(&otp_mutex)); + + /* read all stuff that caller needs */ + if (start + len > 4 * 8) /* 4 banks, 8 registers each */ + len = 4 * 8 - start; + + for (ofs = start; ofs < len; ofs++) { + if (size/sizeof(*bits) <= 0) /* we drained out the buffer */ + break; + *bits = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTn(ofs)); + bits++; + size -= sizeof(*bits); + } + + return ofs - start; /* number of u32's that we saved to buffer */ +} + +/** + * otp_write_bits - store OTP bits + * + * @offset: offset from 0, in u32's + * @data: the u32 to write + * @magic: the magic value to be stored in UNLOCK field + * + **/ +static int otp_write_bits(int offset, u32 data, u32 magic) +{ + u32 c; + int r; + + BUG_ON(!mutex_is_locked(&otp_mutex)); + + if (offset < 0 || offset > 0x1F) + return -EINVAL; + + c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL); + c &= ~BM_OCOTP_CTRL_ADDR; + c |= BF(offset, OCOTP_CTRL_ADDR); + c |= BF(magic, OCOTP_CTRL_WR_UNLOCK); + __raw_writel(c, REGS_OCOTP_BASE + HW_OCOTP_CTRL); + + __raw_writel(data, REGS_OCOTP_BASE + HW_OCOTP_DATA); + + r = otp_wait_busy(0); + if (r < 0) + return r; + + udelay(2); + return 0; +} + +static ssize_t otp_id_show(void *context, char *page, int ascii) +{ + char s[60]; + int ret; + int n, i, j, r; + u32 otp_bits[4 * 8]; + + r = otp_open(O_RDONLY); + if (r < 0) + return 0; + n = otp_read_bits(0, 4 * 8, otp_bits, sizeof(otp_bits)); + otp_close(); + + ret = 0; + + + if (ascii) { + + strcpy(page, ""); + ret = 0; + + if (otp_full) { + for (i = 0; i < 4; i++) { + + ret += sprintf(s, "Bank %d: ", i); + strcat(page, s); + + for (j = 0; j < 8; j++) { + + if (i * 4 + j > n) + break; + ret += sprintf(s, "%08X ", + otp_bits[i * 4 + j]); + strcat(page, s); + } + + strcat(page, "\n"); + ret++; + } + } else { + for (i = 0; i < OTP_USER_SIZE; i++) { + ret += sprintf(s, "%08X ", + otp_bits[i + OTP_USER_OFFSET]); + strcat(page, s); + } + strcat(page, "\n"); + ret++; + } + } else { + + if (otp_full) { + memcpy(page, otp_bits, sizeof(otp_bits)); + ret = sizeof(otp_bits); + } else { + memcpy(page, otp_bits + OTP_USER_OFFSET, + OTP_USER_SIZE * sizeof(u32)); + ret = OTP_USER_SIZE * sizeof(u32); + } + } + + return ret; +} + +static int otp_check_dry_run(const char *page, size_t count) +{ + if (count >= 3 && memcmp(page, "+++", 3) == 0) + return 3; + return 0; +} + +static ssize_t otp_id_store(void *context, const char *page, + size_t count, int ascii) +{ + int r = 0; + const char *p, *cp, *d; + unsigned long index, value; + char tmps[20]; /* subject of strtoul */ + int dry_run; + + r = otp_open(O_WRONLY); + if (r < 0) { + printk(KERN_ERR"Cannot open OTP in WRITE mode\n"); + return r; + } + + if (ascii) { + + dry_run = otp_check_dry_run(page, count); + if (dry_run > 0) + page += dry_run; + + index = 0; + cp = page; + + memset(tmps, 0, sizeof(tmps)); + + for (index = 0, cp = page; cp != NULL; index++) { + p = strchr(cp, ','); + + d = strchr(cp, ':'); + if (d && (!p || d < p)) { + strncpy(tmps, cp, + min_t(int, d - cp, sizeof(tmps) - 1)); + r = strict_strtoul(tmps, 0, &index); + if (r < 0) { + pr_debug("Cannot parse '%s'\n", tmps); + break; + } + cp = d + 1; + } + + memset(tmps, 0, sizeof(tmps)); + + if (!p) + strncpy(tmps, cp, sizeof(tmps)); + else + strncpy(tmps, cp, + min_t(int, p - cp, sizeof(tmps) - 1)); + r = strict_strtoul(tmps, 0, &value); + if (r < 0) { + pr_debug("Cannot parse '%s'\n", tmps); + break; + } + + memset(tmps, 0, sizeof(tmps)); + + cp = p ? ++p : NULL; + + if (!otp_full) { + index += OTP_USER_OFFSET; + if (index > OTP_USER_SIZE) { + printk(KERN_ERR"Cannot write at " + "offset %ld\n", index); + continue; + } + } + + r = 0; + if (!dry_run) { + pr_debug("Index %ld, value 0x%08lx\n", + index, value); + r = otp_write_bits(index, value, 0x3e77); + } else + printk(KERN_NOTICE + "Dry-run: writing 0x%08lX => [%ld]\n", + value, index); + if (r < 0) + break; + } + } else { + printk(KERN_ERR"Binary write is not supported\n"); + r = -ENOSYS; + } + otp_close(); + return (r >= 0) ? count : r; +} + +static struct uid_ops otp_ops = { + .id_show = otp_id_show, + .id_store = otp_id_store, +}; + +static int __init_or_module otp_init(void) +{ + void *p; + + mutex_init(&otp_mutex); + p = uid_provider_init("otp", &otp_ops, NULL); + if (IS_ERR(p)) + return PTR_ERR(p); + return 0; +} + +static void __exit otp_remove(void) +{ + uid_provider_remove("otp"); +} + +module_param(otp_full, int, 0600); +module_init(otp_init); +module_exit(otp_remove); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>"); +MODULE_DESCRIPTION("Unique ID: OTP"); diff --git a/arch/arm/mach-stmp378x/persistent.c b/arch/arm/mach-stmp378x/persistent.c new file mode 100644 index 000000000000..84bc8089c145 --- /dev/null +++ b/arch/arm/mach-stmp378x/persistent.c @@ -0,0 +1,257 @@ +/* + * Freescale STMP378X Persistent bits manipulation driver + * + * Author: Pantelis Antoniou <pantelis@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/sysdev.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> + +#include <mach/regs-rtc.h> + +struct stmp3xxx_persistent_data { + struct device *dev; + struct stmp3xxx_platform_persistent_data *pdata; + int count; + struct attribute_group attr_group; + /* attribute ** follow */ + /* device_attribute follow */ +}; + +#define pd_attribute_ptr(x) \ + ((struct attribute **)((x) + 1)) +#define pd_device_attribute_ptr(x) \ + ((struct device_attribute *)(pd_attribute_ptr(x) + (x)->count + 1)) + +static inline u32 persistent_reg_read(int reg) +{ + u32 msk; + + /* wait for stable value */ + msk = BF(0x01 << reg, RTC_STAT_STALE_REGS); + while (__raw_readl(REGS_RTC_BASE + HW_RTC_STAT) & msk) + cpu_relax(); + + return __raw_readl(REGS_RTC_BASE + 0x60 + (reg * 0x10)); +} + +static inline void persistent_reg_wait_settle(int reg) +{ + u32 msk; + + /* wait until the change is propagated */ + msk = BF(0x01 << reg, RTC_STAT_NEW_REGS); + while (__raw_readl(REGS_RTC_BASE + HW_RTC_STAT) & msk) + cpu_relax(); +} + +static inline void persistent_reg_write(u32 val, int reg) +{ + __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10)); + persistent_reg_wait_settle(reg); +} + +static inline void persistent_reg_set(u32 val, int reg) +{ + __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0x4); + persistent_reg_wait_settle(reg); +} + +static inline void persistent_reg_clr(u32 val, int reg) +{ + __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0x8); + persistent_reg_wait_settle(reg); +} + +static inline void persistent_reg_tog(u32 val, int reg) +{ + __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0xc); + persistent_reg_wait_settle(reg); +} + +static ssize_t +persistent_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmp3xxx_persistent_data *pd = platform_get_drvdata(pdev); + struct device_attribute *devattr = pd_device_attribute_ptr(pd); + const struct stmp3xxx_persistent_bit_config *pb; + int idx; + u32 val; + + idx = attr - devattr; + if ((unsigned int)idx >= pd->count) + return -EINVAL; + + pb = &pd->pdata->bit_config_tab[idx]; + + /* read value and shift */ + val = persistent_reg_read(pb->reg); + val >>= pb->start; + val &= (1 << pb->width) - 1; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +persistent_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmp3xxx_persistent_data *pd = platform_get_drvdata(pdev); + struct device_attribute *devattr = pd_device_attribute_ptr(pd); + const struct stmp3xxx_persistent_bit_config *pb; + int idx, r; + unsigned long val, msk; + + idx = attr - devattr; + if ((unsigned int)idx >= pd->count) + return -EINVAL; + + pb = &pd->pdata->bit_config_tab[idx]; + + /* get value to write */ + r = strict_strtoul(buf, 10, &val); + if (r != 0) + return r; + + /* verify it fits */ + if ((unsigned int)val > (1 << pb->width) - 1) + return -EINVAL; + + /* lockless update, first clear the area */ + msk = ((1 << pb->width) - 1) << pb->start; + persistent_reg_clr(msk, pb->reg); + + /* shift into position */ + val <<= pb->start; + persistent_reg_set(val, pb->reg); + + return count; +} + + +static int __devinit stmp3xxx_persistent_probe(struct platform_device *pdev) +{ + struct stmp3xxx_persistent_data *pd; + struct stmp3xxx_platform_persistent_data *pdata; + const struct stmp3xxx_persistent_bit_config *pb; + struct attribute **attr; + struct device_attribute *devattr; + int i, cnt, size; + int err; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + cnt = pdata->bit_config_cnt; + size = sizeof(*pd) + + (cnt + 1) * sizeof(struct atrribute *) + + cnt * sizeof(struct device_attribute); + pd = kzalloc(size, GFP_KERNEL); + if (pd == NULL) + return -ENOMEM; + pd->dev = &pdev->dev; + pd->pdata = pdata; + platform_set_drvdata(pdev, pd); + pd->count = cnt; + attr = pd_attribute_ptr(pd); + devattr = pd_device_attribute_ptr(pd); + + /* build the attributes structures */ + pd->attr_group.attrs = attr; + pb = pdata->bit_config_tab; + for (i = 0; i < cnt; i++) { + devattr[i].attr.name = pb[i].name; + devattr[i].attr.mode = S_IWUSR | S_IRUGO; + devattr[i].show = persistent_show; + devattr[i].store = persistent_store; + attr[i] = &devattr[i].attr; + } + + err = sysfs_create_group(&pdev->dev.kobj, &pd->attr_group); + if (err != 0) { + platform_set_drvdata(pdev, NULL); + kfree(pd); + return err; + } + + return 0; +} + +static int stmp3xxx_persistent_remove(struct platform_device *pdev) +{ + struct stmp3xxx_persistent_data *pd; + + pd = platform_get_drvdata(pdev); + sysfs_remove_group(&pdev->dev.kobj, &pd->attr_group); + platform_set_drvdata(pdev, NULL); + kfree(pd); + + return 0; +} + +#ifdef CONFIG_PM +static int +stmp3xxx_persistent_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int stmp3xxx_persistent_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define stmp3xxx_persistent_suspend NULL +#define stmp3xxx_persistent_resume NULL +#endif + +static struct platform_driver stmp3xxx_persistent_driver = { + .probe = stmp3xxx_persistent_probe, + .remove = stmp3xxx_persistent_remove, + .suspend = stmp3xxx_persistent_suspend, + .resume = stmp3xxx_persistent_resume, + .driver = { + .name = "stmp3xxx-persistent", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_persistent_init(void) +{ + return platform_driver_register(&stmp3xxx_persistent_driver); +} + +static void __exit stmp3xxx_persistent_exit(void) +{ + platform_driver_unregister(&stmp3xxx_persistent_driver); +} + +MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>"); +MODULE_DESCRIPTION("Persistent bits user-access driver"); +MODULE_LICENSE("GPL"); + +module_init(stmp3xxx_persistent_init); +module_exit(stmp3xxx_persistent_exit); diff --git a/arch/arm/mach-stmp378x/pm.c b/arch/arm/mach-stmp378x/pm.c new file mode 100644 index 000000000000..bbd9fee881a0 --- /dev/null +++ b/arch/arm/mach-stmp378x/pm.c @@ -0,0 +1,439 @@ +/* + * Static Power Management support for Freescale STMP37XX/STMP378X + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/suspend.h> +#include <linux/rtc.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kthread.h> + +#include <asm/cacheflush.h> +#include <asm/mach-types.h> + +#include <asm/mach/time.h> + +#include <mach/platform.h> +#include <mach/dma.h> +#include <mach/regs-icoll.h> +#include <mach/regs-rtc.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-pinctrl.h> +#include <mach/regs-power.h> + +#include "sleep.h" + +#define PENDING_IRQ_RETRY 100 +static void *saved_sram; +static int saved_sleep_state; + +static inline void do_standby(void) +{ + void (*stmp37xx_cpu_standby_ptr) (void); + struct clk *cpu_clk; + struct clk *osc_clk; + struct clk *pll_clk; + struct clk *hbus_clk; + struct clk *cpu_parent = NULL; + int cpu_rate = 0; + int hbus_rate = 0; + int i, pending_irq; + + /* + * 1) switch clock domains from PLL to 24MHz + * 2) lower voltage (TODO) + * 3) switch EMI to 24MHz and turn PLL off (done in sleep.S) + */ + + /* save portion of SRAM to be used by suspend function. */ + memcpy(saved_sram, (void *)STMP3XXX_OCRAM_BASE, + stmp_standby_alloc_sz); + + /* make sure SRAM copy gets physically written into SDRAM. + * SDRAM will be placed into self-refresh during power down + */ + flush_cache_all(); + + /* copy suspend function into SRAM */ + memcpy((void *)STMP3XXX_OCRAM_BASE, stmp37xx_cpu_standby, + stmp_standby_alloc_sz); + + /* now switch the CPU to ref_xtal */ + cpu_clk = clk_get(NULL, "cpu"); + osc_clk = clk_get(NULL, "osc_24M"); + pll_clk = clk_get(NULL, "pll"); + hbus_clk = clk_get(NULL, "hclk"); + + if (!IS_ERR(cpu_clk) && !IS_ERR(osc_clk)) { + cpu_rate = clk_get_rate(cpu_clk); + cpu_parent = clk_get_parent(cpu_clk); + hbus_rate = clk_get_rate(hbus_clk); + clk_set_parent(cpu_clk, osc_clk); + } + + local_irq_disable(); + local_fiq_disable(); + + stmp3xxx_dma_suspend(); + stmp3xxx_suspend_timer(); + + stmp3xxx_setl(BM_POWER_CTRL_ENIRQ_PSWITCH, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_setl(BM_ICOLL_INTERRUPTn_ENABLE, REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn(IRQ_VDD5V)); + + /* clear pending interrupt, if any */ + for (i = 0; i < PENDING_IRQ_RETRY; i++) { + pending_irq = __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_STAT) & 0x7f; + if (pending_irq == 0x7f) + break; + pr_info("irqn = %u\n", pending_irq); + /* Tell ICOLL to release IRQ line */ + __raw_writel(0x0, REGS_ICOLL_BASE + HW_ICOLL_VECTOR); + /* ACK current interrupt */ + __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0, + REGS_ICOLL_BASE + HW_ICOLL_LEVELACK); + /* Barrier */ + (void) __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_STAT); + } + /* do suspend */ + stmp37xx_cpu_standby_ptr = (void *)STMP3XXX_OCRAM_BASE; + stmp37xx_cpu_standby_ptr(); + + pr_info("wakeup irq source = %d\n", __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_STAT)); + saved_sleep_state = 0; /* waking from standby */ + stmp3xxx_clearl(BM_POWER_CTRL_PSWITCH_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_resume_timer(); + stmp3xxx_dma_resume(); + + local_fiq_enable(); + local_irq_enable(); + + if (cpu_parent) { + clk_set_parent(cpu_clk, cpu_parent); + clk_set_rate(cpu_clk, cpu_rate); + clk_set_rate(hbus_clk, hbus_rate); + } + + clk_put(hbus_clk); + clk_put(pll_clk); + clk_put(osc_clk); + clk_put(cpu_clk); + + /* restoring portion of SRAM that was used by suspend function */ + memcpy((void *)STMP3XXX_OCRAM_BASE, saved_sram, + stmp_standby_alloc_sz); +} + +static u32 clk_regs[] = { + HW_CLKCTRL_PLLCTRL0, + HW_CLKCTRL_XTAL, + HW_CLKCTRL_PIX, + HW_CLKCTRL_SSP, + HW_CLKCTRL_GPMI, + HW_CLKCTRL_FRAC, + HW_CLKCTRL_CLKSEQ, +}; + +static noinline void do_mem(void) +{ + void (*stmp37xx_cpu_suspend_ptr) (u32); + struct sleep_data saved_context; + int i; + struct clk *cpu_clk; + struct clk *osc_clk; + struct clk *pll_clk; + struct clk *hbus_clk; + int cpu_rate = 0; + int hbus_rate = 0; + + saved_context.fingerprint = SLEEP_DATA_FINGERPRINT; + + saved_context.old_c00 = __raw_readl(0xC0000000); + saved_context.old_c04 = __raw_readl(0xC0000004); + __raw_writel((u32)&saved_context, (void *)0xC0000000); + + local_irq_disable(); + local_fiq_disable(); + + stmp3xxx_dma_suspend(); + stmp3xxx_suspend_timer(); + + /* clocks */ + for (i = 0; i < ARRAY_SIZE(clk_regs); i++) + saved_context.clks[i] = + __raw_readl(clk_regs[i]); + + /* interrupt collector */ + saved_context.icoll_ctrl = __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_CTRL); + if (machine_is_stmp37xx()) { +#ifdef CONFIG_MACH_STMP37XX + for (i = 0; i < 16; i++) + saved_context.icoll.prio[i] = __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_PRIORITYn(i)); +#endif + } else if (machine_is_stmp378x()) { +#ifdef CONFIG_MACH_STMP378X + for (i = 0; i < 128; i++) + saved_context.icoll.intr[i] = __raw_readl(REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn(i)); +#endif + } + + /* save pinmux state */ + for (i = 0; i < 0x100; i++) + saved_context.pinmux[i] = + __raw_readl(REGS_PINCTRL_BASE + (i<<4)); + + cpu_clk = clk_get(NULL, "cpu"); + osc_clk = clk_get(NULL, "osc_24M"); + pll_clk = clk_get(NULL, "pll"); + hbus_clk = clk_get(NULL, "hclk"); + + cpu_rate = clk_get_rate(cpu_clk); + hbus_rate = clk_get_rate(hbus_clk); + + /* save portion of SRAM to be used by suspend function. */ + memcpy(saved_sram, (void *)STMP3XXX_OCRAM_BASE, stmp_s2ram_alloc_sz); + + /* set the PERSISTENT_SLEEP_BIT for bootloader */ + stmp3xxx_setl(1 << 10, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); /* XXX: temp */ + + /* + * make sure SRAM copy gets physically written into SDRAM. + * SDRAM will be placed into self-refresh during power down + */ + flush_cache_all(); + + /*copy suspend function into SRAM */ + memcpy((void *)STMP3XXX_OCRAM_BASE, stmp37xx_cpu_suspend, + stmp_s2ram_alloc_sz); + + /* do suspend */ + stmp37xx_cpu_suspend_ptr = (void *)STMP3XXX_OCRAM_BASE; + stmp37xx_cpu_suspend_ptr(0); + + saved_sleep_state = 1; /* waking from non-standby state */ + + /* restoring portion of SRAM that was used by suspend function */ + memcpy((void *)STMP3XXX_OCRAM_BASE, saved_sram, stmp_s2ram_alloc_sz); + + /* clocks */ + for (i = 0; i < ARRAY_SIZE(clk_regs); i++) + __raw_writel(saved_context.clks[i], + clk_regs[i]); + + /* interrupt collector */ + __raw_writel(saved_context.icoll_ctrl, REGS_ICOLL_BASE + HW_ICOLL_CTRL); + if (machine_is_stmp37xx()) { +#ifdef CONFIG_MACH_STMP37XX + for (i = 0; i < 16; i++) + __raw_writel(saved_context.icoll.prio[i], REGS_ICOLL_BASE + HW_ICOLL_PRIORITYn(i)); +#endif + } else if (machine_is_stmp378x()) { +#ifdef CONFIG_MACH_STMP378X + for (i = 0; i < 128; i++) + __raw_writel(saved_context.icoll.intr[i], REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn(i)); +#endif + } + + /* restore pinmux state */ + for (i = 0; i < 0x100; i++) + __raw_writel(saved_context.pinmux[i], + REGS_PINCTRL_BASE + (i<<4)); + + clk_set_rate(cpu_clk, cpu_rate); + clk_set_rate(hbus_clk, hbus_rate); + + __raw_writel(saved_context.old_c00, 0xC0000000); + __raw_writel(saved_context.old_c04, 0xC0000004); + + clk_put(hbus_clk); + clk_put(pll_clk); + clk_put(osc_clk); + clk_put(cpu_clk); + + stmp3xxx_resume_timer(); + stmp3xxx_dma_resume(); + + local_fiq_enable(); + local_irq_enable(); +} + +static int stmp37xx_pm_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + do_standby(); + break; + case PM_SUSPEND_MEM: + do_mem(); + break; + } + return 0; +} + +static int stmp37xx_pm_valid(suspend_state_t state) +{ + return (state == PM_SUSPEND_STANDBY) || + (state == PM_SUSPEND_MEM); +} + +static suspend_state_t saved_state; + +static int stmp37xx_pm_begin(suspend_state_t state) +{ + saved_state = state; + return 0; +} + +static void stmp37xx_pm_end(void) +{ + /*XXX: Nothing to do */ +} + +suspend_state_t stmp37xx_pm_get_target(void) +{ + return saved_state; +} +EXPORT_SYMBOL(stmp37xx_pm_get_target); + +/** + * stmp37xx_pm_get_sleep_state - get sleep state we waking from + * + * returns boolean: 0 if waking up from standby, 1 otherwise + */ +int stmp37xx_pm_sleep_was_deep(void) +{ + return saved_sleep_state; +} +EXPORT_SYMBOL(stmp37xx_pm_sleep_was_deep); + +static struct platform_suspend_ops stmp37xx_suspend_ops = { + .enter = stmp37xx_pm_enter, + .valid = stmp37xx_pm_valid, + .begin = stmp37xx_pm_begin, + .end = stmp37xx_pm_end, +}; + +void stmp37xx_pm_idle(void) +{ + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + + stmp3xxx_setl(1<<12, REGS_CLKCTRL_BASE + HW_CLKCTRL_CPU); + __asm__ __volatile__ ("mcr p15, 0, r0, c7, c0, 4"); + + local_fiq_enable(); + local_irq_enable(); +} + +static void stmp37xx_pm_power_off(void) +{ + __raw_writel((0x3e77 << 16) | 1, REGS_POWER_BASE + HW_POWER_RESET); +} + +struct stmp37xx_pswitch_state { + int dev_running; +}; + +static DECLARE_COMPLETION(suspend_request); + +static int suspend_thread_fn(void *data) +{ + while (1) { + wait_for_completion(&suspend_request); + pm_suspend(PM_SUSPEND_STANDBY); + } + return 0; +} + +static struct stmp37xx_pswitch_state pswitch_state = { + .dev_running = 0, +}; + +static irqreturn_t pswitch_interrupt(int irq, void *dev) +{ + int pin_value, i; + + /* check if irq by pswitch */ + if (!(__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & BM_POWER_CTRL_PSWITCH_IRQ)) + return IRQ_HANDLED; + for (i = 0; i < 3000; i++) { + pin_value = __raw_readl(REGS_POWER_BASE + HW_POWER_STS) & + BF(0x1, POWER_STS_PSWITCH); + if (pin_value == 0) + break; + mdelay(1); + } + if (i < 3000) { + pr_info("pswitch goto suspend\n"); + complete(&suspend_request); + } else { + pr_info("release pswitch to power down\n"); + for (i = 0; i < 5000; i++) { + pin_value = __raw_readl(REGS_POWER_BASE + HW_POWER_STS) & + BF(0x1, POWER_STS_PSWITCH); + if (pin_value == 0) + break; + mdelay(1); + } + pr_info("pswitch power down\n"); + stmp37xx_pm_power_off(); + } + stmp3xxx_clearl(BM_POWER_CTRL_PSWITCH_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + return IRQ_HANDLED; +} + +static struct irqaction pswitch_irq = { + .name = "pswitch", + .flags = IRQF_DISABLED | IRQF_SHARED, + .handler = pswitch_interrupt, + .dev_id = &pswitch_state, +}; + +static void init_pswitch(void) +{ + kthread_run(suspend_thread_fn, NULL, "pswitch"); + stmp3xxx_clearl(BM_POWER_CTRL_PSWITCH_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_setl(BM_POWER_CTRL_POLARITY_PSWITCH | + BM_POWER_CTRL_ENIRQ_PSWITCH, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_clearl(BM_POWER_CTRL_PSWITCH_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + setup_irq(IRQ_VDD5V, &pswitch_irq); +} + +static int __init stmp37xx_pm_init(void) +{ + saved_sram = kmalloc(0x4000, GFP_ATOMIC); + if (!saved_sram) { + printk(KERN_ERR + "PM Suspend: can't allocate memory to save portion of SRAM\n"); + return -ENOMEM; + } + + pm_power_off = stmp37xx_pm_power_off; + pm_idle = stmp37xx_pm_idle; + suspend_set_ops(&stmp37xx_suspend_ops); + init_pswitch(); + return 0; +} + +late_initcall(stmp37xx_pm_init); diff --git a/arch/arm/mach-stmp378x/power.c b/arch/arm/mach-stmp378x/power.c new file mode 100644 index 000000000000..57d89b586743 --- /dev/null +++ b/arch/arm/mach-stmp378x/power.c @@ -0,0 +1,537 @@ +/* + * Freescale STMP378X voltage regulator low-level driver + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* #define DEBUG */ + +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/machine.h> +#include <mach/platform.h> +#include <mach/power.h> +#include <mach/regulator.h> +#include <mach/regs-power.h> +#include <mach/stmp3xxx.h> + +static int get_voltage(struct stmp3xxx_regulator *sreg) +{ + struct stmp3xxx_platform_regulator_data *rdata = sreg->rdata; + u32 val = __raw_readl(rdata->control_reg) & 0x1f; + int uv = rdata->min_voltage + val * + (rdata->max_voltage - rdata->min_voltage) / 0x1f; + return uv; +} + +static int get_bo_voltage(struct stmp3xxx_regulator *sreg) +{ + int uv; + int offs; + + if (!sreg->parent) + return -EINVAL; + + uv = get_voltage(sreg->parent); + offs = (__raw_readl(sreg->parent->rdata->control_reg) & ~0x700) >> 8; + return uv - 25000*offs; +} + +static int set_voltage(struct stmp3xxx_regulator *sreg, int uv) +{ + u32 val, reg, i; + + pr_debug("%s: uv %d, min %d, max %d\n", __func__, + uv, sreg->rdata->min_voltage, sreg->rdata->max_voltage); + + if (uv < sreg->rdata->min_voltage || uv > sreg->rdata->max_voltage) + return -EINVAL; + + val = (uv - sreg->rdata->min_voltage) * 0x1f / + (sreg->rdata->max_voltage - sreg->rdata->min_voltage); + reg = (__raw_readl(sreg->rdata->control_reg) & ~0x1f); + pr_debug("%s: calculated val %d\n", __func__, val); + __raw_writel(val | reg, sreg->rdata->control_reg); + for (i = 20; i; i--) { + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_DC_OK) + break; + udelay(1); + } + + if (i) + goto out; + + __raw_writel(val | reg, sreg->rdata->control_reg); + for (i = 40000; i; i--) { + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_DC_OK) + break; + udelay(1); + } + + if (i) + goto out; + + for (i = 40000; i; i--) { + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_DC_OK) + break; + udelay(1); + } + +out: + return !i; +} + +static int set_bo_voltage(struct stmp3xxx_regulator *sreg, int bo_uv) +{ + int uv; + int offs; + u32 reg; + int i; + + if (!sreg->parent) + return -EINVAL; + + uv = get_voltage(sreg->parent); + offs = (uv - bo_uv) / 25000; + if (offs < 0 || offs > 7) + return -EINVAL; + + reg = (__raw_readl(sreg->parent->rdata->control_reg) & ~0x700); + pr_debug("%s: calculated offs %d\n", __func__, offs); + __raw_writel((offs << 8) | reg, sreg->parent->rdata->control_reg); + + for (i = 10000; i; i--) { + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_DC_OK) + break; + udelay(1); + } + + if (i) + goto out; + + for (i = 10000; i; i--) { + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_DC_OK) + break; + udelay(1); + } + +out: + return !i; +} + +static int enable(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 0; +} + +static int disable(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 0; +} + +static int is_enabled(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 1; +} + +static int set_mode(struct stmp3xxx_regulator *sreg, int mode) +{ + int ret = 0; + u32 val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = __raw_readl(sreg->rdata->control_reg); + __raw_writel(val | (1 << 17), sreg->rdata->control_reg); + break; + + case REGULATOR_MODE_NORMAL: + val = __raw_readl(sreg->rdata->control_reg); + __raw_writel(val & ~(1<<17), sreg->rdata->control_reg); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int get_mode(struct stmp3xxx_regulator *sreg) +{ + u32 val = __raw_readl(sreg->rdata->control_reg) & (1 << 17); + + return val ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct stmp3xxx_platform_regulator_data vddd_data = { + .name = "vddd", + .set_voltage = set_voltage, + .get_voltage = get_voltage, + .enable = enable, + .disable = disable, + .is_enabled = is_enabled, + .set_mode = set_mode, + .get_mode = get_mode, + .control_reg = (u32)(REGS_POWER_BASE + HW_POWER_VDDDCTRL), + .min_voltage = 800000, + .max_voltage = 1575000, +}; + +static struct stmp3xxx_platform_regulator_data vdddbo_data = { + .name = "vddd_bo", + .parent_name = "vddd", + .set_voltage = set_bo_voltage, + .get_voltage = get_bo_voltage, + .enable = enable, + .disable = disable, + .is_enabled = is_enabled, + .set_mode = set_mode, + .get_mode = get_mode, + .min_voltage = 800000, + .max_voltage = 1575000, +}; + +static struct stmp3xxx_platform_regulator_data vdda_data = { + .name = "vdda", + .set_voltage = set_voltage, + .get_voltage = get_voltage, + .enable = enable, + .disable = disable, + .is_enabled = is_enabled, + .set_mode = set_mode, + .get_mode = get_mode, + .control_reg = (u32)(REGS_POWER_BASE + HW_POWER_VDDACTRL), + .min_voltage = 1500000, + .max_voltage = 2275000, +}; + +static struct stmp3xxx_platform_regulator_data vddio_data = { + .name = "vddio", + .set_voltage = set_voltage, + .get_voltage = get_voltage, + .enable = enable, + .disable = disable, + .is_enabled = is_enabled, + .set_mode = set_mode, + .get_mode = get_mode, + .control_reg = (u32)(REGS_POWER_BASE + HW_POWER_VDDIOCTRL), + .min_voltage = 2800000, + .max_voltage = 3575000, +}; + +static struct regulator_init_data vddd_init = { + .constraints = { + .name = "vddd", + .min_uV = 800000, + .max_uV = 1575000, + .valid_modes_mask = REGULATOR_MODE_FAST | + REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + .input_uV = 5000000, + .always_on = 1, + } +}; + +static struct regulator_init_data vdddbo_init = { + .constraints = { + .name = "vdddbo", + .min_uV = 800000, + .max_uV = 1575000, + .valid_modes_mask = REGULATOR_MODE_FAST | + REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + .input_uV = 5000000, + .always_on = 1, + } +}; + + +static struct regulator_init_data vdda_init = { + .constraints = { + .name = "vdda", + .min_uV = 1500000, + .max_uV = 2275000, + .valid_modes_mask = REGULATOR_MODE_FAST | + REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + .input_uV = 5000000, + .always_on = 1, + } +}; + + +static struct regulator_init_data vddio_init = { + .constraints = { + .name = "vddio", + .min_uV = 2800000, + .max_uV = 3575000, + .valid_modes_mask = REGULATOR_MODE_FAST | + REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + .input_uV = 5000000, + .always_on = 1, + } +}; + +/* now the current regulators */ +/* Restriction: .... no set_current call on root regulator */ +static int main_add_current(struct stmp3xxx_regulator *sreg, + int uA) +{ + + pr_debug("%s: enter reg %s, uA=%d\n", + __func__, sreg->regulator.name, uA); + if (uA > 0 && (sreg->cur_current + uA > sreg->rdata->max_current)) + return -EINVAL; + else + sreg->cur_current += uA; + return 0; +} + +static int cur_reg_set_current(struct stmp3xxx_regulator *sreg, int uA) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: enter reg %s, uA=%d\n", + __func__, sreg->regulator.name, uA); + + if (sreg->parent) { + spin_lock_irqsave(&sreg->parent->lock, flags); + ret = main_add_current(sreg->parent, uA - sreg->cur_current); + spin_unlock_irqrestore(&sreg->parent->lock, flags); + } + + + if ((!ret) || (!sreg->parent)) + goto out; + + if (sreg->mode == REGULATOR_MODE_FAST) + return ret; + + while (ret) { + wait_event(sreg->parent->wait_q , + (uA - sreg->cur_current < + sreg->parent->rdata->max_current - + sreg->parent->cur_current)); + spin_lock_irqsave(&sreg->parent->lock, flags); + ret = main_add_current(sreg->parent, uA - sreg->cur_current); + spin_unlock_irqrestore(&sreg->parent->lock, flags); + } +out: + if (sreg->parent && (uA - sreg->cur_current < 0)) + wake_up_all(&sreg->parent->wait_q); + sreg->cur_current = uA; + return 0; + +} + +static int cur_reg_get_current(struct stmp3xxx_regulator *sreg) +{ + return sreg->cur_current; +} + +static int enable_cur_reg(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 0; +} + +static int disable_cur_reg(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 0; +} + +static int cur_reg_is_enabled(struct stmp3xxx_regulator *sreg) +{ + /* XXX: TODO */ + return 1; +} + +static int cur_reg_set_mode(struct stmp3xxx_regulator *sreg, int mode) +{ + int ret = 0; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_FAST: + sreg->mode = mode; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int cur_reg_get_mode(struct stmp3xxx_regulator *sreg) +{ + return sreg->mode; +} + +static struct stmp3xxx_platform_regulator_data overall_cur_data = { + .name = "overall_current", + .set_current = cur_reg_set_current, + .get_current = cur_reg_get_current, + .enable = enable_cur_reg, + .disable = disable_cur_reg, + .is_enabled = cur_reg_is_enabled, + .set_mode = cur_reg_set_mode, + .get_mode = cur_reg_get_mode, + .max_current = 0x7fffffff, +}; + +static struct regulator_init_data overall_cur_init = { + .constraints = { + .name = "overall_current", + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .valid_ops_mask = REGULATOR_CHANGE_CURRENT | + REGULATOR_CHANGE_MODE, + .max_uA = 0x7fffffff, + .min_uA = 0x0, + .always_on = 1, + } +}; + +static struct stmp3xxx_platform_regulator_data sibling_cur_data = { + .parent_name = "overall_current", + .set_current = cur_reg_set_current, + .get_current = cur_reg_get_current, + .enable = enable_cur_reg, + .disable = disable_cur_reg, + .is_enabled = cur_reg_is_enabled, + .set_mode = cur_reg_set_mode, + .get_mode = cur_reg_get_mode, +}; + +static struct platform_device *devices[] = { + &stmp3xxx_keyboard, + &stmp3xxx_touchscreen, + &stmp3xxx_appuart, + &stmp3xxx_dbguart, + &stmp3xxx_watchdog, + &stmp3xxx_rtc, + &stmp3xxx_framebuffer, + &stmp3xxx_backlight, + &stmp3xxx_rotdec, + &stmp378x_i2c, + &stmp3xxx_persistent, + &stmp3xxx_dcp_bootstream, + &stmp3xxx_dcp, + &stmp3xxx_battery, + &stmp378x_pxp, +}; + +static int sibling_current_devices_num; + +int stmp3xxx_platform_add_regulator(const char *name, int count) +{ + int i; + pr_debug("%s: name %s, count %d\n", __func__, name, count); + for (i = sibling_current_devices_num; + i < sibling_current_devices_num + count; + i++) { + struct regulator_init_data *sibling_init = + kzalloc(sizeof(struct regulator_init_data), + GFP_KERNEL); + struct stmp3xxx_regulator *curr_reg = + kzalloc(sizeof(struct stmp3xxx_regulator), + GFP_KERNEL); + struct stmp3xxx_platform_regulator_data *d = + kzalloc(sizeof(struct stmp3xxx_platform_regulator_data), + GFP_KERNEL); + if (!d || !curr_reg || !sibling_init) + return -ENOMEM; + + sibling_init->constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST; + sibling_init->constraints.valid_ops_mask = + REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_MODE; + sibling_init->constraints.max_uA = 0x7fffffff; + sibling_init->constraints.min_uA = 0x0; + + memcpy(d, &sibling_cur_data, sizeof(sibling_cur_data)); + d->parent_name = kstrdup(sibling_cur_data.parent_name, + GFP_KERNEL); + snprintf(d->name, 80, "%s-%d", + name, i - sibling_current_devices_num + 1); + sibling_init->constraints.name = kstrdup(d->name, GFP_KERNEL); + sibling_init->constraints.always_on = 1; + curr_reg->rdata = d; + stmp3xxx_register_regulator(curr_reg, 101 + i, sibling_init); + } + sibling_current_devices_num += count; + return 0; +} + +static struct stmp3xxx_regulator vddd_reg = { + .rdata = &vddd_data, +}; + +static struct stmp3xxx_regulator vdda_reg = { + .rdata = &vdda_data, +}; + +static struct stmp3xxx_regulator vddio_reg = { + .rdata = &vddio_data, +}; + +static struct stmp3xxx_regulator vdddbo_reg = { + .rdata = &vdddbo_data, +}; + +static struct stmp3xxx_regulator overall_cur_reg = { + .rdata = &overall_cur_data, +}; + + +static int __init regulators_init(void) +{ + int i; + int retval = 0; + u32 vddio = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL) & ~0x1f; + pr_debug("regulators_init \n"); + __raw_writel(vddio | 0x14, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + vdddbo_reg.parent = &vddd_reg; + stmp3xxx_register_regulator(&vddd_reg, STMP3XXX_VDDD, &vddd_init); + stmp3xxx_register_regulator(&vdddbo_reg, STMP3XXX_VDDDBO, &vdddbo_init); + stmp3xxx_register_regulator(&vdda_reg, STMP3XXX_VDDA, &vdda_init); + stmp3xxx_register_regulator(&vddio_reg, STMP3XXX_VDDIO, &vddio_init); + stmp3xxx_register_regulator(&overall_cur_reg, + STMP3XXX_OVERALL_CUR, &overall_cur_init); + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + retval = stmp3xxx_platform_add_regulator(devices[i]->name, 1); + if (retval) + return retval; + } + stmp3xxx_platform_add_regulator("mmc_ssp", 2); + stmp3xxx_platform_add_regulator("charger", 1); + stmp3xxx_platform_add_regulator("power-test", 1); + stmp3xxx_platform_add_regulator("cpufreq", 1); + return 0; +} +postcore_initcall(regulators_init); diff --git a/arch/arm/mach-stmp378x/sleep.S b/arch/arm/mach-stmp378x/sleep.S new file mode 100644 index 000000000000..acbfa326bebd --- /dev/null +++ b/arch/arm/mach-stmp378x/sleep.S @@ -0,0 +1,540 @@ +/* + * Freescale STMP37XX/STMP378X low level sleep states support + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/hardware.h> +#include <asm/pgtable-hwdef.h> +#include <mach/regs-power.h> +#include <mach/regs-clkctrl.h> +#include "sleep.h" + +#define HW_DRAM_CTL06 IO_ADDRESS(0x800E0018) +#define HW_DRAM_CTL08 IO_ADDRESS(0x800E0020) +#define HW_EMI_STAT IO_ADDRESS(0x80020010) +#define HW_RTC_PERSISTENT0 \ + IO_ADDRESS(0x8005C060) + +#define PHYS_RAM_START 0x40000000 + +.global cpu_arm926_switch_mm + + .text + +.align 8 +ENTRY(stmp37xx_cpu_standby) + @ save registers on stack + stmfd sp!, {r0 - r9, lr} + + adr r9, __stmp_temp_stack + + @ clean cache + ldr r1, __stmp_flush_cache_addr + mov lr, pc + mov pc, r1 + + @ put DRAM into self refresh + mov r0, #(HW_DRAM_CTL08 & 0x000000FF) + orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00) + orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000) + orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000) + ldr r1, [r0] + orr r1, r1, #(1 << 8) + str r1, [r0] + @ wait for it to actually happen + mov r0, #(HW_EMI_STAT & 0x000000FF) + orr r0, r0, #(HW_EMI_STAT & 0x0000FF00) + orr r0, r0, #(HW_EMI_STAT & 0x00FF0000) + orr r0, r0, #(HW_EMI_STAT & 0xFF000000) +1: ldr r1, [r0] + teq r1, #(1 << 1) + beq 1b + nop + nop + nop + nop + nop + +#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING + @ RAM to clk from xtal + mov lr, pc + b stmp3xxx_ram_save_timings + mov lr, pc + b stmp3xxx_ram_24M_set_timings + + mov r0, #(HW_CLKCTRL_CLKSEQ & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0xFF000000) + ldr r4, [r0] + mov r1, #(1<<6) + str r1, [r0, #4] +1: ldr r1, [r0] + tst r1, #BM_CLKCTRL_EMI_BUSY_REF_XTAL + bne 1b + + @ save RAM divisors + mov r0, #(HW_CLKCTRL_FRAC & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_FRAC & 0xFF000000) + ldr r8, [r0] + and r8, r8, #(0x3F << 8) + lsr r8, r8, #8 + mov r0, #(HW_CLKCTRL_EMI & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_EMI & 0xFF000000) + ldr r7, [r0] + and r7, r7, #0x3F + + @ shut the PLL down + mov r0, #(HW_CLKCTRL_PLLCTRL0 & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0xFF000000) + mov r1, #(1<<16) + str r1, [r0, #0x08] @ clear + + @ set vddd to minimum + mov r0, #(HW_POWER_VDDDCTRL & 0x000000FF) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0x0000FF00) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0x00FF0000) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0xFF000000) + ldr r6, [r0] + bic r1, r6, #0xFF + bic r1, r1, #0x30 + orr r1, r1, #0xa + str r1, [r0] + /* now wait 1000 us = 24000 cycles */ + mov r0, #24 << 10 +3: sub r0, r0, #1 + cmp r0, #0 + bne 3b + nop +#endif + + @ do enter standby + mov r0, #(HW_CLKCTRL_CPU & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_CPU & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_CPU & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_CPU & 0xFF000000) + mov r1, #(1<<12) + str r1, [r0, #4] + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 + nop + + @ sleeping now... + + @ remove INTERRUPT_WAIT bit + str r1, [r0, #8] + nop + nop + nop + +#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING + @ restore vddd + mov r0, #(HW_POWER_VDDDCTRL & 0x000000FF) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0x0000FF00) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0x00FF0000) + orr r0, r0, #(HW_POWER_VDDDCTRL & 0xFF000000) + ldr r1, [r0] + str r6, [r0] + /* now wait 1000 us = 24000 cycles */ + mov r0, #24 << 10 +12: sub r0, r0, #1 + cmp r0, #0 + bne 12b + nop + + @ put the PLL back up + mov r0, #(HW_CLKCTRL_PLLCTRL0 & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_PLLCTRL0 & 0xFF000000) + mov r1, #(1<<16) + str r1, [r0, #0x04] @ set + /* now wait 10 us = 240 cycles */ + mov r0, #240 +11: sub r0, r0, #1 + cmp r0, #0 + bne 11b + nop + + @ set divisors and switch EMI back to PLL + mov lr, pc + b stmp3xxx_ram_restore_timings + mov lr, pc + b __stmp_emi_set_values + + mov r0, #(HW_CLKCTRL_CLKSEQ & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_CLKSEQ & 0xFF000000) + mov r1, #(1<<6) + str r1, [r0, #8] + + mov r0, #(HW_CLKCTRL_EMI & 0x000000FF) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x0000FF00) + orr r0, r0, #(HW_CLKCTRL_EMI & 0x00FF0000) + orr r0, r0, #(HW_CLKCTRL_EMI & 0xFF000000) + ldr r1, [r0] + bic r1, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE + str r1, [r0] +#endif + + @ restore normal DRAM mode + mov r0, #(HW_DRAM_CTL08 & 0x000000FF) + orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00) + orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000) + orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000) + ldr r1, [r0] + bic r1, r1, #(1 << 8) + str r1, [r0] + @ wait for it to actually happen + mov r0, #(HW_EMI_STAT & 0x000000FF) + orr r0, r0, #(HW_EMI_STAT & 0x0000FF00) + orr r0, r0, #(HW_EMI_STAT & 0x00FF0000) + orr r0, r0, #(HW_EMI_STAT & 0xFF000000) +102: ldr r1, [r0] + tst r1, #(1 << 1) + bne 102b + + nop + nop + nop + + @ restore regs and return + ldmfd sp!, {r0 - r9, pc} + + .space 0x100 +__stmp_temp_stack: + .word 0 + +#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING +#include "emi.inc" +#endif + +__stmp_flush_cache_addr: + .word arm926_flush_kern_cache_all + +ENTRY(stmp_standby_alloc_sz) + .word . - stmp37xx_cpu_standby + +ENTRY(stmp37xx_cpu_suspend) + @ save registers on stack + stmfd sp!, {r1 - r12, lr} + + @ save context + mov r0, #0xd3 @ SVC, Interrupts disabled + msr cpsr, r0 + mov r1, #0xC0000000 + ldr r1, [r1] + mrc p15, 0, r0, c1, c0, 0 + str r0, [r1, #MMUCTL_OFFS] + mrc p15, 0, r0, c15, c1, 0 + str r0, [r1, #MMUCPACCESS_OFS] + mrc p15, 0, r0, c2, c0, 0 + str r0, [r1, #MMUTTB_OFFS] + mrc p15, 0, r0, c3, c0, 0 + str r0, [r1, #MMUDOMAIN_OFFS] + mrc p15, 0, r0, c13, c0, 0 + str r0, [r1, #MMUPID_OFFS] + + str sp, [r1, #SVC_SP_OFFS] + mrs r0, spsr + str r0, [r1, #SVC_SPSR_OFFS] + + add r2, r1, #FIQ_SPSR_OFFS + mov r0, #0xd1 @ FIQ, Interrupts disabled + msr cpsr, r0 + mrs r3, spsr + stmia r2!, {r3, r8-r12, sp, lr} + + add r2, r1, #ABT_SPSR_OFFS + mov r0, #0xd7 @ ABT, Interrupts disabled + msr cpsr, r0 + mrs r3, spsr + stmia r2!, {r3, sp, lr} + + add r2, r1, #IRQ_SPSR_OFFS + mov r0, #0xd2 @ IRQ, Interrupts disabled + msr cpsr, r0 + mrs r3, spsr + stmia r2!, {r3, sp, lr} + + add r2, r1, #UND_SPSR_OFFS + mov r0, #0xdb @ UND, Interrupts disabled + msr cpsr, r0 + mrs r3, spsr + stmia r2!, {r3, sp, lr} + + add r2, r1, #SYS_SP_OFFS + mov r0, #0xdf @ SYS, Interrupts disabled + msr cpsr, r0 + stmia r2!, {sp, lr} + + add r2, r1, #SVC_R8_OFFS + mov r0, #0xd3 @ Back to SVC, Interrupts disabled + msr cpsr, r0 + + @ save entry point + sub r1, r1, #(0xC0000000 - PHYS_RAM_START) + mov r0, #0xC0000000 + str r1, [r0] + ldr r1, __stmp_resume_point + sub r1, r1, #(0xC0000000 - PHYS_RAM_START) + str r1, [r0, #4] + mov r0, #0 + + @ clean cache + ldr r1, __stmp_flush_cache_addr2 + mov lr, pc + mov pc, r1 + + @ enable internal xtal + mov r2, #(HW_POWER_MINPWR & 0x000000FF) + orr r2, r2, #(HW_POWER_MINPWR & 0x0000FF00) + orr r2, r2, #(HW_POWER_MINPWR & 0x00FF0000) + orr r2, r2, #(HW_POWER_MINPWR & 0xFF000000) + ldr r1, [r2] + orr r1, r1, #(1<<9) + str r1, [r2] + orr r1, r1, #(1<<8) + str r1, [r2] + + @ enable RTC/RAM clocks + mov r0, #(HW_RTC_PERSISTENT0 & 0x000000FF) + orr r0, r0, #(HW_RTC_PERSISTENT0 & 0x0000FF00) + orr r0, r0, #(HW_RTC_PERSISTENT0 & 0x00FF0000) + orr r0, r0, #(HW_RTC_PERSISTENT0 & 0xFF000000) + mov r1, #((1<<4)|(1<<5)|1) + str r1, [r0, #4] + + @ put DRAM into self refresh + mov r0, #(HW_DRAM_CTL08 & 0x000000FF) + orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00) + orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000) + orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000) + ldr r1, [r0] + orr r1, r1, #(1 << 8) + str r1, [r0] + @ wait for it to actually happen + mov r0, #(HW_EMI_STAT & 0x000000FF) + orr r0, r0, #(HW_EMI_STAT & 0x0000FF00) + orr r0, r0, #(HW_EMI_STAT & 0x00FF0000) + orr r0, r0, #(HW_EMI_STAT & 0xFF000000) +1: ldr r1, [r0] + teq r1, #(1 << 1) + beq 1b + nop + nop + nop + nop + nop + nop + + @ power off RAM + mov r0, #(HW_DRAM_CTL06 & 0x000000FF) + orr r0, r0, #(HW_DRAM_CTL06 & 0x0000FF00) + orr r0, r0, #(HW_DRAM_CTL06 & 0x00FF0000) + orr r0, r0, #(HW_DRAM_CTL06 & 0xFF000000) + ldr r1, [r0] + orr r1, r1, #(1<<24) + str r1, [r0] + nop + nop + nop + nop + + @ do enter sleep + mov r0, #(HW_POWER_RESET & 0x000000FF) + orr r0, r0, #(HW_POWER_RESET & 0x0000FF00) + orr r0, r0, #(HW_POWER_RESET & 0x00FF0000) + orr r0, r0, #(HW_POWER_RESET & 0xFF000000) + mov r1, #0xFF000000 + orr r1, r1, #0x00FF0000 + str r1, [r0, #8] + mov r1, #0x3E000000 + orr r1, r1, #0x00770000 + str r1, [r0, #4] + mov r1, #2 + str r1, [r0, #8] + mov r1, #1 + str r1, [r0, #4] + nop + nop + nop + nop + nop + nop + + @ sleeping now... + +__restore_context: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + mcr p15, 0, r0, c8, c7, 0 @ Invalidate TLBs + mcr p15, 0, r0, c7, c7, 0 @ Invalidate I & D cache + nop + nop + + mov r0, #0xd3 + msr cpsr, r0 + + bl __create_temp_page_tables + mov r3, r4 + + mov r1, #PHYS_RAM_START + ldr r1, [r1] + ldr r2, [r1, #MMUDOMAIN_OFFS] + ldr r4, [r1, #MMUCPACCESS_OFS] + ldr r5, [r1, #MMUPID_OFFS] + ldr r6, =__resume_after_mmu + ldr r7, [r1, #MMUCTL_OFFS] + ldr r8, [r1, #MMUTTB_OFFS] + add r1, r1, #(0xC0000000 - PHYS_RAM_START) + mov r0, #0 +@ mcr p15, 0, r4, c15, c1, 0 @ cpaccess + mcr p15, 0, r5, c13, c0, 0 @ pid + mcr p15, 0, r2, c3, c0, 0 @ domain + mcr p15, 0, r3, c2, c0, 0 @ ttb + b 1f + .align 5 +1: mov r0, r0 + mcr p15, 0, r7, c1, c0, 0 @ mmuctl + nop + mrc p15, 0, r0, c3, c0, 0 @ read id + mov r0, r0 + mov r0, r0 + sub pc, r6, r5, lsr #32 + nop + nop + nop +__resume_after_mmu: + mov r0, #0 + mcr p15, 0, r0, c8, c7, 0 @ Invalidate TLBs + mcr p15, 0, r0, c7, c7, 0 @ Invalidate I & D cache + + mov r0, r8 + bl cpu_arm926_switch_mm + + mov r0, #0xd1 @FIQ, Interrupts disabled + ldr r2, [r1, #FIQ_SPSR_OFFS] + add r3, r1, #FIQ_R8_OFFS + msr cpsr, r0 + msr spsr, r2 + ldmia r3!, {r8-r12, sp, lr} + + mov r0, #0xd7 @ABT, Interrupts disabled + ldr r2, [r1, #ABT_SPSR_OFFS] + add r3, r1, #ABT_SP_OFFS + msr cpsr, r0 + msr spsr, r2 + ldmia r3!, {sp, lr} + + mov r0, #0xd2 @IRQ, Interrupts disabled + ldr r2, [r1, #IRQ_SPSR_OFFS] + add r3, r1, #IRQ_SP_OFFS + msr cpsr, r0 + msr spsr, r2 + ldmia r3!, {sp, lr} + + mov r0, #0xdb @UND, Interrupts disabled + ldr r2, [r1, #UND_SPSR_OFFS] + add r3, r1, #UND_SP_OFFS + msr cpsr, r0 + msr spsr, r2 + ldmia r3!, {sp, lr} + + mov r0, #0xdf @SYS, Interrupts disabled + add r3, r1, #SYS_SP_OFFS + msr cpsr, r0 + ldmia r3!, {sp, lr} + + mov r0, #0xd3 @SVC, interrupts disabled + ldr r2, [r1, #SVC_SPSR_OFFS] + ldr r3, [r1, #SVC_SP_OFFS] + msr cpsr, r0 + msr spsr, r2 + mov sp, r3 + +#if 0 + @ select CPU bypass, will be cleared afterwards + ldr r0, =HW_CLKCTRL_CLKSEQ + ldr r2, =HW_CLKCTRL_HBUS + ldr r4, =HW_CLKCTRL_CPU + mov r1, #(1<<7) + ldr r3, [r2] + bic r3, r3, #BM_CLKCTRL_HBUS_DIV + orr r3, r3, #1 + ldr r5, [r4] + bic r5, r5, #BM_CLKCTRL_CPU_DIV_CPU + orr r5, r5, #1 + str r1, [r0, #4] + str r3, [r2] + str r5, [r4] +#endif + @ restore regs and return + ldmfd sp!, {r1 - r12, lr} + mov pc, lr + +__stmp_flush_cache_addr2: + .word arm926_flush_kern_cache_all +__stmp_resume_point: + .word __restore_context +ENTRY(stmp_s2ram_alloc_sz) + .word . - stmp37xx_cpu_suspend + +__create_temp_page_tables: + ldr r4, =(__temp_ttb - 0xC0000000 + PHYS_RAM_START) + + /* + * Clear the 16K level 1 swapper page table + */ + mov r0, r4 + mov r3, #0 + add r6, r0, #0x4000 +1: str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 + teq r0, r6 + bne 1b + + /* + * Create identity mapping for the area close to where we are to + * cater for the MMU enable. + */ + mov r6, pc, lsr #20 @ kind of where we are + ldr r7, =(PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ) + + orr r3, r7, r6, lsl #20 @ flags + kernel base + str r3, [r4, r6, lsl #2] @ identity mapping + + mov r6, r6, lsl #20 + add r6, r6, #(0xC0000000-PHYS_RAM_START) + str r3, [r4, r6, lsr #18] + + mov pc, lr + .ltorg + + .section ".sdata", "a" + .align 14 +__temp_ttb: + .space 0x8000 diff --git a/arch/arm/mach-stmp378x/sleep.h b/arch/arm/mach-stmp378x/sleep.h new file mode 100644 index 000000000000..79264d7a2580 --- /dev/null +++ b/arch/arm/mach-stmp378x/sleep.h @@ -0,0 +1,120 @@ +/* + * Deep Sleep related defines + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __PM_H__ +#define __PM_H__ + +#include <mach/regs-clkctrl.h> + +#define MMUTTB1_MASK 0x00003FE0 +#define MMUTTBC_MASK 0xFFFFFFFC + +#define LINK_OFFS 0x08 +#define MMUCTL_OFFS 0x0C +#define MMUAUXCTL_OFFS 0x10 +#define MMUCPACCESS_OFS 0x14 +#define MMUTTB_OFFS 0x18 +#define MMUPID_OFFS 0x1C +#define MMUDOMAIN_OFFS 0x20 +#define SVC_R8_OFFS 0x2C +#define SVC_SP_OFFS 0x40 +#define SVC_SPSR_OFFS 0x44 +#define FIQ_SPSR_OFFS 0x48 +#define FIQ_R8_OFFS 0x4C +#define FIQ_SP_OFFS 0x60 +#define ABT_R8_OFFS 0x68 +#define ABT_SPSR_OFFS 0x7C +#define ABT_SP_OFFS 0x80 +#define IRQ_R8_OFFS 0x88 +#define IRQ_SPSR_OFFS 0x9C +#define IRQ_SP_OFFS 0xA0 +#define UND_SPSR_OFFS 0xA8 +#define UND_SP_OFFS 0xAC +#define SYS_SPSR_OFFS 0xB4 +#define SYS_SP_OFFS 0xB8 + +#ifndef __ASSEMBLER__ +#define SLEEP_DATA_FINGERPRINT 0xdeadbeef +struct sleep_data { + u32 fingerprint; + u32 wake_addr; + u32 link_addr; + u32 mmuctl; + u32 mmuauxctl; + u32 mmucpaccess; + u32 mmuttb; + u32 mmupid; + u32 mmudomain; + u32 svc_r6; + u32 svc_r7; + u32 svc_r8; + u32 svc_r9; + u32 svc_r10; + u32 svc_r11; + u32 svc_r12; + u32 svc_sp; + u32 svc_spsr; + u32 fiq_spsr; + u32 fiq_r8; + u32 fiq_r9; + u32 fiq_r10; + u32 fiq_r11; + u32 fiq_r12; + u32 fiq_sp; + u32 fiq_lr; + u32 abt_r8; + u32 abt_r9; + u32 abt_r10; + u32 abt_r11; + u32 abt_r12; + u32 abt_spsr; + u32 abt_sp; + u32 abt_lr; + u32 irq_r8; + u32 irq_r9; + u32 irq_r10; + u32 irq_r11; + u32 irq_r12; + u32 irq_spsr; + u32 irq_sp; + u32 irq_lr; + u32 und_spsr; + u32 und_sp; + u32 und_lr; + u32 sys_spsr; + u32 sys_sp; + u32 sys_lr; + u32 pinmux[0x100]; + u32 icoll_ctrl; + union { + u32 prio[0x10]; + u32 intr[0x80]; + } icoll; + u32 clks[16]; + u32 old_c00; + u32 old_c04; +}; + +extern int stmp_s2ram_alloc_sz; +void stmp37xx_cpu_suspend(void); +extern int stmp_standby_alloc_sz; +void stmp37xx_cpu_standby(void); +void stmp3xxx_suspend_timer(void); +void stmp3xxx_resume_timer(void); + +#endif /* __ASSEMBLER__ */ +#endif /* __PM_H__ */ diff --git a/arch/arm/mach-stmp378x/stmp378x.c b/arch/arm/mach-stmp378x/stmp378x.c index ddd49a760fd4..eb418c6eaadc 100644 --- a/arch/arm/mach-stmp378x/stmp378x.c +++ b/arch/arm/mach-stmp378x/stmp378x.c @@ -42,6 +42,7 @@ #include <mach/regs-apbx.h> #include <mach/regs-pxp.h> #include <mach/regs-i2c.h> +#include <mach/regs-ocotp.h> #include "stmp378x.h" /* @@ -64,14 +65,14 @@ static void stmp378x_mask_irq(unsigned int irq) { /* IRQ disable */ stmp3xxx_clearl(BM_ICOLL_INTERRUPTn_ENABLE, - REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn + irq * 0x10); + REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn(irq)); } static void stmp378x_unmask_irq(unsigned int irq) { /* IRQ enable */ stmp3xxx_setl(BM_ICOLL_INTERRUPTn_ENABLE, - REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn + irq * 0x10); + REGS_ICOLL_BASE + HW_ICOLL_INTERRUPTn(irq)); } static struct irq_chip stmp378x_chip = { @@ -293,7 +294,24 @@ struct platform_device stmp378x_i2c = { .num_resources = ARRAY_SIZE(i2c_resources), }; +struct platform_device stmp378x_audio = { + .name = "stmp378x-audio", + .id = -1, +}; + void __init stmp378x_map_io(void) { iotable_init(stmp378x_io_desc, ARRAY_SIZE(stmp378x_io_desc)); } + +int get_evk_board_version() +{ + int boardid; + boardid = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTCAP); + boardid &= 0x30000000; + boardid = boardid >> 28; + + return boardid; +} + +EXPORT_SYMBOL_GPL(get_evk_board_version); diff --git a/arch/arm/mach-stmp378x/stmp378x_devb.c b/arch/arm/mach-stmp378x/stmp378x_devb.c index 90d8fe6f10fe..201c679ae427 100644 --- a/arch/arm/mach-stmp378x/stmp378x_devb.c +++ b/arch/arm/mach-stmp378x/stmp378x_devb.c @@ -23,6 +23,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/spi/spi.h> +#include <linux/input.h> #include <asm/setup.h> #include <asm/mach-types.h> @@ -32,8 +33,10 @@ #include <mach/pinmux.h> #include <mach/platform.h> #include <mach/stmp3xxx.h> -#include <mach/mmc.h> #include <mach/gpmi.h> +#include <mach/mmc.h> +#include <mach/lcdif.h> +#include <mach/ddi_bc.h> #include "stmp378x.h" @@ -53,8 +56,21 @@ static struct platform_device *devices[] = { &stmp3xxx_battery, &stmp378x_pxp, &stmp378x_i2c, + &stmp3xxx_spdif, + &stmp378x_audio, }; +int usb_host_wakeup_irq(struct device *wkup_dev) +{ + return 0; +} +EXPORT_SYMBOL(usb_host_wakeup_irq); + +void usb_host_set_wakeup(struct device *wkup_dev, bool para) +{ +} +EXPORT_SYMBOL(usb_host_set_wakeup); + static struct pin_desc i2c_pins_desc[] = { { PINID_I2C_SCL, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 }, { PINID_I2C_SDA, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 }, @@ -77,14 +93,14 @@ static struct pin_group dbguart_pins[] = { }, }; -static int dbguart_pins_control(int id, int request) +static int dbguart_pinmux(int request, int id) { int r = 0; if (request) - r = stmp3xxx_request_pin_group(&dbguart_pins[id], "debug uart"); + r = stmp3xxx_request_pin_group(&dbguart_pins[id], "dbguart"); else - stmp3xxx_release_pin_group(&dbguart_pins[id], "debug uart"); + stmp3xxx_release_pin_group(&dbguart_pins[id], "dbguart"); return r; } @@ -104,104 +120,6 @@ static struct pin_desc appuart_pins_1[] = { #endif }; -static struct pin_desc mmc_pins_desc[] = { - { PINID_SSP1_DATA0, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, - { PINID_SSP1_DATA1, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, - { PINID_SSP1_DATA2, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, - { PINID_SSP1_DATA3, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, - { PINID_SSP1_CMD, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 }, - { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 }, - { PINID_SSP1_DETECT, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 }, -}; - -static struct pin_group mmc_pins = { - .pins = mmc_pins_desc, - .nr_pins = ARRAY_SIZE(mmc_pins_desc), -}; - -static int stmp3xxxmmc_get_wp(void) -{ - return gpio_get_value(PINID_PWM4); -} - -static int stmp3xxxmmc_hw_init_ssp1(void) -{ - int ret; - - ret = stmp3xxx_request_pin_group(&mmc_pins, "mmc"); - if (ret) - goto out; - - /* Configure write protect GPIO pin */ - ret = gpio_request(PINID_PWM4, "mmc wp"); - if (ret) - goto out_wp; - - gpio_direction_input(PINID_PWM4); - - /* Configure POWER pin as gpio to drive power to MMC slot */ - ret = gpio_request(PINID_PWM3, "mmc power"); - if (ret) - goto out_power; - - gpio_direction_output(PINID_PWM3, 0); - mdelay(100); - - return 0; - -out_power: - gpio_free(PINID_PWM4); -out_wp: - stmp3xxx_release_pin_group(&mmc_pins, "mmc"); -out: - return ret; -} - -static void stmp3xxxmmc_hw_release_ssp1(void) -{ - gpio_free(PINID_PWM3); - gpio_free(PINID_PWM4); - stmp3xxx_release_pin_group(&mmc_pins, "mmc"); -} - -static void stmp3xxxmmc_cmd_pullup_ssp1(int enable) -{ - stmp3xxx_pin_pullup(PINID_SSP1_CMD, enable, "mmc"); -} - -static unsigned long -stmp3xxxmmc_setclock_ssp1(void __iomem *base, unsigned long hz) -{ - struct clk *ssp, *parent; - char *p; - long r; - - ssp = clk_get(NULL, "ssp"); - - /* using SSP1, no timeout, clock rate 1 */ - writel(BF(2, SSP_TIMING_CLOCK_DIVIDE) | - BF(0xFFFF, SSP_TIMING_TIMEOUT), - base + HW_SSP_TIMING); - - p = (hz > 1000000) ? "io" : "osc_24M"; - parent = clk_get(NULL, p); - clk_set_parent(ssp, parent); - r = clk_set_rate(ssp, 2 * hz / 1000); - clk_put(parent); - clk_put(ssp); - - return hz; -} - -static struct stmp3xxxmmc_platform_data mmc_data = { - .hw_init = stmp3xxxmmc_hw_init_ssp1, - .hw_release = stmp3xxxmmc_hw_release_ssp1, - .get_wp = stmp3xxxmmc_get_wp, - .cmd_pullup = stmp3xxxmmc_cmd_pullup_ssp1, - .setclock = stmp3xxxmmc_setclock_ssp1, -}; - - static struct pin_group appuart_pins[] = { [0] = { .pins = appuart_pins_0, @@ -213,6 +131,15 @@ static struct pin_group appuart_pins[] = { }, }; +static int appuart_pinmux(int req, int id) +{ + if (req) + return stmp3xxx_request_pin_group(&appuart_pins[id], "appuart"); + else + stmp3xxx_release_pin_group(&appuart_pins[id], "appuart"); + return 0; +} + static struct pin_desc ssp1_pins_desc[] = { { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0, }, { PINID_SSP1_CMD, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, }, @@ -265,24 +192,150 @@ static struct pin_group gpmi_pins = { .nr_pins = ARRAY_SIZE(gpmi_pins_desc), }; -static struct mtd_partition gpmi_partitions[] = { +static int gpmi_pinmux(int req) +{ + if (req) + return stmp3xxx_request_pin_group(&gpmi_pins, "gpmi"); + else + stmp3xxx_release_pin_group(&gpmi_pins, "gpmi"); + return 0; +} + +const char *gpmi_part_probes[] = { "cmdlinepart", NULL }; + +#define UID_SIZE SZ_1M +#define UID_OFFSET (20*SZ_1M) + +struct mtd_partition gpmi_partitions_chip0[] = { + [0] = { + .offset = 0, + .size = UID_OFFSET, + .name = "Boot#0", + .mask_flags = 0, + }, + /* This partition is managed by UBI */ + [1] = { + .offset = UID_OFFSET + UID_SIZE, + .size = MTDPART_SIZ_FULL, + .name = "UBI#0", + .mask_flags = 0, + }, +}; + +struct mtd_partition gpmi_partitions_chip1[] = { [0] = { - .name = "boot", - .size = 10 * SZ_1M, - .offset = 0, + .offset = 0, + .size = UID_OFFSET, + .name = "Boot#1", + .mask_flags = 0, }, + /* This partition is managed by UBI */ [1] = { - .name = "data", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, + .offset = UID_OFFSET, + .size = MTDPART_SIZ_FULL, + .name = "UBI#1", + .mask_flags = 0, }, }; -static struct gpmi_platform_data gpmi_data = { - .pins = &gpmi_pins, - .nr_parts = ARRAY_SIZE(gpmi_partitions), - .parts = gpmi_partitions, - .part_types = { "cmdline", NULL }, +static char *gpmi_concat_parts[] = { + [0] = "UBI#0", + [1] = "UBI#1", + [2] = NULL, +}; + +static struct gpmi_platform_data gpmi_partitions = { + .uid_offset = UID_OFFSET, + .uid_size = UID_SIZE, + .io_uA = 70000, + .items = 2, + .concat_name = "UBI", + .concat_parts = gpmi_concat_parts, + .pinmux = gpmi_pinmux, + .parts = { + [0] = { + .part_probe_types = gpmi_part_probes, + .nr_partitions = ARRAY_SIZE(gpmi_partitions_chip0), + .partitions = gpmi_partitions_chip0, + }, + [1] = { + .part_probe_types = gpmi_part_probes, + .nr_partitions = ARRAY_SIZE(gpmi_partitions_chip1), + .partitions = gpmi_partitions_chip1, + }, + }, +}; + +static struct pin_desc lcd_hx8238a_desc[] = { + { PINID_LCD_D00, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D01, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D02, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D03, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D04, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D05, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D06, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D07, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D08, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D09, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D10, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D11, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D12, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D13, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D14, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D15, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D16, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_D17, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_RESET, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_VSYNC, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_HSYNC, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_ENABLE, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_LCD_DOTCK, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D13, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D12, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D11, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D10, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D09, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, + { PINID_GPMI_D08, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 }, +}; + +struct pin_group lcd_pins = { + .pins = lcd_hx8238a_desc, + .nr_pins = ARRAY_SIZE(lcd_hx8238a_desc), +}; + +unsigned lcd_spi_pins[] = { + [SPI_MOSI] = PINID_LCD_WR, + [SPI_SCLK] = PINID_LCD_RS, + [SPI_CS] = PINID_LCD_CS, +}; + +static struct pin_desc spdif_pins_desc[] = { + { PINID_ROTARYA, PIN_FUN3, PIN_4MA, PIN_1_8V, 0, }, +}; + +struct pin_group spdif_pins = { + .pins = spdif_pins_desc, + .nr_pins = ARRAY_SIZE(spdif_pins_desc), +}; + +int spdif_pinmux(int req) +{ + if (req) + return stmp3xxx_request_pin_group(&spdif_pins, "spdif"); + else + stmp3xxx_release_pin_group(&spdif_pins, "spdif"); + return 0; +} +EXPORT_SYMBOL_GPL(spdif_pinmux); + +static struct stmp3xxxmmc_platform_data mmc_data = { + .hw_init = stmp3xxxmmc_hw_init_ssp1, + .hw_release = stmp3xxxmmc_hw_release_ssp1, + .get_wp = stmp3xxxmmc_get_wp, + .cmd_pullup = stmp3xxxmmc_cmd_pullup_ssp1, + .setclock = stmp3xxxmmc_setclock_ssp1, + .read_uA = 50000, + .write_uA = 70000, }; static struct spi_board_info spi_board_info[] __initdata = { @@ -297,6 +350,48 @@ static struct spi_board_info spi_board_info[] __initdata = { #endif }; +/* battery info data */ +static ddi_bc_Cfg_t battery_data = { + .u32StateMachinePeriod = 100, /* ms */ + .u16CurrentRampSlope = 75, /* mA/s */ + .u16ConditioningThresholdVoltage = 2900, /* mV */ + .u16ConditioningMaxVoltage = 3000, /* mV */ + .u16ConditioningCurrent = 60, /* mA */ + .u32ConditioningTimeout = 4*60*60*1000, /* ms (4 hours) */ + .u16ChargingVoltage = 4200, /* mV */ + /* FIXME: the current comparator could have h/w bugs in current + * detection through POWER_STS.CHRGSTS bit */ + .u16ChargingCurrent = 600, /* mA 600 */ + .u16ChargingThresholdCurrent = 60, /* mA 60 */ + .u32ChargingTimeout = 4*60*60*1000,/* ms (4 hours) */ + .u32TopOffPeriod = 30*60*1000, /* ms (30 minutes) */ + .useInternalBias = 0, /* ext bias current */ + .monitorDieTemp = 1, /* Monitor the die */ + .u8DieTempHigh = 115, /* deg centigrade */ + .u8DieTempLow = 96, /* deg centigrade */ + .u16DieTempSafeCurrent = 400, /* mA */ + .monitorBatteryTemp = 0, /* Monitor the battery*/ + .u8BatteryTempChannel = 1, /* LRADC 1 */ + .u16BatteryTempHigh = 642, /* Unknown units */ + .u16BatteryTempLow = 497, /* Unknown units */ + .u16BatteryTempSafeCurrent = 0, /* mA */ +}; + +static struct stmpkbd_keypair keyboard_data[] = { + { 100, KEY_F4 }, + { 306, KEY_F5 }, + { 626, KEY_F6 }, + { 932, KEY_F7 }, + { 1260, KEY_F8 }, + { 1584, KEY_F9 }, + { 1907, KEY_F10 }, + { 2207, KEY_F11 }, + { 2525, KEY_F12 }, + { 2831, KEY_F13}, + { 3134, KEY_F14 }, + { -1, 0 }, +}; + static void __init stmp378x_devb_init(void) { stmp3xxx_pinmux_init(NR_REAL_IRQS); @@ -304,13 +399,15 @@ static void __init stmp378x_devb_init(void) /* init stmp3xxx platform */ stmp3xxx_init(); - stmp3xxx_dbguart.dev.platform_data = dbguart_pins_control; - stmp3xxx_appuart.dev.platform_data = appuart_pins; + stmp3xxx_dbguart.dev.platform_data = dbguart_pinmux; + stmp3xxx_appuart.dev.platform_data = appuart_pinmux; + stmp3xxx_gpmi.dev.platform_data = &gpmi_partitions; stmp3xxx_mmc.dev.platform_data = &mmc_data; - stmp3xxx_gpmi.dev.platform_data = &gpmi_data; stmp3xxx_spi1.dev.platform_data = &ssp1_pins; stmp3xxx_spi2.dev.platform_data = &ssp2_pins; stmp378x_i2c.dev.platform_data = &i2c_pins; + stmp3xxx_battery.dev.platform_data = &battery_data; + stmp3xxx_keyboard.dev.platform_data = &keyboard_data; /* register spi devices */ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); diff --git a/arch/arm/mach-stmp378x/stmp378x_devb_rotdec.c b/arch/arm/mach-stmp378x/stmp378x_devb_rotdec.c new file mode 100644 index 000000000000..05270fe39419 --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_devb_rotdec.c @@ -0,0 +1,47 @@ +/* + * Freescale STMP378X Rotary Encoder module pin multiplexing + * + * Author: Drew Benedetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/module.h> +#include <mach/pins.h> +#include <mach/pinmux.h> + +#define ROTARY_FUN PIN_FUN1 + +#define TITLE "stmp3xxx-rotdec" + +int rotdec_pinmux_request(void) +{ + int rc = 0; + + rc |= stmp3xxx_request_pin(PINID_ROTARYA, ROTARY_FUN, TITLE); + rc |= stmp3xxx_request_pin(PINID_ROTARYB, ROTARY_FUN, TITLE); + + return rc; +} +EXPORT_SYMBOL_GPL(rotdec_pinmux_request); + +void rotdec_pinmux_free(void) +{ + stmp3xxx_release_pin(PINID_ROTARYA, TITLE); + stmp3xxx_release_pin(PINID_ROTARYB, TITLE); +} +EXPORT_SYMBOL_GPL(rotdec_pinmux_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>"); diff --git a/arch/arm/mach-stmp378x/stmp378x_i2c.c b/arch/arm/mach-stmp378x/stmp378x_i2c.c new file mode 100644 index 000000000000..f5e96fca5de8 --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_i2c.c @@ -0,0 +1,281 @@ +/* + * Freescale STMP378X I2C low-level/dma functions + * + * Author: Dmitrij Frasenyak <sed@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> + +#include <linux/dma-mapping.h> +#include <mach/hardware.h> +#include <mach/regs-i2c.h> +#include <mach/regs-apbx.h> +#include <mach/dma.h> +#include <mach/i2c.h> +#include <mach/platform.h> +#include <mach/pinmux.h> + +#define STMP378X_APBX_I2C 3 + +static unsigned int dma_channel = + STMP3XXX_DMA(STMP378X_APBX_I2C, STMP3XXX_BUS_APBX); + + +static struct stmp3xxx_dma_descriptor i2c_dma_read[2]; +static struct stmp3xxx_dma_descriptor i2c_dma_write; +static dma_addr_t i2c_buf_phys; +static u8 *i2c_buf_virt; + + +/* + * Select device to read from + */ + +u32 cmd_i2c_select[4] = { + 0, /* Chain to i2c_read */ + + (BF(1, APBX_CHn_CMD_XFER_COUNT) | + /* BM_APBX_CHn_CMD_SEMAPHORE | */ + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_CHAIN | + BM_APBX_CHn_CMD_IRQONCMPLT | /* For debug*/ + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_RETAIN_CLOCK | + BM_I2C_CTRL0_PRE_SEND_START | + BM_I2C_CTRL0_MASTER_MODE | + BM_I2C_CTRL0_DIRECTION | + BF(1, I2C_CTRL0_XFER_COUNT) + +}; + +u32 cmd_i2c_write[4] = { + 0, + + (BM_APBX_CHn_CMD_SEMAPHORE | + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_IRQONCMPLT | + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_PRE_SEND_START | + BM_I2C_CTRL0_MASTER_MODE | +/* BM_I2C_CTRL0_POST_SEND_STOP | */ + BM_I2C_CTRL0_DIRECTION + +}; + + +u32 cmd_i2c_read[4] = { + 0, + + (BM_APBX_CHn_CMD_SEMAPHORE | + BF(1, APBX_CHn_CMD_CMDWORDS) | + BM_APBX_CHn_CMD_WAIT4ENDCMD | + BM_APBX_CHn_CMD_IRQONCMPLT | + BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND)), + + 0, /* dma handler */ + + BM_I2C_CTRL0_SEND_NAK_ON_LAST | +/* BM_I2C_CTRL0_POST_SEND_STOP | */ + BM_I2C_CTRL0_MASTER_MODE | + 0 +}; + + +int hw_i2c_init_dma(struct device *dev) +{ + int ret; + + ret = stmp3xxx_dma_request(dma_channel, dev, "i2c"); + if (ret) { + dev_err(dev, "stmp3xxx_dma_request failed: error %d\n", ret); + return ret; + } + + i2c_buf_virt = + dma_alloc_coherent( + dev, + PAGE_SIZE, + &i2c_buf_phys, + GFP_KERNEL); + + if (i2c_buf_virt == NULL) + return -ENOMEM; + + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_read[0]); + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_read[1]); + + stmp3xxx_dma_allocate_command( + dma_channel, + &i2c_dma_write); + + stmp3xxx_dma_reset_channel(dma_channel); + stmp3xxx_dma_clear_interrupt(dma_channel); + stmp3xxx_dma_enable_interrupt(dma_channel); + return 0; +}; + +void hw_i2c_free_dma(struct device *dev) +{ + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_write); + + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_read[1]); + + stmp3xxx_dma_free_command( + dma_channel, + &i2c_dma_read[0]); + + dma_free_coherent( + dev, + PAGE_SIZE, + i2c_buf_virt, + i2c_buf_phys); + + stmp3xxx_dma_release(dma_channel); +} + +void hw_i2c_clear_dma_interrupt(void) +{ + stmp3xxx_dma_clear_interrupt(dma_channel); +} +EXPORT_SYMBOL(hw_i2c_clear_dma_interrupt); + +void hw_i2c_setup_write(u8 addr, void *buff, int len, int flags) +{ + + memcpy(i2c_dma_write.command, &cmd_i2c_write, sizeof(cmd_i2c_write)); + + i2c_dma_write.command->cmd |= + BF(len+1, APBX_CHn_CMD_XFER_COUNT); + + i2c_dma_write.command->pio_words[0] |= + BF(len+1, I2C_CTRL0_XFER_COUNT) | flags; + + i2c_dma_write.command->buf_ptr = i2c_buf_phys; + i2c_buf_virt[0] = addr | I2C_WRITE ; + memcpy(&i2c_buf_virt[1], buff, len); +} +EXPORT_SYMBOL(hw_i2c_setup_write); + +void hw_i2c_finish_read(void *buff, int len) +{ + memcpy(buff, &i2c_buf_virt[1], len); + +} +EXPORT_SYMBOL(hw_i2c_finish_read); + +void hw_i2c_setup_read(u8 addr, void *buff, int len, int flags) +{ + + if (len > (PAGE_SIZE - 4)) + BUG(); + + memcpy(i2c_dma_read[0].command, + &cmd_i2c_select, + sizeof(cmd_i2c_select)); + + memcpy(i2c_dma_read[1].command, + &cmd_i2c_read, + sizeof(cmd_i2c_read)); + + i2c_dma_read[0].command->next = i2c_dma_read[1].handle; + i2c_dma_read[0].command->buf_ptr = i2c_buf_phys ; + i2c_buf_virt[0] = addr | I2C_READ ; + + i2c_dma_read[1].command->cmd |= BF(len, APBX_CHn_CMD_XFER_COUNT); + + i2c_dma_read[1].command->pio_words[0] |= + BF(len, I2C_CTRL0_XFER_COUNT) | flags; + + i2c_dma_read[1].command->buf_ptr = (u32)i2c_buf_phys + 1 ; + memcpy(&i2c_buf_virt[1], buff, len); + +} +EXPORT_SYMBOL(hw_i2c_setup_read); + +void hw_i2c_run(int dir) +{ + if (dir == I2C_WRITE) + stmp3xxx_dma_go(dma_channel, &i2c_dma_write, 1); + else + stmp3xxx_dma_go(dma_channel, &i2c_dma_read[0], 1); +} +EXPORT_SYMBOL(hw_i2c_run); + +void hw_i2c_reset_dma(void) +{ + stmp3xxx_dma_reset_channel(dma_channel); + stmp3xxx_dma_clear_interrupt(dma_channel); +} +EXPORT_SYMBOL(hw_i2c_reset_dma); + + +int hw_i2c_init(struct device *dev) +{ + if (stmp3xxx_request_pin_group(dev->platform_data, "i2c")) + return -1; + + + /* Take controller out of reset */ + stmp3xxx_clearl(BM_I2C_CTRL0_SFTRST | + BM_I2C_CTRL0_CLKGATE, + REGS_I2C_BASE + HW_I2C_CTRL0); + udelay(10); + +/* * Set timing + * High time = 120 clks; read bit at 48 for 95Khz/24mhz + * Low time = 128 clks; write bit at 48 for 95khz/24mhz +*/ + +/* + Don't set 400khz by default; stfm1000 needs 100khz at the start. + __raw_writel(0x00780030, REGS_I2C_BASE + HW_I2C_TIMING0); + __raw_writel(0x001F000F, REGS_I2C_BASE + HW_I2C_TIMING1); + __raw_writel(0x0015000D, REGS_I2C_BASE + HW_I2C_TIMING2); +*/ + dev_dbg(dev, "I2C module version %x\n ", + __raw_readl(REGS_I2C_BASE + HW_I2C_VERSION)); + hw_i2c_init_dma(dev); + return 0; +} +EXPORT_SYMBOL(hw_i2c_init); + +void hw_i2c_stop(struct device *dev) +{ + stmp3xxx_setl(BM_I2C_CTRL0_SFTRST, + REGS_I2C_BASE + HW_I2C_CTRL0); + hw_i2c_reset_dma(); + hw_i2c_free_dma(dev); + stmp3xxx_release_pin_group(dev->platform_data, "i2c"); +} +EXPORT_SYMBOL(hw_i2c_stop); diff --git a/arch/arm/mach-stmp378x/stmp378x_lcdif.c b/arch/arm/mach-stmp378x/stmp378x_lcdif.c new file mode 100644 index 000000000000..abd1d7234950 --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_lcdif.c @@ -0,0 +1,208 @@ +/* + * Freescale STMP378X LCDIF low-level routines + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* #define DEBUG */ + +#include <linux/dma-mapping.h> +#include <linux/delay.h> + +#include <mach/hardware.h> +#include <mach/dma.h> +#include <mach/dma.h> +#include <mach/regs-lcdif.h> +#include <mach/regs-pinctrl.h> +#include <mach/lcdif.h> + +#define MAX_CHAIN_LEN 10 + +static struct stmp3xxx_dma_descriptor video_dma_descriptor[MAX_CHAIN_LEN]; +static struct stmp3xxx_lcd_dma_chain_info dma_chain_info[MAX_CHAIN_LEN]; +static unsigned dma_chain_info_pos; + +void stmp3xxx_init_lcdif(void) +{ + /* Reset controller */ + stmp3xxx_setl(BM_LCDIF_CTRL_SFTRST, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + udelay(10); + + /* Take controller out of reset */ + stmp3xxx_clearl(BM_LCDIF_CTRL_SFTRST | BM_LCDIF_CTRL_CLKGATE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + /* Setup the bus protocol */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_MODE86, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + stmp3xxx_clearl(BM_LCDIF_CTRL1_BUSY_ENABLE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + /* Take display out of reset */ + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + /* VSYNC is an input by default */ + stmp3xxx_setl(BM_LCDIF_VDCTRL0_VSYNC_OEB, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + /* Reset display */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + udelay(10); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + udelay(10); +} + +EXPORT_SYMBOL(stmp3xxx_init_lcdif); + +static int stmp378x_lcd_master = 1; +int stmp3xxx_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize, + int lcd_master) +{ + int ret = 0; + + stmp378x_lcd_master = lcd_master; + if (lcd_master) { + stmp3xxx_setl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_CUR_BUF); + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + } else { + ret = + stmp3xxx_dma_request(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + dev, "lcdif"); + if (ret) { + dev_err(dev, + "stmp3xxx_dma_request failed: error %d\n", ret); + goto out; + } + + stmp3xxx_dma_reset_channel(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + + stmp3xxx_dma_clear_interrupt(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + stmp3xxx_dma_enable_interrupt(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + + dotclk_dma_chain_init(memsize, phys, video_dma_descriptor, + dma_chain_info, &dma_chain_info_pos); + } +out: + return ret; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_dma_init); + +void stmp3xxx_lcdif_dma_release(void) +{ + int i; + + if (stmp378x_lcd_master) { + stmp3xxx_clearl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + return; + } + + for (i = 0; i < dma_chain_info_pos; i++) + stmp3xxx_dma_free_command(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + &video_dma_descriptor[i]); + stmp3xxx_dma_release(STMP3XXX_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH)); + + dma_chain_info_pos = 0; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_dma_release); + +void stmp3xxx_lcdif_run(void) +{ + if (stmp378x_lcd_master) { + stmp3xxx_setl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BM_LCDIF_CTRL_RUN, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + } else { + video_dma_descriptor[dma_chain_info_pos - 1].command->cmd &= + ~BM_APBH_CHn_CMD_SEMAPHORE; + stmp3xxx_dma_go(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + video_dma_descriptor, 1); + } +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_run); + +void stmp3xxx_lcdif_stop(void) +{ + if (stmp378x_lcd_master) { + stmp3xxx_clearl(BM_LCDIF_CTRL_RUN, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + udelay(100); + } else { + video_dma_descriptor[dma_chain_info_pos - 1].command->cmd |= + BM_APBH_CHn_CMD_SEMAPHORE; + udelay(100); + } +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_stop); + +int stmp3xxx_lcdif_pan_display(dma_addr_t addr) +{ + if (stmp378x_lcd_master) + __raw_writel(addr, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + else { + int i; + /* Modify the chain addresses */ + for (i = 0; i < dma_chain_info_pos; ++i) { + *dma_chain_info[i].dma_addr_p = addr + + dma_chain_info[i].offset; + barrier(); + } + } + return 0; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_pan_display); + +static BLOCKING_NOTIFIER_HEAD(lcdif_client_list); + +int stmp3xxx_lcdif_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&lcdif_client_list, nb); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_register_client); + +void stmp3xxx_lcdif_unregister_client(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&lcdif_client_list, nb); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_unregister_client); + +void stmp3xxx_lcdif_notify_clients(unsigned long event, + struct stmp3xxx_platform_fb_entry *pentry) +{ + blocking_notifier_call_chain(&lcdif_client_list, event, pentry); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_notify_clients); diff --git a/arch/arm/mach-stmp378x/stmp378x_pwm_led.c b/arch/arm/mach-stmp378x/stmp378x_pwm_led.c new file mode 100644 index 000000000000..7fe4892d326b --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_pwm_led.c @@ -0,0 +1,54 @@ +/* + * Freescale STMP378X PWM LEDs pin multiplexing + * + * Author: Drew Bendetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/module.h> +#include <mach/pinmux.h> + +#define PWM_PINID(n) STMP3XXX_PINID(1, 26 + n) + +int pwm_led_pinmux_request(int pwmn, char *title) +{ + int rc = 0; + + /* PIN_FUN1 is PWM for these pins */ + rc = stmp3xxx_request_pin(PWM_PINID(pwmn), PIN_FUN1, title); + if (rc) + return rc; + + stmp3xxx_pin_voltage(PWM_PINID(pwmn), PIN_3_3V, title); + /* pwm0-3 support 4,8,12mA; pwm4 supports 8,16,24mA + * I'm forcing 8 here since it's the only one in common + */ + stmp3xxx_pin_strength(PWM_PINID(pwmn), PIN_8MA, title); + + return 0; +} +EXPORT_SYMBOL_GPL(pwm_led_pinmux_request); + +void pwm_led_pinmux_free(int pwmn, char *title) +{ + stmp3xxx_pin_voltage(PWM_PINID(pwmn), PIN_4MA, title); + stmp3xxx_pin_strength(PWM_PINID(pwmn), PIN_1_8V, title); + + stmp3xxx_release_pin(PWM_PINID(pwmn), title); +} +EXPORT_SYMBOL_GPL(pwm_led_pinmux_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>"); diff --git a/arch/arm/mach-stmp378x/tvenc.c b/arch/arm/mach-stmp378x/tvenc.c new file mode 100644 index 000000000000..85697b4a05c1 --- /dev/null +++ b/arch/arm/mach-stmp378x/tvenc.c @@ -0,0 +1,285 @@ +/* + * Freescale STMP378X dvi panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* #define DEBUG */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> + +#include <mach/regs-lcdif.h> +#include <mach/regs-lradc.h> +#include <mach/regs-pinctrl.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-pwm.h> +#include <mach/regs-apbh.h> +#include <mach/gpio.h> +#include <mach/pins.h> +#include <mach/pinmux.h> +#include <mach/lcdif.h> +#include <mach/stmp3xxx.h> +#include <mach/platform.h> + +#include <mach/regs-tvenc.h> + +enum { + TVENC_MODE_OFF = 0, + TVENC_MODE_NTSC, + TVENC_MODE_PAL, +}; + +/* NTSC 720x480 mode */ +#define NTSC_X_RES 720 +#define NTSC_Y_RES 480 +#define NTSC_H_BLANKING 262 +#define NTSC_V_LINES 525 + +/* PAL 720x576 mode */ +#define PAL_X_RES 720 +#define PAL_Y_RES 576 +#define PAL_H_BLANKING 274 +#define PAL_V_LINES 625 + +/* frame size */ +#define DVI_H_BLANKING(m) (m == TVENC_MODE_NTSC ? \ + NTSC_H_BLANKING : PAL_H_BLANKING) +#define DVI_V_LINES(m) (m == TVENC_MODE_NTSC ? \ + NTSC_V_LINES : PAL_V_LINES) +#define DVI_H_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_X_RES : PAL_X_RES) +#define DVI_V_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_Y_RES : PAL_Y_RES) +/* fileds range */ +#define DVI_F1_START(m) 1 +#define DVI_F1_END(m) (DVI_V_LINES(m) / 2) +#define DVI_F2_START(m) (DVI_F1_END(m) + 1) +#define DVI_F2_END(m) DVI_V_LINES(m) +/* blanking range */ +#define DVI_V1_BLANK_START(m) DVI_F1_END(m) +#define DVI_V1_BLANK_END(m) (DVI_V1_BLANK_START(m) + \ + (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2) +#define DVI_V2_BLANK_START(m) DVI_F2_END(m) +#define DVI_V2_BLANK_END(m) ((DVI_V2_BLANK_START(m) + \ + (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2 - 1) % \ + DVI_V_LINES(m)) + +static struct clk *lcd_clk; +static struct clk *clk_ref_vid; +static struct clk *clk_tv108M_ng; +static struct clk *clk_tv27M; + +static int tvenc_mode; + +static void init_tvenc_hw(int mode) +{ + /* Reset module */ + stmp3xxx_setl(BM_TVENC_CTRL_SFTRST, REGS_TVENC_BASE + HW_TVENC_CTRL); + udelay(10); + + /* Take module out of reset */ + stmp3xxx_clearl(BM_TVENC_CTRL_SFTRST | BM_TVENC_CTRL_CLKGATE, + REGS_TVENC_BASE + HW_TVENC_CTRL); + + if (mode == TVENC_MODE_NTSC) { + /* Config NTSC-M mode, 8-bit Y/C in, SYNC out */ + stmp3xxx_clearl(BM_TVENC_CONFIG_SYNC_MODE | + BM_TVENC_CONFIG_PAL_SHAPE | + BM_TVENC_CONFIG_YGAIN_SEL | + BM_TVENC_CONFIG_CGAIN, + REGS_TVENC_BASE + HW_TVENC_CONFIG); + stmp3xxx_setl(BM_TVENC_CONFIG_FSYNC_PHS | + BF(0x4, TVENC_CONFIG_SYNC_MODE), + REGS_TVENC_BASE + HW_TVENC_CONFIG); + + /* 859 pixels/line for NTSC */ + __raw_writel(857, REGS_TVENC_BASE + HW_TVENC_SYNCOFFSET); + + __raw_writel(0x21F07C1F, REGS_TVENC_BASE + HW_TVENC_COLORSUB0); + stmp3xxx_clearl(BM_TVENC_COLORBURST_NBA | + BM_TVENC_COLORBURST_PBA, + REGS_TVENC_BASE + HW_TVENC_COLORBURST); + stmp3xxx_setl(BF(0xc8, TVENC_COLORBURST_NBA) | + BF(0, TVENC_COLORBURST_PBA), + REGS_TVENC_BASE + HW_TVENC_COLORBURST); + } else if (mode == TVENC_MODE_PAL) { + /* Config PAL-B mode, 8-bit Y/C in, SYNC out */ + stmp3xxx_clearl(BM_TVENC_CONFIG_SYNC_MODE | + BM_TVENC_CONFIG_ENCD_MODE | + BM_TVENC_CONFIG_YGAIN_SEL | + BM_TVENC_CONFIG_CGAIN | + BM_TVENC_CONFIG_FSYNC_PHS, + REGS_TVENC_BASE + HW_TVENC_CONFIG); + stmp3xxx_setl(BM_TVENC_CONFIG_PAL_SHAPE | + BF(1, TVENC_CONFIG_YGAIN_SEL) | BF(1, + TVENC_CONFIG_CGAIN) + | BF(0x1, TVENC_CONFIG_ENCD_MODE) | BF(0x4, + TVENC_CONFIG_SYNC_MODE), + REGS_TVENC_BASE + HW_TVENC_CONFIG); + + /* 863 pixels/line for PAL */ + __raw_writel(863, REGS_TVENC_BASE + HW_TVENC_SYNCOFFSET); + + __raw_writel(0x2A098ACB, REGS_TVENC_BASE + HW_TVENC_COLORSUB0); + stmp3xxx_clearl(BM_TVENC_COLORBURST_NBA | + BM_TVENC_COLORBURST_PBA, + REGS_TVENC_BASE + HW_TVENC_COLORBURST); + stmp3xxx_setl(BF(0xd6, TVENC_COLORBURST_NBA) | + BF(0x2a, TVENC_COLORBURST_PBA), + REGS_TVENC_BASE + HW_TVENC_COLORBURST); + } + + /* Power up DAC */ + __raw_writel(BM_TVENC_DACCTRL_GAINDN | + BM_TVENC_DACCTRL_GAINUP | + BM_TVENC_DACCTRL_PWRUP1 | + BM_TVENC_DACCTRL_DUMP_TOVDD1 | + BF(3, TVENC_DACCTRL_RVAL), + REGS_TVENC_BASE + HW_TVENC_DACCTRL); + + /* set all to zero is a requirement for NTSC */ + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION0); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION1); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION2); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION3); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION4); +} + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct stmp3xxx_platform_fb_entry *pentry) +{ + int ret = 0; + + lcd_clk = clk_get(dev, "lcdif"); + clk_enable(lcd_clk); + clk_set_rate(lcd_clk, 1000000 / pentry->cycle_time_ns); /* kHz */ + + clk_ref_vid = clk_get(NULL, "ref_vid"); + clk_tv108M_ng = clk_get(NULL, "tv108M_ng"); + clk_tv27M = clk_get(NULL, "tv27M"); + clk_enable(clk_ref_vid); + clk_enable(clk_tv108M_ng); + clk_enable(clk_tv27M); + + tvenc_mode = pentry->x_res == NTSC_Y_RES ? TVENC_MODE_NTSC : + TVENC_MODE_PAL; + + init_tvenc_hw(tvenc_mode); + + setup_dvi_panel(DVI_H_ACTIVE(tvenc_mode), DVI_V_ACTIVE(tvenc_mode), + DVI_H_BLANKING(tvenc_mode), DVI_V_LINES(tvenc_mode), + DVI_V1_BLANK_START(tvenc_mode), + DVI_V1_BLANK_END(tvenc_mode), + DVI_V2_BLANK_START(tvenc_mode), + DVI_V2_BLANK_END(tvenc_mode), + DVI_F1_START(tvenc_mode), DVI_F1_END(tvenc_mode), + DVI_F2_START(tvenc_mode), DVI_F2_END(tvenc_mode)); + + ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1); + + return ret; +} + +static void release_panel(struct device *dev, + struct stmp3xxx_platform_fb_entry *pentry) +{ + release_dvi_panel(); + + stmp3xxx_lcdif_dma_release(); + + clk_disable(clk_ref_vid); + clk_disable(clk_tv108M_ng); + clk_disable(clk_tv27M); + clk_disable(lcd_clk); + clk_put(clk_ref_vid); + clk_put(clk_tv108M_ng); + clk_put(clk_tv27M); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + stmp3xxx_clearl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + /* Wait until current transfer is complete, max 30ms */ + for (count = 30000; count > 0; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & + BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + stmp3xxx_setl(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static struct stmp3xxx_platform_fb_entry ntsc_fb_entry = { + .name = "tvenc_ntsc", + /* x/y swapped */ + .x_res = NTSC_Y_RES, + .y_res = NTSC_X_RES, + .bpp = 32, + /* the pix_clk should be near 27Mhz for proper syncronization */ + .cycle_time_ns = 74, + .lcd_type = STMP3XXX_LCD_PANEL_DVI, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = stmp3xxx_lcdif_run, + .pan_display = stmp3xxx_lcdif_pan_display, +}; + +static struct stmp3xxx_platform_fb_entry pal_fb_entry = { + .name = "tvenc_pal", + /* x/y swapped */ + .x_res = PAL_Y_RES, + .y_res = PAL_X_RES, + .bpp = 32, + /* the pix_clk should be near 27Mhz for proper syncronization */ + .cycle_time_ns = 74, + .lcd_type = STMP3XXX_LCD_PANEL_DVI, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = stmp3xxx_lcdif_run, + .pan_display = stmp3xxx_lcdif_pan_display, +}; + +static int __init register_devices(void) +{ + stmp3xxx_lcd_register_entry(&ntsc_fb_entry, + stmp3xxx_framebuffer.dev.platform_data); + stmp3xxx_lcd_register_entry(&pal_fb_entry, + stmp3xxx_framebuffer.dev.platform_data); + + return 0; +} + +subsys_initcall(register_devices); |