📄 twl4030_power.c
字号:
/* * arch/arm/plat-omap/power_companion.c * * This file contains code to control TWL4030 power compaion chip operation. * * Copyright (C) 2006 Texas Instruments, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#include <linux/types.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/delay.h>#include <asm/arch/power_companion.h>#include <asm/arch/clock.h>#include <asm/arch/mux.h>#include <asm/arch/prcm.h>#include <asm/semaphore.h>/* Note: H4 uses the Menelaus companion chip. Currently it is assumed u-boot * has set up the chip. For Triton2, some part of this is done by the loader. * However, it turns out that a polled mode u-boot I2C setup can take a long * time. So major init sequences are better done here. T2 needs to setup * sequences before it can effectively shut down its HF clock and handle more * complex power needs. */int power_companion_initialized = 0;struct semaphore pwr_reg_lock;static int vaux1_ldo_use_count;int twl4030_vaux1_ldo_use(void){ vaux1_ldo_use_count++; /* If first module to use the VAUX2 voltage, enable the voltage */ if (vaux1_ldo_use_count == 1) { if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX1_DEDICATED, TWL4030_VAUX1_DEDICATED)) { vaux1_ldo_use_count--; return -EIO; } if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX1_DEV_GRP, TWL4030_VAUX1_DEV_GRP)) { vaux1_ldo_use_count--; return -EIO; } } return 0;}EXPORT_SYMBOL(twl4030_vaux1_ldo_use);int twl4030_vaux1_ldo_unuse(void){ if (vaux1_ldo_use_count == 0) return 0; /* If VAUX2 voltage is not used by any modules, disable the voltage */ vaux1_ldo_use_count--; if (vaux1_ldo_use_count == 0) { /* * The VAUX2 voltage is not used by any module * Disable the voltage */ if (0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX1_DEDICATED)) { vaux1_ldo_use_count++; return -EIO; } if (0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX1_DEV_GRP)) { vaux1_ldo_use_count++; return -EIO; } } return 0;}EXPORT_SYMBOL(twl4030_vaux1_ldo_unuse);static int vaux2_ldo_use_count = 0;int twl4030_vaux2_ldo_use(void){ vaux2_ldo_use_count++; /* If first module to use the VAUX2 voltage, enable the voltage */ if(vaux2_ldo_use_count == 1){ if(0!= t2_out(PM_RECEIVER, ENABLE_VAUX2_DEDICATED, TWL4030_VAUX2_DEDICATED)) { vaux2_ldo_use_count --; return -EIO; } if(0!= t2_out(PM_RECEIVER, ENABLE_VAUX2_DEV_GRP, TWL4030_VAUX2_DEV_GRP)) { vaux2_ldo_use_count --; return -EIO; } } return 0;}int twl4030_vaux2_ldo_unuse(void){ if (vaux2_ldo_use_count == 0) return 0; /* If VAUX2 voltage is not used by any modules, disable the voltage */ vaux2_ldo_use_count--; if(vaux2_ldo_use_count == 0){ /* * The VAUX2 voltage is not used by any module * Disable the voltage */ if( 0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX2_DEDICATED)) { vaux2_ldo_use_count ++; return -EIO; } if( 0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX2_DEV_GRP)) { vaux2_ldo_use_count ++; return -EIO; } } return 0;}EXPORT_SYMBOL(twl4030_vaux2_ldo_use);EXPORT_SYMBOL(twl4030_vaux2_ldo_unuse);static int vaux3_ldo_use_count = 0;int twl4030_vaux3_ldo_use(void){ vaux3_ldo_use_count++; /* If first module to use the VAUX2 voltage, enable the voltage */ if(vaux3_ldo_use_count == 1){ if(0!= t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED, TWL4030_VAUX3_DEDICATED)) { vaux3_ldo_use_count --; return -EIO; } if(0!= t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP, TWL4030_VAUX3_DEV_GRP)) { vaux3_ldo_use_count --; return -EIO; } } return 0;}int twl4030_vaux3_ldo_unuse(void){ if (vaux3_ldo_use_count == 0) return 0; /* If VAUX3 voltage is not used by any modules, disable the voltage */ vaux3_ldo_use_count--; if(vaux3_ldo_use_count == 0){ /* * The VAUX3 voltage is not used by any module * Disable the voltage */ if( 0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX3_DEDICATED)) { vaux3_ldo_use_count ++; return -EIO; } if( 0 != t2_out(PM_RECEIVER, 0x00, TWL4030_VAUX3_DEV_GRP)) { vaux3_ldo_use_count ++; return -EIO; } } return 0;}EXPORT_SYMBOL(twl4030_vaux3_ldo_use);EXPORT_SYMBOL(twl4030_vaux3_ldo_unuse);int enable_vmode_companion_voltage_scaling(int vdd, int floor, int roof, int mode){ int e = 0; /* set floor, roof voltage, enable jump mode, enable scaling */ if (vdd == PRCM_VDD1) { e |= t2_out(PM_RECEIVER, floor, R_VDD1_VFLOOR); e |= t2_out(PM_RECEIVER, roof, R_VDD1_VROOF); e |= t2_out(PM_RECEIVER, mode, R_VDD1_STEP); e |= t2_out(PM_RECEIVER, EN_SCALE, R_VDD1_VMODE_CFG); } else if (vdd == PRCM_VDD2) { e |= t2_out(PM_RECEIVER, floor, R_VDD2_VFLOOR); e |= t2_out(PM_RECEIVER, roof, R_VDD2_VROOF); e |= t2_out(PM_RECEIVER, mode, R_VDD2_STEP); e |= t2_out(PM_RECEIVER, EN_SCALE, R_VDD2_VMODE_CFG); } if(e){ printk(KERN_ERR "Error enabling voltage scaling\n"); e = -EIO; } return e;}EXPORT_SYMBOL(enable_vmode_companion_voltage_scaling);int disable_vmode_companion_voltage_scaling(void){ int e = 0; e = t2_out(PM_RECEIVER, DIS_SCALE, R_VDD1_VMODE_CFG); if(e) e = -EIO; return e;}EXPORT_SYMBOL(disable_vmode_companion_voltage_scaling);int write_val_companion_reg(u8 mod_no, u8 value, u8 reg){ int e = 0; down(&pwr_reg_lock); e |= t2_out(mod_no, value, reg); up(&pwr_reg_lock); if(e){ printk(KERN_ERR "TWL4030 Power Companion Register Access Error\n"); e = -EIO; } return e;}EXPORT_SYMBOL(write_val_companion_reg);int set_bits_companion_reg(u8 mod_no, u8 value, u8 reg){ int e = 0; u8 RdReg; down(&pwr_reg_lock); e |= t2_in(mod_no, &RdReg, reg); RdReg |= value; e |= t2_out(mod_no, RdReg, reg); up(&pwr_reg_lock); if(e){ printk(KERN_ERR "TWL4030 Power Companion Register Access Error\n"); e = -EIO; } return e;}EXPORT_SYMBOL(set_bits_companion_reg);int clear_bits_companion_reg(u8 mod_no, u8 value, u8 reg){ int e = 0; u8 RdReg; down(&pwr_reg_lock); e |= t2_in(mod_no, &RdReg, reg); RdReg &= ~value; e |= t2_out(mod_no, RdReg, reg); up(&pwr_reg_lock); if(e){ printk(KERN_ERR "TWL4030 Power Companion Register Access Error\n"); e = -EIO; } return e;}EXPORT_SYMBOL(clear_bits_companion_reg);static int protect_pm_master(void){ int e = 0; e = t2_out(PM_MASTER, KEY_LOCK, R_PROTECTKEY); return e;}static int unprotect_pm_master(void){ int e = 0; e |= t2_out(PM_MASTER, KEY_UNLOCK1, R_PROTECTKEY); e |= t2_out(PM_MASTER, KEY_UNLOCK2, R_PROTECTKEY); return e;}static int init_pwr_intc(void){ int e; /* Set interrupt to clear when we write it. Enable interrupt * exclusively between INT1 and INT2 */ e = t2_out(PM_INT, (SIH_COR|SIH_EXCLEN), R_PWR_SIH_CTRL); /* mask all PWR INT1/2 interrupts */ e |= t2_out(PM_INT, 0xFF, R_PWR_IMR1); e |= t2_out(PM_INT, 0xFF, R_PWR_IMR2); /* disable falling and rising edge of all PWR sources */ e |=t2_out(PM_INT, 0x00, R_PWR_EDR1); e |=t2_out(PM_INT, 0x00, R_PWR_EDR2); /* clear all PWR INT1/2 interrupts */ e |= t2_out(PM_INT, 0xFF, R_PWR_ISR1); e |= t2_out(PM_INT, 0xFF, R_PWR_ISR2); return e;}/* Set up an action to shut down T2's clocks and wake them up * following P1. Depending on usage this sequence might be * reduced to just a unicast to the HFCLKOUT resource. */static int config_sleep_wake_sequence(void){ #define A2S_I 2 #define AS2_J 4 #define S2A_I 3 #define S2A_J 4 u8 sleep_on_seq[A2S_I][AS2_J] = {{0x14, 0x78, 0x04, 0x2C}, {0x1E, 0x78, 0x02, 0x3F} }; u8 sleep_off_seq[S2A_I][S2A_J] = {{0x01, 0x7E, 0x30, 0x2E}, {0x1A, 0x7E, 0x37, 0x2F}, {0x1E, 0x7E, 0x02, 0x3F} }; int e=0, i=0, j=0; u8 data; /* CLKREQ is pulled high on the 2430SDP, therefore, we need to take * it out of the HFCLKOUT DEV_GRP for P1 else HFCLKOUT can't be stopped. */ e |=t2_out(PM_RECEIVER, 0x20, R_HFCLKOUT_DEV_GRP); /* Set ACTIVE to SLEEP SEQ address in T2 memory*/ e |=t2_out(PM_MASTER, 0x2B, R_SEQ_ADD_A2S); /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */ e |=t2_out(PM_MASTER, 0x2D, R_SEQ_ADD_SA12); /* Set SLEEP to ACTIVE SEQ address for P3 */ e |=t2_out(PM_MASTER, 0x2D, R_SEQ_ADD_S2A3); /* Install Active->Sleep (A2S) sequence */ while (i < A2S_I){ while (j < AS2_J){ data = (((0x2B + i) << 2) + j ); /*set starting t2-addr*/ e |= t2_out(PM_MASTER, data, R_MEMORY_ADDRESS); /*select t2-addr*/ data = sleep_on_seq[i][j]; /* get data */ e |= t2_out(PM_MASTER, data, R_MEMORY_DATA); /*data into t2-addr*/ j++; } j=0; i++; } i = j = 0; /* Install Sleep->Active (S2A) sequence */ while (i < S2A_I){ while (j < S2A_J){ data = (((0x2D + i) << 2) + j ); /* set starting t2-addr */ e |= t2_out(PM_MASTER, data, R_MEMORY_ADDRESS); /* sel t2-addr */ data = sleep_off_seq[i][j]; /* get data */ e |= t2_out(PM_MASTER, data, R_MEMORY_DATA); /* put into t2-addr */ j++; } j=0; i++; } /* P1/P2/P3 LVL_WAKEUP should be on LEVEL */ e |= t2_out(PM_MASTER, LVL_WAKEUP, R_P1_SW_EVENTS); e |= t2_out(PM_MASTER, LVL_WAKEUP, R_P2_SW_EVENTS); e |= t2_out(PM_MASTER, LVL_WAKEUP, R_P3_SW_EVENTS); if(e) printk(KERN_ERR "TWL4030 Power Companion seq config error\n"); return e;}int power_companion_init(void){ struct clk *osc; u32 rate, ctrl = HFCLK_FREQ_26_MHZ; int e = 0; if(power_companion_initialized != 0) return 0; osc = clk_get(NULL,"osc_ck"); rate = clk_get_rate(osc); clk_put(osc); switch(rate) { case 19200000 : ctrl = HFCLK_FREQ_19p2_MHZ; break; case 26000000 : ctrl = HFCLK_FREQ_26_MHZ; break; case 38400000 : ctrl = HFCLK_FREQ_38p4_MHZ; break; } ctrl |= HIGH_PERF_SQ; /* Semaphore lock to prevent concurrent access */ init_MUTEX(&pwr_reg_lock); e |= unprotect_pm_master(); e |= t2_out(PM_MASTER, ctrl, R_CFGBOOT); /* effect->MADC+USB ck en */ e |= init_pwr_intc(); e |= config_sleep_wake_sequence(); /* Set action for P1-SLEEP1 */ e |= protect_pm_master(); /* Enabling the SmartReflex */ e = set_bits_companion_reg(PM_RECEIVER, DCDC_GLOBAL_CFG_ENABLE_SRFLX, R_DCDC_GLOBAL_CFG); if (e) printk(KERN_INFO "Unable to enable SR\n"); if(e){ printk(KERN_ERR "TWL4030 Power Companion Init Error\n"); e = -EIO; } else { power_companion_initialized = 1; printk(KERN_INFO "TWL4030 Power Companion Active\n"); } return e;}EXPORT_SYMBOL(power_companion_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -