| /* |
| * * 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 <debug.h> |
| #include <reg.h> |
| #include <bits.h> |
| #include <platform/iomap.h> |
| #include <platform/pmic.h> |
| |
| #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; |
| } |