/* * * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #define TRUE 1 #define FALSE 0 /* FTS regulator PMR registers */ #define SSBI_REG_ADDR_S1_PMR (0xA7) #define SSBI_REG_ADDR_S2_PMR (0xA8) #define SSBI_REG_ADDR_S3_PMR (0xA9) #define SSBI_REG_ADDR_S4_PMR (0xAA) #define REGULATOR_PMR_STATE_MASK 0x60 #define REGULATOR_PMR_STATE_OFF 0x20 /* Regulator control registers for shutdown/reset */ #define SSBI_REG_ADDR_L22_CTRL 0x121 /* SLEEP CNTL register */ #define SSBI_REG_ADDR_SLEEP_CNTL 0x02B #define PM8058_SLEEP_SMPL_EN_MASK 0x04 #define PM8058_SLEEP_SMPL_EN_RESET 0x04 #define PM8058_SLEEP_SMPL_EN_PWR_OFF 0x00 /* PON CNTL 1 register */ #define SSBI_REG_ADDR_PON_CNTL_1 0x01C #define PM8058_PON_PUP_MASK 0xF0 #define PM8058_PON_WD_EN_MASK 0x08 #define PM8058_PON_WD_EN_RESET 0x08 #define PM8058_PON_WD_EN_PWR_OFF 0x00 #define PM8058_RTC_CTRL 0x1E8 #define PM8058_RTC_ALARM_ENABLE BIT(1) #define PM_IRQ_ID_TO_BLOCK_INDEX(id) (uint8_t)(id / 8) #define PM_IRQ_ID_TO_BIT_MASK(id) (uint8_t)(1 << (id % 8)) /* HDMI MPP Registers */ #define SSBI_MPP_CNTRL_BASE 0x27 #define SSBI_MPP_CNTRL(n) (SSBI_MPP_CNTRL_BASE + (n)) #define PM8901_MPP_TYPE_MASK 0xE0 #define PM8901_MPP_CONFIG_LVL_MASK 0x1C #define PM8901_MPP_CONFIG_CTL_MASK 0x03 #define PM8901_MPP0_CTRL_VAL 0x30 #define VREG_PMR_STATE_MASK 0x60 #define VREG_PMR_STATE_HPM 0x7F #define VS_CTRL_USE_PMR 0xD0 #define VS_CTRL_ENABLE_MASK 0xD0 #define LDO_CTRL_VPROG_MASK 0x1F #define REGULATOR_EN_MASK 0x80 #define PM8901_HDMI_MVS_CTRL 0x058 #define PM8901_HDMI_MVS_PMR 0x0B8 #define PM8058_HDMI_L16_CTRL 0x08A typedef int (*pm8058_write_func) (unsigned char *, unsigned short, unsigned short); extern int pa1_ssbi2_write_bytes(unsigned char *buffer, unsigned short length, unsigned short slave_addr); extern int pa1_ssbi2_read_bytes(unsigned char *buffer, unsigned short length, unsigned short slave_addr); extern int pa2_ssbi2_write_bytes(unsigned char *buffer, unsigned short length, unsigned short slave_addr); extern int pa2_ssbi2_read_bytes(unsigned char *buffer, unsigned short length, unsigned short slave_addr); /* PM8058 APIs */ int pm8058_write(uint16_t addr, uint8_t * data, uint16_t length) { return pa1_ssbi2_write_bytes(data, length, addr); } int pm8058_read(uint16_t addr, uint8_t * data, uint16_t length) { return pa1_ssbi2_read_bytes(data, length, addr); } void pm8058_write_one(unsigned data, unsigned address) { pm8058_write_func wr_function = &pa1_ssbi2_write_bytes; if (wr_function == NULL) return; if ((*wr_function) (&data, 1, address)) dprintf(CRITICAL, "Error in initializing register\n"); } int pm8058_get_irq_status(pm_irq_id_type irq, bool * rt_status) { unsigned block_index, reg_data, reg_mask; int errFlag; block_index = PM_IRQ_ID_TO_BLOCK_INDEX(irq); /* select the irq block */ errFlag = pa1_ssbi2_write_bytes(&block_index, 1, IRQ_BLOCK_SEL_USR_ADDR); if (errFlag) { dprintf(INFO, "Device Timeout"); return 1; } /* read real time status */ errFlag = pa1_ssbi2_read_bytes(®_data, 1, IRQ_STATUS_RT_USR_ADDR); if (errFlag) { dprintf(INFO, "Device Timeout"); return 1; } reg_mask = PM_IRQ_ID_TO_BIT_MASK(irq); if ((reg_data & reg_mask) == reg_mask) { /* The RT Status is high. */ *rt_status = TRUE; } else { /* The RT Status is low. */ *rt_status = FALSE; } return 0; } bool pm8058_gpio_get(unsigned int gpio) { pm_irq_id_type gpio_irq; bool status; int ret; gpio_irq = gpio + PM_GPIO01_CHGED_ST_IRQ_ID; ret = pm8058_get_irq_status(gpio_irq, &status); if (ret) dprintf(CRITICAL, "pm8058_gpio_get failed\n"); return status; } int pm8058_mwrite(uint16_t addr, uint8_t val, uint8_t mask, uint8_t * reg_save) { int rc = 0; uint8_t reg; reg = (*reg_save & ~mask) | (val & mask); if (reg != *reg_save) rc = pm8058_write(addr, ®, 1); if (rc) dprintf(CRITICAL, "pm8058_write failed; addr=%03X, rc=%d\n", addr, rc); else *reg_save = reg; return rc; } int pm8058_ldo_set_voltage() { int ret = 0; unsigned vprog = 0x00000110; ret = pm8058_mwrite(PM8058_HDMI_L16_CTRL, vprog, LDO_CTRL_VPROG_MASK, 0); if (ret) { dprintf(SPEW, "Failed to set voltage for l16 regulator\n"); } return ret; } int pm8058_vreg_enable() { int ret = 0; ret = pm8058_mwrite(PM8058_HDMI_L16_CTRL, REGULATOR_EN_MASK, REGULATOR_EN_MASK, 0); if (ret) { dprintf(SPEW, "Vreg enable failed for PM 8058\n"); } return ret; } /* PM8901 APIs */ /* * Write to the control registers on PMIC via the SSBI2 interface. * Returns : (0) on success and (-1) on error. */ int pm8901_write(uint8_t * buffer, uint32_t length, uint32_t slave_addr) { return pa2_ssbi2_write_bytes(buffer, length, slave_addr); } /* * Read from the control registers on PMIC via the SSBI2 interface. * Returns : (0) on success and (-1) on error. */ int pm8901_read(uint8_t * buffer, uint32_t length, uint32_t slave_addr) { return pa2_ssbi2_read_bytes(buffer, length, slave_addr); } /* * PMIC 8901 LDO vreg read. */ int pm8901_test_bank_read(uint8_t * buffer, uint8_t bank, uint16_t addr) { int ret = pm8901_write(&bank, 1, addr); /* if the write does not work we can't read. */ if (ret) { return ret; } return pm8901_read(buffer, 1, addr); } /* * PMIC 8901 LDO vreg write. */ int pm8901_vreg_write(uint8_t * buffer, uint8_t mask, uint16_t addr, uint8_t prev_val) { uint8_t reg; /* Clear the bits we want to try and set. */ reg = (prev_val & ~mask); /* Set the bits we want to set, before writing them to addr */ reg |= (*buffer & mask); return pm8901_write(®, 1, addr); } int pm8901_reset_pwr_off(int reset) { int rc = 0, i; uint8_t pmr; uint8_t pmr_addr[4] = { SSBI_REG_ADDR_S2_PMR, SSBI_REG_ADDR_S3_PMR, SSBI_REG_ADDR_S4_PMR, SSBI_REG_ADDR_S1_PMR, }; /* Turn off regulators S1, S2, S3, S4 when shutting down. */ if (!reset) { for (i = 0; i < 4; i++) { rc = pm8901_read(&pmr, 1, pmr_addr[i]); if (rc) { goto get_out; } pmr &= ~REGULATOR_PMR_STATE_MASK; pmr |= REGULATOR_PMR_STATE_OFF; rc = pm8901_write(&pmr, 1, pmr_addr[i]); if (rc) { goto get_out; } } } get_out: return rc; } int pm8901_ldo_disable(int ldo_id) { int rc = -1; uint8_t prev_val = 0x0, val = 0x3F, mask = 0x7F; if(ldo_id >= LDO_START && ldo_id <= LDO_END) { rc = pm8901_read(&prev_val, 1, PM8901_PMR_REG(ldo_id)); if (rc) goto get_out; rc = pm8901_vreg_write(&val, mask, PM8901_PMR_REG(ldo_id), prev_val); if (rc) goto get_out; } get_out: return rc; } int pm8058_reset_pwr_off(int reset) { int rc; uint8_t pon, ctrl, smpl; /* Set regulator L22 to 1.225V in high power mode. */ rc = pm8058_read(SSBI_REG_ADDR_L22_CTRL, &ctrl, 1); if (rc) { goto get_out3; } /* Leave pull-down state intact. */ ctrl &= 0x40; ctrl |= 0x93; rc = pm8058_write(SSBI_REG_ADDR_L22_CTRL, &ctrl, 1); if (rc) { } get_out3: if (!reset) { /* Only modify the SLEEP_CNTL reg if shutdown is desired. */ rc = pm8058_read(SSBI_REG_ADDR_SLEEP_CNTL, &smpl, 1); if (rc) { goto get_out2; } smpl &= ~PM8058_SLEEP_SMPL_EN_MASK; smpl |= PM8058_SLEEP_SMPL_EN_PWR_OFF; rc = pm8058_write(SSBI_REG_ADDR_SLEEP_CNTL, &smpl, 1); if (rc) { } } get_out2: rc = pm8058_read(SSBI_REG_ADDR_PON_CNTL_1, &pon, 1); if (rc) { goto get_out; } pon &= ~PM8058_PON_WD_EN_MASK; pon |= reset ? PM8058_PON_WD_EN_RESET : PM8058_PON_WD_EN_PWR_OFF; /* Enable all pullups */ pon |= PM8058_PON_PUP_MASK; rc = pm8058_write(SSBI_REG_ADDR_PON_CNTL_1, &pon, 1); if (rc) { goto get_out; } get_out: return rc; } int pm8058_rtc0_alarm_irq_disable(void) { int rc; uint8_t reg; rc = pm8058_read(PM8058_RTC_CTRL, ®, 1); if (rc) { return rc; } reg = (reg & ~PM8058_RTC_ALARM_ENABLE); rc = pm8058_write(PM8058_RTC_CTRL, ®, 1); if (rc) { return rc; } return rc; } int pm8901_mpp_enable() { uint8_t prevval = 0x0; uint16_t mask; uint8_t conf; int ret = 0; conf = PM8901_MPP0_CTRL_VAL; mask = PM8901_MPP_TYPE_MASK | PM8901_MPP_CONFIG_LVL_MASK | PM8901_MPP_CONFIG_CTL_MASK; if (ret = pm8901_vreg_write(&conf, mask, SSBI_MPP_CNTRL(0), prevval)) { dprintf(SPEW, "PM8901 MPP failed\n"); } return ret; } int pm8901_vs_enable() { uint8_t val = VREG_PMR_STATE_HPM; int prevval = 0x0; int ret = 0; if (ret = pm8901_vreg_write(&val, VREG_PMR_STATE_HPM, PM8901_HDMI_MVS_PMR, prevval)) { dprintf(SPEW, "pm8901_vreg_write failed for MVS PMR register\n"); return ret; } val = VS_CTRL_USE_PMR; if (ret = pm8901_vreg_write(&val, VS_CTRL_ENABLE_MASK, PM8901_HDMI_MVS_CTRL, prevval)) { dprintf(SPEW, "pm8901_vreg_write failed for MVS ctrl register\n"); return ret; } return ret; }