| /* |
| * NDA AND NEED-TO-KNOW REQUIRED |
| * |
| * Copyright © 2013-2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This file contains information that is proprietary to Synaptics |
| * Incorporated ("Synaptics"). The holder of this file shall treat all |
| * information contained herein as confidential, shall use the |
| * information only for its intended purpose, and shall not duplicate, |
| * disclose, or disseminate any of this information in any manner |
| * unless Synaptics has otherwise provided express, written |
| * permission. |
| * |
| * Use of the materials may require a license of intellectual property |
| * from a third party or from Synaptics. This file conveys no express |
| * or implied licenses to any intellectual property rights belonging |
| * to Synaptics. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| #include "i2c_driver.h" |
| #include "global.h" |
| #include "io.h" |
| #include "debug.h" |
| #include "memmap.h" |
| #include "chip_voltage_info.h" |
| #include "SysMgr.h" |
| #include "apbRegBase.h" |
| #include "apb_timer.h" |
| #include "lgpl_printf.h" |
| |
| #include "pv_comp.h" |
| |
| extern void delay_ms(unsigned int ms); |
| |
| #ifndef VOUT_CPU_ID |
| #define VOUT_CPU_ID (2) |
| #endif |
| |
| #ifndef VOUT_CORE_ID |
| #define VOUT_CORE_ID (0) |
| #endif |
| |
| #ifndef VOUT_CPU_CHANNEL |
| #define VOUT_CPU_CHANNEL (0) |
| #endif |
| |
| #ifndef VOUT_CORE_CHANNEL |
| #define VOUT_CORE_CHANNEL (1) |
| #endif |
| |
| // all defined under uv |
| #ifndef MINIMAL_VOL_STEP_SY20276 |
| #define MINIMAL_VOL_STEP_SY20276 10000 |
| #endif |
| #define MIN_VOLTAGE 600000 |
| #define MAX_VOLTAGE 1500000 |
| |
| #define SLAVE_ADDR 0x60 |
| #define RSVD 0xFFFFFFFF |
| #define REG_VSEL0 0x0 |
| |
| extern void udelay(unsigned long usec); |
| extern int i2c_master_init(int id, int speed, int b_10bit_addr); |
| extern int i2c_master_write_and_read(int id, int target_addr, unsigned char* send_buff, int send_len, unsigned char* recv_buff, int recv_len); |
| |
| static int pmic_volt2data(int volt, int *p_data) |
| { |
| unsigned int data; |
| |
| if ((volt >= MIN_VOLTAGE) && (volt <= MAX_VOLTAGE)) |
| data = (volt - MIN_VOLTAGE) / 10000; |
| else |
| data = RSVD; |
| |
| if (data == RSVD) |
| return -1; |
| else { |
| *p_data = data; |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int pmic_data2volt(int *p_volt, int vdata) |
| { |
| unsigned int vol; |
| int data = vdata; |
| |
| data = data & 0x7F; |
| if (data <= 0x7f && 0x5a <= data) |
| vol = MAX_VOLTAGE; |
| else if (data < 0x5a) |
| vol = MIN_VOLTAGE + data * 10000; |
| else |
| vol = RSVD; |
| |
| if (vol == RSVD) |
| return -1; |
| else { |
| *p_volt = vol; |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int sy20276_set_vol(int master_id, int slaveAddr, int volt) |
| { |
| int target = volt, vdata = 0; |
| unsigned char buff[2]; |
| unsigned char data_reg0, data; |
| unsigned int bulk_reg0 = REG_VSEL0; |
| int ret = 1; |
| |
| // read, SY20276b slave address is 0x60 |
| i2c_master_init(master_id, 100, 0); //int id, int speed, int b_10bit_addr |
| ret = i2c_master_write_and_read(master_id, slaveAddr, (unsigned char*)&bulk_reg0, 1, (unsigned char*)&data_reg0, 1); |
| if (ret) |
| { |
| lgpl_printf("PMIC i2c read fail !\n"); |
| return ret; |
| } |
| data_reg0 = data_reg0 & 0x80; |
| |
| ret = pmic_volt2data(target, &vdata); |
| if (ret == 0) { |
| data_reg0 = data_reg0 | (unsigned char)vdata; |
| lgpl_printf( "data_reg0 = 0x%x \n", data_reg0); |
| |
| buff[0] = (unsigned char)REG_VSEL0; |
| buff[1] = (unsigned char)data_reg0; |
| //i2c_master_init(master_id, 100, 0); |
| // write |
| ret = i2c_master_write_and_read(master_id, slaveAddr, buff, 2, (unsigned char*)0, 0); |
| if (ret) { |
| lgpl_printf("PMIC i2c write fail !\n"); |
| return ret; |
| } |
| |
| //i2c_master_init(master_id, 100, 0); //int id, int speed, int b_10bit_addr |
| // read |
| buff[0] = (unsigned char)REG_VSEL0; |
| ret = i2c_master_write_and_read(master_id, slaveAddr, buff, 1, (unsigned char *)&data, 1); |
| if (ret) { |
| lgpl_printf("PMIC i2c read fail !\n"); |
| return ret; |
| } |
| |
| if (data_reg0 != data) { |
| lgpl_printf( "PMIC sy20276: read:0x%02x != write:0x%02x\n", data, data_reg0); |
| return -1; |
| } |
| |
| lgpl_printf( "set to %d mv\n", target); |
| delay_ms(1); |
| } |
| else { |
| lgpl_printf( "Illegal target voltage for PMIC sy20276 !\n"); |
| return -1; |
| } |
| return ret; |
| } |
| |
| static int sy20276_get_vol(int master_id, int slaveAddr) |
| { |
| int read = 0xff; |
| int volt; |
| unsigned int bulk_reg = REG_VSEL0; |
| int ret = 1; |
| |
| i2c_master_init(master_id, 100, 0); //int id, int speed, int b_10bit_addr |
| // read, SY20276 slave address is 0x60 |
| ret = i2c_master_write_and_read(master_id, slaveAddr, (unsigned char*)&bulk_reg, 1, (unsigned char*)&read, 1); |
| |
| lgpl_printf( "\nREG_VSEL0 = 0x%x \n", read); |
| ret |= pmic_data2volt(&volt, read); |
| if(ret) { |
| lgpl_printf(" i2c read fail\n"); |
| return -1; |
| } |
| |
| return volt; |
| } |
| |
| static int i2c_get_cpu_volt(void) |
| { |
| int volt = sy20276_get_vol(VOUT_CPU_ID, SLAVE_ADDR); |
| |
| if(volt < 0) |
| return 0; |
| |
| return volt; |
| } |
| |
| static int i2c_get_core_volt(void) |
| { |
| int volt = sy20276_get_vol(VOUT_CORE_ID, SLAVE_ADDR); |
| |
| if(volt < 0) |
| return 0; |
| |
| return volt; |
| } |
| |
| static int i2c_set_volt(int master_id, int from, int to) |
| { |
| int volt = from; |
| |
| while (volt != to){ |
| if (volt > to){ |
| volt -= MINIMAL_VOL_STEP_SY20276; |
| if (volt < to) |
| volt = to; |
| }else{ |
| volt += MINIMAL_VOL_STEP_SY20276; |
| if (volt > to) |
| volt = to; |
| } |
| |
| if(0 != sy20276_set_vol(master_id, SLAVE_ADDR, volt)) |
| return 1; |
| //udelay(50); //FIXME: 1ms delayed |
| } |
| |
| return 0; |
| } |
| |
| static int i2c_set_vcpu_volt(int from, int to) |
| { |
| return i2c_set_volt(VOUT_CPU_ID, from ,to); |
| } |
| |
| static int i2c_set_vcore_volt(int from, int to) |
| { |
| return i2c_set_volt(VOUT_CORE_ID, from ,to); |
| } |
| |
| const dvfs_ops_t sy20276_ops = { |
| .get_vcpu_volt = i2c_get_cpu_volt, |
| .get_vcore_volt = i2c_get_core_volt, |
| .set_vcpu_volt = i2c_set_vcpu_volt, |
| .set_vcore_volt =i2c_set_vcore_volt |
| }; |