blob: 5e37ccf8797e3adc13d2cbc6d31fa649cdf7ba25 [file] [log] [blame]
/*
* drivers/amlogic/power/aml_pmu/aml1218-rw.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/utsname.h>
#include <linux/i2c.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/input.h>
#include <linux/amlogic/aml_pmu.h>
#define MAX_BUF 100
#define DEBUG_DVFS 0
int aml1218_write(int32_t add, uint8_t val)
{
int ret;
uint8_t buf[3] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
buf[2] = val & 0xff;
ret = i2c_transfer(pdev->adapter, msg, 1);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_write);
int aml1218_write16(int32_t add, uint16_t val)
{
int ret;
uint8_t buf[4] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
buf[2] = val & 0xff;
buf[3] = (val >> 8) & 0xff;
ret = i2c_transfer(pdev->adapter, msg, 1);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_write16);
int aml1218_writes(int32_t add, uint8_t *buff, int len)
{
int ret;
uint8_t buf[MAX_BUF] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = len + 2,
.buf = buf,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
memcpy(buf + 2, buff, len > MAX_BUF ? MAX_BUF : len);
ret = i2c_transfer(pdev->adapter, msg, 1);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_writes);
int aml1218_read(int add, uint8_t *val)
{
int ret;
uint8_t buf[2] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
},
{
.addr = AML1218_ADDR,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
ret = i2c_transfer(pdev->adapter, msg, 2);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_read);
int aml1218_read16(int add, uint16_t *val)
{
int ret;
uint8_t buf[2] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
},
{
.addr = AML1218_ADDR,
.flags = I2C_M_RD,
.len = 2,
.buf = (uint8_t *)val,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
ret = i2c_transfer(pdev->adapter, msg, 2);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_read16);
int aml1218_reads(int add, uint8_t *buff, int len)
{
int ret;
uint8_t buf[2] = {};
struct i2c_client *pdev;
struct i2c_msg msg[] = {
{
.addr = AML1218_ADDR,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
},
{
.addr = AML1218_ADDR,
.flags = I2C_M_RD,
.len = len,
.buf = buff,
}
};
pdev = g_aml1218_client;
buf[0] = add & 0xff;
buf[1] = (add >> 8) & 0x0f;
ret = i2c_transfer(pdev->adapter, msg, 2);
if (ret < 0) {
AML1218_DBG("%s: i2c transfer failed, ret:%d\n", __func__, ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_reads);
int aml1218_set_bits(int addr, uint8_t bits, uint8_t mask)
{
uint8_t val;
int ret;
ret = aml1218_read(addr, &val);
if (ret)
return ret;
val &= ~(mask);
val |= (bits & mask);
return aml1218_write(addr, val);
}
EXPORT_SYMBOL_GPL(aml1218_set_bits);
static int find_idx(uint32_t start, uint32_t target, uint32_t step, int size)
{
int i = 0;
if (start < target) {
AML1218_DBG("%s, invalid input of voltage:%u\n",
__func__, target);
return -1;
}
do {
if ((start - step) < target)
break;
start -= step;
i++;
} while (i < size);
if (i >= size) {
AML1218_DBG("%s, input voltage %u outof range\n",
__func__, target);
return -1;
}
return i;
}
static unsigned int VDDEE_voltage_table[] = { /* voltage table of VDDEE */
1184, 1170, 1156, 1142, 1128, 1114, 1100, 1086,
1073, 1059, 1045, 1031, 1017, 1003, 989, 975,
961, 947, 934, 920, 906, 892, 878, 864,
850, 836, 822, 808, 794, 781, 767, 753
};
int find_idx_by_vddEE_voltage(int voltage, unsigned int *table)
{
int i;
for (i = 0; i < 32; i++) {
if (voltage >= table[i])
break;
}
if (voltage == table[i])
return i;
if (i == 0)
return 0;
else
return i - 1;
}
int aml1218_set_vddEE_voltage(int voltage)
{
int addr = 0x005d;
int idx_to, idx_cur;
unsigned char val;
aml1218_read(addr, &val);
idx_cur = ((val & 0x7c) >> 2);
idx_to = find_idx_by_vddEE_voltage(voltage, VDDEE_voltage_table);
val &= ~0x7c;
val |= (idx_to << 2);
#ifdef DEBUG_DVFS
#if DEBUG_DVFS
pr_info("%s, idx_to:%x, idx_cur:%x, val:%x, voltage:%d\n",
__func__, idx_to, idx_cur, val, voltage);
#endif
#endif
aml1218_write(addr, val);
udelay(5 * 100);
return 0;
}
int aml1218_set_dcdc_voltage(int dcdc, uint32_t voltage)
{
int addr;
int idx_to;
int range = 64;
int step = 1875 * 10;
int start = 1881 * 1000;
int idx_cur;
uint8_t val = 0;
static uint8_t dcdc_val[3] = {};
if (dcdc == 4) {
aml1218_set_vddEE_voltage(voltage / 1000);
return 0;
}
if (dcdc > 3 || dcdc < 0)
return -1;
addr = 0x34+(dcdc-1)*9;
if (dcdc == 3) {
step = 50 * 1000;
range = 32;
start = 3600 * 1000;
}
if (dcdc_val[dcdc] == 0)
aml1218_read(addr, &val); /* read first time */
else
val = dcdc_val[dcdc];
idx_to = find_idx(start, voltage, step, range);
idx_cur = (val & 0x7e) >> 1;
step = idx_cur - idx_to;
if (step < 0)
step = -step;
val &= ~0x7e;
val |= (idx_to << 1);
aml1218_write(addr, val);
udelay(20 * step);
dcdc_val[dcdc] = val;
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_set_dcdc_voltage);
int aml1218_get_dcdc_voltage(int dcdc, uint32_t *uV)
{
int addr;
uint8_t val;
int ret;
/* int start; */
if (dcdc == 4) {
addr = 0x5d;
ret = aml1218_read(addr, &val);
if (ret)
return ret;
*uV = VDDEE_voltage_table[(val >> 2) & 0x1f] * 1000;
return 0;
}
if (dcdc > 3 || dcdc < 0)
return -EINVAL;
addr = 0x34+(dcdc-1)*9;
ret = aml1218_read(addr, &val);
if (ret)
return ret;
val &= 0x7e;
val >>= 1;
if (dcdc == 3)
*uV = (3600000 - val * 50000); /* step: 50 mv */
else
*uV = (1881300 - val * 18750); /* step: 20 mv */
return 0;
}
EXPORT_SYMBOL_GPL(aml1218_get_dcdc_voltage);