mirror of
https://github.com/revyos/th1520-vendor-uboot.git
synced 2026-09-16 20:32:09 +02:00
Linux_SDK_V2.0.2
Signed-off-by: devops_admin <devops_admin@alibabacloud.com>
This commit is contained in:
@@ -130,6 +130,8 @@ source "drivers/w1-eeprom/Kconfig"
|
||||
|
||||
source "drivers/watchdog/Kconfig"
|
||||
|
||||
source "drivers/mcu/Kconfig"
|
||||
|
||||
config PHYS_TO_BUS
|
||||
bool "Custom physical to bus address mapping"
|
||||
help
|
||||
|
||||
@@ -85,6 +85,9 @@ obj-y += misc/
|
||||
obj-$(CONFIG_MMC) += mmc/
|
||||
obj-$(CONFIG_NVME) += nvme/
|
||||
obj-$(CONFIG_PCI_ENDPOINT) += pci_endpoint/
|
||||
ifdef CONFIG_RV_BOOK
|
||||
obj-$(CONFIG_DM_POWER_DELIVERY) += power/power_delivery/
|
||||
endif
|
||||
obj-y += dfu/
|
||||
obj-$(CONFIG_PCH) += pch/
|
||||
obj-y += phy/allwinner/
|
||||
|
||||
@@ -135,6 +135,14 @@ config FASTBOOT_CMD_OEM_FORMAT
|
||||
relies on the env variable partitions to contain the list of
|
||||
partitions as required by the gpt command.
|
||||
|
||||
config FASTBOOT_CMD_OEM_NV_OPERATION
|
||||
bool "Enable the 'oem nv get/set' command"
|
||||
help
|
||||
Add support for the "oem get/set" command from a client.
|
||||
|
||||
config FASTBOOT_ECIES_AUTH
|
||||
bool "Enabled ecies fastboot auth"
|
||||
|
||||
endif # FASTBOOT
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -3,5 +3,7 @@
|
||||
obj-y += fb_common.o
|
||||
obj-y += fb_getvar.o
|
||||
obj-y += fb_command.o
|
||||
obj-$(CONFIG_FASTBOOT_ECIES_AUTH) += fb_ecies.o
|
||||
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
|
||||
obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
|
||||
obj-$(CONFIG_FASTBOOT_CMD_OEM_NV_OPERATION) += fb_nv_operation.o
|
||||
@@ -45,6 +45,19 @@ static void oem_format(char *, char *);
|
||||
static void oem_command(char *, char *);
|
||||
int image_have_head(unsigned long img_src_addr);
|
||||
|
||||
#if CONFIG_FASTBOOT_CMD_OEM_NV_OPERATION
|
||||
void oem_nv_get_proccess(char *cmd_parameter, char *response);
|
||||
void oem_nv_set_proccess(char *cmd_parameter, char *response);
|
||||
void oem_nv_factory_recovery_process(char *cmd_parameter, char *response);
|
||||
static void oem_nv_get(char *cmd_parameter, char *response);
|
||||
static void oem_nv_set(char *cmd_parameter, char *response);
|
||||
static void oem_fcty(char *cmd_parameter, char *response);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FASTBOOT_ECIES_AUTH
|
||||
int ecies_process_data(uint8_t * data, int data_size,char *response);
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
const char *command;
|
||||
void (*dispatch)(char *cmd_parameter, char *response);
|
||||
@@ -97,6 +110,20 @@ static const struct {
|
||||
.command = "oem command",
|
||||
.dispatch = oem_command,
|
||||
},
|
||||
#if CONFIG_FASTBOOT_CMD_OEM_NV_OPERATION
|
||||
[FASTBOOT_COMMAND_OEM_NV_GET] = {
|
||||
.command = "oem nv get",
|
||||
.dispatch = oem_nv_get,
|
||||
},
|
||||
[FASTBOOT_COMMAND_OEM_NV_SET] = {
|
||||
.command = "oem nv set",
|
||||
.dispatch = oem_nv_set,
|
||||
},
|
||||
[FASTBOOT_COMMAND_OEM_FCTY] = {
|
||||
.command = "oem fcty",
|
||||
.dispatch = oem_fcty,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -294,7 +321,90 @@ int check_image_board_id(uint8_t *image_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hibernate_image_cleaned_flag = 0;
|
||||
void clean_hibernate_image_header(char *response)
|
||||
{
|
||||
struct blk_desc *dev_desc;
|
||||
disk_partition_t info;
|
||||
if(0x03 == hibernate_image_cleaned_flag) //already erased all
|
||||
{
|
||||
return;
|
||||
}
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
|
||||
fastboot_fail("invalid mmc device", response);
|
||||
return;
|
||||
}
|
||||
char * buf = memalign(CONFIG_SYS_CACHELINE_SIZE,4096);
|
||||
if(!buf) {
|
||||
printf(" mem alloc for hibernate partition header failed!\n");
|
||||
return;
|
||||
}
|
||||
/* if fastresume partition exists, earse the old image header */
|
||||
if(part_get_info_by_name(dev_desc, "fastresume", &info)) {
|
||||
printf(" find fastresume partition , erase the header:\n");
|
||||
memset(buf,0xff,4096);
|
||||
if(blk_dwrite(dev_desc, info.start, 4096/info.blksz, buf) != 4096/info.blksz)
|
||||
{
|
||||
printf(" fastresume header write failed!\n");
|
||||
hibernate_image_cleaned_flag = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hibernate_image_cleaned_flag |= 0x1;
|
||||
}
|
||||
}
|
||||
/* if swap partition exists, earse the old image header */
|
||||
if(part_get_info_by_name(dev_desc, "swap", &info)) {
|
||||
printf(" find swap partition , erase the header:\n");
|
||||
memset(buf,0xff,4096);
|
||||
if(blk_dwrite(dev_desc, info.start, 4096/info.blksz, buf) != 4096/info.blksz)
|
||||
{
|
||||
printf(" swap header write failed!\n");
|
||||
hibernate_image_cleaned_flag = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hibernate_image_cleaned_flag |= 0x2;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
|
||||
void fasboot_uboot_write_process(void *buf, char *response)
|
||||
{
|
||||
char cmdbuf[32];
|
||||
u32 block_cnt;
|
||||
struct blk_desc *dev_desc;
|
||||
int ret = 0;
|
||||
disk_partition_t info;
|
||||
|
||||
ret = check_image_board_id(buf);
|
||||
if (ret != 0) {
|
||||
fastboot_fail("U-BOOT image does not match the type of BOARD", response);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
|
||||
fastboot_fail("invalid mmc device", response);
|
||||
return;
|
||||
}
|
||||
|
||||
run_command("mmc partconf 0 1 0 1", 0);
|
||||
|
||||
block_cnt = image_size / BLOCK_SIZE;
|
||||
if (image_size % BLOCK_SIZE) {
|
||||
block_cnt = block_cnt +1;
|
||||
}
|
||||
|
||||
sprintf(cmdbuf, "mmc write 0x%p 0 %x", buf, block_cnt);
|
||||
|
||||
run_command(cmdbuf, 0);
|
||||
run_command("mmc partconf 0 1 0 0", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* flash() - write the downloaded image to the indicated partition.
|
||||
*
|
||||
@@ -308,36 +418,10 @@ static void flash(char *cmd_parameter, char *response)
|
||||
{
|
||||
#ifdef THEAD_LIGHT_FASTBOOT
|
||||
char cmdbuf[32];
|
||||
u32 block_cnt;
|
||||
struct blk_desc *dev_desc;
|
||||
disk_partition_t info;
|
||||
int ret = 0;
|
||||
|
||||
if (strcmp(cmd_parameter, "uboot") == 0) {
|
||||
ret = check_image_board_id(fastboot_buf_addr);
|
||||
if (ret != 0) {
|
||||
fastboot_fail("U-BOOT image does not match the type of BOARD", response);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
|
||||
fastboot_fail("invalid mmc device", response);
|
||||
return;
|
||||
}
|
||||
|
||||
run_command("mmc partconf 0 1 0 1", 0);
|
||||
|
||||
block_cnt = image_size / BLOCK_SIZE;
|
||||
if (image_size % BLOCK_SIZE) {
|
||||
block_cnt = block_cnt +1;
|
||||
}
|
||||
|
||||
sprintf(cmdbuf, "mmc write 0x%p 0 %x", fastboot_buf_addr, block_cnt);
|
||||
|
||||
run_command(cmdbuf, 0);
|
||||
run_command("mmc partconf 0 1 0 0", 0);
|
||||
|
||||
fasboot_uboot_write_process(fastboot_buf_addr, response);
|
||||
} else if ((strcmp(cmd_parameter, "fw") == 0)) {
|
||||
memcpy((void *)LIGHT_FW_ADDR, fastboot_buf_addr, image_size);
|
||||
} else if ((strcmp(cmd_parameter, "uImage") == 0)) {
|
||||
@@ -352,13 +436,15 @@ static void flash(char *cmd_parameter, char *response)
|
||||
memcpy((void *)LIGHT_TF_FW_ADDR, fastboot_buf_addr, image_size);
|
||||
} else if ((strcmp(cmd_parameter, TEE_PART_NAME) == 0)) {
|
||||
memcpy((void *)LIGHT_TEE_FW_ADDR, fastboot_buf_addr, image_size);
|
||||
} else if ((strcmp(cmd_parameter, "boot") == 0)) {
|
||||
}
|
||||
#ifdef CONFIG_RV_BOOK
|
||||
else if ((strcmp(cmd_parameter, "boot") == 0)) {
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
|
||||
fastboot_fail("invalid mmc device", response);
|
||||
return;
|
||||
}
|
||||
/* if fastresume partition exists, earse the old image header */
|
||||
fastboot_fail("invalid mmc device", response);
|
||||
return;
|
||||
}
|
||||
/* if fastresume partition exists, earse the old image header */
|
||||
if(part_get_info_by_name(dev_desc, "fastresume", &info)) {
|
||||
printf(" find fastresume partition , erase the header:\n");
|
||||
char * buf = memalign(CONFIG_SYS_CACHELINE_SIZE,4096);
|
||||
@@ -371,6 +457,22 @@ static void flash(char *cmd_parameter, char *response)
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_FASTBOOT_ECIES_AUTH
|
||||
else if ((strcmp(cmd_parameter, "ecies") == 0)) {
|
||||
ecies_process_data(fastboot_buf_addr, image_size,response);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//If version is updated, hibernate image may not compatible with current,erase it.
|
||||
if ((strcmp(cmd_parameter, "boot") == 0)
|
||||
|| (strcmp(cmd_parameter, "uboot") == 0)
|
||||
|| (strcmp(cmd_parameter, "root") == 0)) {
|
||||
|
||||
clean_hibernate_image_header(response);
|
||||
}
|
||||
|
||||
if(strcmp(cmd_parameter, "uboot") == 0 || (strcmp(cmd_parameter, "fw") == 0) ||
|
||||
(strcmp(cmd_parameter, "uImage") == 0) || (strcmp(cmd_parameter, "dtb") == 0) ||
|
||||
(strcmp(cmd_parameter, "rootfs") == 0) || (strcmp(cmd_parameter, "aon") == 0)) {
|
||||
@@ -380,54 +482,7 @@ static void flash(char *cmd_parameter, char *response)
|
||||
#endif
|
||||
|
||||
#if CONFIG_IS_ENABLED(LIGHT_SEC_UPGRADE)
|
||||
if(strcmp(cmd_parameter, TF_IMG_UPD_NAME) == 0) {
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
/* tee/tf/uboot image must be written into stash partition */
|
||||
sprintf(cmdbuf, "%s", STASH_PART_NAME);
|
||||
fastboot_mmc_flash_write(cmdbuf, fastboot_buf_addr, image_size, response);
|
||||
#endif
|
||||
/* Send ACK to host */
|
||||
fastboot_okay(NULL, response);
|
||||
|
||||
/* set secure upgrade flag to indicate it is TF image upgrade*/
|
||||
sprintf(cmdbuf,"env set sec_upgrade_mode 0x%x", TF_SEC_UPGRADE_FLAG);
|
||||
run_command(cmdbuf, 0);
|
||||
run_command("saveenv", 0);
|
||||
run_command("reset", 0);
|
||||
return;
|
||||
} else if (strcmp(cmd_parameter, TEE_IMG_UPD_NAME) == 0) {
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
/* tee/tf/uboot image must be written into stash partition */
|
||||
sprintf(cmdbuf, "%s", STASH_PART_NAME);
|
||||
fastboot_mmc_flash_write(cmdbuf, fastboot_buf_addr, image_size, response);
|
||||
#endif
|
||||
|
||||
/* Send ACK to host */
|
||||
fastboot_okay(NULL, response);
|
||||
|
||||
/* set secure upgrade flag to indicate it is TEE image upgrade*/
|
||||
sprintf(cmdbuf,"env set sec_upgrade_mode 0x%x", TEE_SEC_UPGRADE_FLAG);
|
||||
run_command(cmdbuf, 0);
|
||||
run_command("saveenv", 0);
|
||||
run_command("reset", 0);
|
||||
return;
|
||||
} else if (strcmp(cmd_parameter, SBMETA_IMG_UPD_NAME) == 0) {
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
/* tee/tf/uboot image must be written into stash partition */
|
||||
sprintf(cmdbuf, "%s", STASH_PART_NAME);
|
||||
fastboot_mmc_flash_write(cmdbuf, fastboot_buf_addr, image_size, response);
|
||||
#endif
|
||||
|
||||
/* Send ACK to host */
|
||||
fastboot_okay(NULL, response);
|
||||
|
||||
/* set secure upgrade flag to indicate it is TEE image upgrade*/
|
||||
sprintf(cmdbuf,"env set sec_upgrade_mode 0x%x", SBMETA_SEC_UPGRADE_FLAG);
|
||||
run_command(cmdbuf, 0);
|
||||
run_command("saveenv", 0);
|
||||
run_command("reset", 0);
|
||||
return;
|
||||
} else if (strcmp(cmd_parameter, UBOOT_IMG_UPD_NAME) == 0) {
|
||||
if (strcmp(cmd_parameter, UBOOT_IMG_UPD_NAME) == 0) {
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
|
||||
env_set_hex("ubootupdsize", image_size);
|
||||
@@ -530,3 +585,38 @@ static void oem_command(char *cmd_parameter, char *response)
|
||||
else
|
||||
fastboot_okay(NULL, response);
|
||||
}
|
||||
|
||||
#if CONFIG_FASTBOOT_CMD_OEM_NV_OPERATION
|
||||
/**
|
||||
* oem_nv_get() - Execute the OEM NV GET command
|
||||
*
|
||||
* @cmd_parameter: Pointer to command parameter
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
static void oem_nv_get(char *cmd_parameter, char *response)
|
||||
{
|
||||
oem_nv_get_proccess(cmd_parameter,response);
|
||||
}
|
||||
|
||||
/**
|
||||
* oem_nv_set() - Execute the OEM NV Set command
|
||||
*
|
||||
* @cmd_parameter: Pointer to command parameter
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
static void oem_nv_set(char *cmd_parameter, char *response)
|
||||
{
|
||||
oem_nv_set_proccess(cmd_parameter,response);
|
||||
}
|
||||
|
||||
/**
|
||||
* oem_fcty() - Execute the OEM fcty command
|
||||
*
|
||||
* @cmd_parameter: Pointer to command parameter
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
static void oem_fcty(char *cmd_parameter, char *response)
|
||||
{
|
||||
oem_nv_factory_recovery_process(cmd_parameter,response);
|
||||
}
|
||||
#endif
|
||||
198
drivers/fastboot/fb_ecies.c
Normal file
198
drivers/fastboot/fb_ecies.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#include <sec_ecies_session.h>
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <env.h>
|
||||
#include <fastboot.h>
|
||||
#include <fastboot-internal.h>
|
||||
#include <fb_mmc.h>
|
||||
#include <fb_nand.h>
|
||||
#include <part.h>
|
||||
#include <stdlib.h>
|
||||
#include <csi_sec_img_verify.h>
|
||||
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
|
||||
extern void fasboot_uboot_write_process(void *buf, char *response);
|
||||
#define MAX_ECIES_IMAGE_SIZE (9*1024*1024)
|
||||
#define ECIES_MALLOC_START (MAX_ECIES_IMAGE_SIZE * 2)
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t file_size;
|
||||
char partition_name[64];
|
||||
} __attribute__((__packed__)) send_file_info_t;
|
||||
|
||||
static uint32_t ecies_file_pos = 0;
|
||||
static int slave_init = 0;
|
||||
|
||||
static int current_pos = 0;
|
||||
|
||||
void *csi_ecies_malloc(uint32_t size)
|
||||
{
|
||||
void * ptr = NULL;
|
||||
|
||||
if (current_pos + size >= CONFIG_FASTBOOT_BUF_SIZE) {
|
||||
current_pos = 0;
|
||||
}
|
||||
|
||||
ptr = (void *)(long)(CONFIG_FASTBOOT_BUF_ADDR + ECIES_MALLOC_START + current_pos);
|
||||
current_pos += size;
|
||||
return ptr;
|
||||
};
|
||||
|
||||
void csi_ecies_free(void *buffer)
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
static int ecies_data_write(uint8_t *buf,uint8_t *data,int data_len)
|
||||
{
|
||||
char response[FASTBOOT_RESPONSE_LEN];
|
||||
|
||||
if (data_len == 0 || buf == NULL || data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf + ecies_file_pos,data,data_len);
|
||||
ecies_file_pos += data_len;
|
||||
if (ecies_file_pos == sizeof(send_file_info_t) + ((send_file_info_t *)buf)->file_size) {
|
||||
if (strcmp(((send_file_info_t *)buf)->partition_name, TEE_PART_NAME) == 0) {
|
||||
memcpy((void *)LIGHT_TEE_FW_ADDR, buf + sizeof(send_file_info_t), ((send_file_info_t *)buf)->file_size);
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
fastboot_mmc_flash_write(((send_file_info_t *)buf)->partition_name, buf + sizeof(send_file_info_t), ((send_file_info_t *)buf)->file_size,
|
||||
response);
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
|
||||
fastboot_nand_flash_write(((send_file_info_t *)buf)->partition_name, buf + sizeof(send_file_info_t), ((send_file_info_t *)buf)->file_size,
|
||||
response);
|
||||
#endif
|
||||
} else if (strcmp(((send_file_info_t *)buf)->partition_name, UBOOT_PART_NAME) == 0) {
|
||||
fasboot_uboot_write_process(buf + sizeof(send_file_info_t),response);
|
||||
} else {
|
||||
printf("unknown partition name\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
ecies_file_pos = 0;
|
||||
} else if(ecies_file_pos > sizeof(send_file_info_t) + ((send_file_info_t *)buf)->file_size) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hex_to_str(char *dest, const uint8_t *src, int len)
|
||||
{
|
||||
char ddl = 0;
|
||||
char ddh = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ddh = 48 + src[i] / 16;
|
||||
ddl = 48 + src[i] % 16;
|
||||
|
||||
if (ddh > 57) {
|
||||
ddh = ddh + 7;
|
||||
}
|
||||
|
||||
if (ddl > 57) {
|
||||
ddl = ddl + 7;
|
||||
}
|
||||
|
||||
dest[i * 2] = ddh;
|
||||
dest[i * 2 + 1] = ddl;
|
||||
}
|
||||
|
||||
dest[len * 2] = '\0';
|
||||
}
|
||||
|
||||
int ecies_process_data(uint8_t * data, int data_size,char *response)
|
||||
{
|
||||
static ecies_session_t ss_slave;
|
||||
char apduResponse[ECIES_INIT_RESPONSE_LEN] = {0};
|
||||
uint8_t *plaintext = csi_ecies_malloc(MAX_ECIES_IMAGE_SIZE);
|
||||
uint32_t plaintextLen = 0;
|
||||
uint32_t apduResponseLen = 0;
|
||||
int ret = 0;
|
||||
uint8_t cla = 0;
|
||||
uint8_t errcode = 0;
|
||||
|
||||
if (plaintext == NULL) {
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!slave_init) {
|
||||
csi_sec_library_init();
|
||||
ret = hal_ecies_slave_init(&ss_slave);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"hal_ecies_slave_init ERROR");
|
||||
ret = -2;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
ret = hal_ecies_slave_session_comm(&ss_slave, data, data_size, (uint8_t *)apduResponse, &apduResponseLen, plaintext, &plaintextLen);
|
||||
if (ret != 0) {
|
||||
ret = hal_ecies_status_get((uint8_t *)apduResponse, apduResponseLen, &cla, &errcode);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"hal_ecies_errcode_get ERROR");
|
||||
ret = -3;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (cla == ECIES_CLA_INITIALIZE_UPDATE_RESPONSE && errcode == ECIES_RESPONSE_SESSION_OPENED_ERROR) {
|
||||
slave_init = 0;
|
||||
ecies_file_pos = 0;
|
||||
hal_ecies_slave_uninit(&ss_slave);
|
||||
ret = hal_ecies_slave_init(&ss_slave);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"hal_ecies_slave_init ERROR");
|
||||
ret = -4;
|
||||
goto end;
|
||||
}
|
||||
ret = hal_ecies_slave_session_comm(&ss_slave, data, data_size, (uint8_t*)apduResponse, &apduResponseLen, plaintext, &plaintextLen);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"hal_ecies_slave_session_comm ERROR");
|
||||
ret = -5;
|
||||
goto end;
|
||||
}
|
||||
} else if(errcode != ECIES_RESPONSE_OK) {
|
||||
if (errcode == ECIES_CLA_SDATA_SEND_RESPONSE) {
|
||||
strcpy(response,"ECIES_CLA_SDATA_SEND_RESPONSE ERROR");
|
||||
ret = -6;
|
||||
goto end;
|
||||
} else if (errcode == ECIES_CLA_SESSION_CLOSE) {
|
||||
strcpy(response,"ECIES_CLA_SESSION_CLOSE ERROR");
|
||||
ret = -7;
|
||||
goto end;
|
||||
} else if (errcode == ECIES_CLA_INITIALIZE_UPDATE) {
|
||||
strcpy(response,"ECIES_CLA_INITIALIZE_UPDATE ERROR");
|
||||
ret = -8;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slave_init && plaintextLen) {
|
||||
ret = ecies_data_write((uint8_t*)(long)(CONFIG_FASTBOOT_BUF_ADDR + MAX_ECIES_IMAGE_SIZE),plaintext,plaintextLen);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ecies_data_write ERROR");
|
||||
ret = -8;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
slave_init = 1;
|
||||
|
||||
strcpy(response,"SUCCESS:");
|
||||
hex_to_str(response + strlen(response),(uint8_t*)apduResponse,apduResponseLen);
|
||||
ret = 0;
|
||||
end:
|
||||
if (plaintext) {
|
||||
csi_ecies_free(plaintext);
|
||||
plaintext = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
342
drivers/fastboot/fb_nv_operation.c
Normal file
342
drivers/fastboot/fb_nv_operation.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <fb_mmc.h>
|
||||
#include <command.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/types.h>
|
||||
#include <configs/light-c910.h>
|
||||
#include <thead/clock_config.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/arch-thead/light-iopmp.h>
|
||||
#include "../lib/sec_library/include/soc.h"
|
||||
|
||||
#define MAX_NV_NUMBER_SIZE 32
|
||||
#define MAX_NV_DATA_SIZE 128
|
||||
#define NV_BLOCK_SIZE 512
|
||||
|
||||
extern int32_t wj_efuse_get_lc(long unsigned int, int *lc);
|
||||
|
||||
typedef enum {
|
||||
FB_SYS_ACTION_FACTORY_RECOVER,
|
||||
FB_SYS_ACTION_READ_EFUSE,
|
||||
} fb_sys_action_t;
|
||||
|
||||
static int nv_get(uint8_t *data,int offset,int data_len)
|
||||
{
|
||||
struct blk_desc *dev_desc;
|
||||
struct disk_partition part_info;
|
||||
ulong block_start;
|
||||
uint8_t data_nv[NV_BLOCK_SIZE] = {0};
|
||||
int ret;
|
||||
int n;
|
||||
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (dev_desc == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = part_get_info_by_name(dev_desc, NV_PARTITION_NAME, &part_info);
|
||||
if (ret < 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(NV_BLOCK_SIZE != dev_desc->blksz) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
block_start = part_info.start + offset / dev_desc->blksz;
|
||||
n = blk_dread(dev_desc, block_start, 1, data_nv);
|
||||
if (n != 1) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
memcpy(data,data_nv + offset % NV_BLOCK_SIZE,data_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nv_set(uint8_t *data,int offset,int data_len)
|
||||
{
|
||||
struct blk_desc *dev_desc;
|
||||
struct disk_partition part_info;
|
||||
ulong block_start;
|
||||
uint8_t data_nv[NV_BLOCK_SIZE] = {0};
|
||||
int ret;
|
||||
int n;
|
||||
|
||||
|
||||
dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
||||
if (dev_desc == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = part_get_info_by_name(dev_desc, NV_PARTITION_NAME, &part_info);
|
||||
if (ret < 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (NV_BLOCK_SIZE != dev_desc->blksz) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
block_start = part_info.start + offset / dev_desc->blksz;
|
||||
|
||||
n = blk_dread(dev_desc, block_start, 1, data_nv);
|
||||
if (n != 1) {
|
||||
return -4;
|
||||
}
|
||||
memcpy(data_nv + offset % NV_BLOCK_SIZE,data,data_len);
|
||||
n = blk_dwrite(dev_desc, block_start, 1, data_nv);
|
||||
if (n != 1) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int factory_recovery(char *response)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
|
||||
fastboot_mmc_erase("metadata", response);
|
||||
if (strcmp(response, "OKAY") != 0) {
|
||||
return -1;
|
||||
}
|
||||
fastboot_mmc_erase("misc", response);
|
||||
if (strcmp(response, "OKAY") != 0) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
|
||||
fastboot_nand_erase("metadata", response);
|
||||
if (strcmp(response, "OKAY") != 0) {
|
||||
return -3;
|
||||
}
|
||||
fastboot_nand_erase("misc", response);
|
||||
if (strcmp(response, "OKAY") != 0) {
|
||||
return -4;
|
||||
}
|
||||
#endif
|
||||
|
||||
strcpy(response,"OKAY");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_efuse_status(char *response)
|
||||
{
|
||||
int ret;
|
||||
int lc = 0;
|
||||
ret = wj_efuse_get_lc(WJ_EFUSE_BASE, &lc);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(lc == 0) {
|
||||
strcpy(response,"LC_INIT");
|
||||
} else {
|
||||
strcpy(response,"LC_BLOWNED");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int sys_action(int flag,char *response)
|
||||
{
|
||||
if (flag == FB_SYS_ACTION_FACTORY_RECOVER) {
|
||||
return factory_recovery(response);
|
||||
} else if (flag == FB_SYS_ACTION_READ_EFUSE) {
|
||||
return read_efuse_status(response);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int toupper(int c)
|
||||
{
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return c - 32;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void str_to_hex(uint8_t *dest, const char *src, int len)
|
||||
{
|
||||
char h1, h2;
|
||||
unsigned char s1, s2;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
h1 = src[2 * i];
|
||||
h2 = src[2 * i + 1];
|
||||
s1 = toupper(h1) - 48;
|
||||
s2 = toupper(h2) - 48;
|
||||
if (s1 > 9) {
|
||||
s1 = s1 - 7;
|
||||
}
|
||||
|
||||
if (s2 > 9) {
|
||||
s2 = s2 - 7;
|
||||
}
|
||||
|
||||
dest[i] = s1 * 16 + s2;
|
||||
}
|
||||
}
|
||||
|
||||
static void hex_to_str(char *dest, const uint8_t *src, int len)
|
||||
{
|
||||
char ddl = 0;
|
||||
char ddh = 0;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ddh = 48 + src[i] / 16;
|
||||
ddl = 48 + src[i] % 16;
|
||||
|
||||
if (ddh > 57) {
|
||||
ddh = ddh + 7;
|
||||
}
|
||||
|
||||
if (ddl > 57) {
|
||||
ddl = ddl + 7;
|
||||
}
|
||||
|
||||
dest[i * 2] = ddh;
|
||||
dest[i * 2 + 1] = ddl;
|
||||
}
|
||||
|
||||
dest[len * 2] = '\0';
|
||||
}
|
||||
|
||||
static int char_to_int(char *data,int *out)
|
||||
{
|
||||
*out = 0;
|
||||
while(*data) {
|
||||
if(*data < '0' || *data > '9')
|
||||
return -1;
|
||||
|
||||
*out = *out * 10 + (*data - '0');
|
||||
++data;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if CONFIG_FASTBOOT_CMD_OEM_NV_OPERATION
|
||||
void oem_nv_factory_recovery_process(char *cmd_parameter, char *response)
|
||||
{
|
||||
int ret = 0;
|
||||
int flag = 0;
|
||||
|
||||
ret = char_to_int(cmd_parameter,&flag);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR FLAG INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = sys_action(flag,response);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR SYS ACTION FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void oem_nv_get_proccess(char *cmd_parameter, char *response)
|
||||
{
|
||||
char *sep = NULL;
|
||||
char offset[MAX_NV_NUMBER_SIZE] = {0};
|
||||
char len[MAX_NV_NUMBER_SIZE] = {0};
|
||||
int sep_pos = 0;
|
||||
uint8_t nv_data[MAX_NV_DATA_SIZE + 32] = {0};
|
||||
char nv_data_str[MAX_NV_DATA_SIZE * 2 + 1] = {0};
|
||||
int ret;
|
||||
int offset_int = 0;
|
||||
int len_int = 0;
|
||||
|
||||
sep = strstr(cmd_parameter, ":");
|
||||
if (sep == NULL) {
|
||||
strcpy(response,"ERROR INVALID PARAM");
|
||||
return;
|
||||
}
|
||||
|
||||
sep_pos = sep - cmd_parameter;
|
||||
memcpy(offset,cmd_parameter,sep - cmd_parameter);
|
||||
memcpy(len,cmd_parameter + (sep - cmd_parameter) + 1,strlen(cmd_parameter) - sep_pos - 1);
|
||||
|
||||
ret = char_to_int(offset,&offset_int);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR OFFSET INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = char_to_int(len,&len_int);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR LEN INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len_int > MAX_NV_DATA_SIZE) {
|
||||
strcpy(response,"ERROR NV SIZE TOO LARGE");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nv_get(nv_data, offset_int, len_int);
|
||||
if (ret != 0) {
|
||||
printf("nv_get failed:%d\n",ret);
|
||||
strcpy(response,"ERROR NV GET FAILED");
|
||||
return;
|
||||
}
|
||||
|
||||
hex_to_str(nv_data_str,nv_data,len_int);
|
||||
strcpy(response,"SUCCESS:");
|
||||
strcat(response,nv_data_str);
|
||||
}
|
||||
|
||||
void oem_nv_set_proccess(char *cmd_parameter, char *response)
|
||||
{
|
||||
char *sep = NULL;
|
||||
char offset[MAX_NV_NUMBER_SIZE] = {0};
|
||||
uint8_t nv_data[MAX_NV_DATA_SIZE + 32] = {0};
|
||||
char *nv_data_str;
|
||||
int data_len;
|
||||
int ret;
|
||||
int offset_int = 0;
|
||||
|
||||
sep = strstr(cmd_parameter, ":");
|
||||
if (sep == NULL) {
|
||||
strcpy(response,"ERROR INVALID PARAM");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(offset,cmd_parameter,sep - cmd_parameter);
|
||||
nv_data_str = cmd_parameter + (sep - cmd_parameter) + 1;
|
||||
|
||||
data_len = strlen(nv_data_str) / 2;
|
||||
|
||||
ret = char_to_int(offset,&offset_int);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR OFFSET INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data_len > MAX_NV_DATA_SIZE) {
|
||||
strcpy(response,"ERROR NV SIZE TOO LARGE");
|
||||
return;
|
||||
}
|
||||
|
||||
str_to_hex(nv_data,nv_data_str,data_len);
|
||||
|
||||
ret = nv_set(nv_data, offset_int, data_len);
|
||||
if (ret != 0) {
|
||||
strcpy(response,"ERROR NV SET FAILED");
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(response,"OKAY");
|
||||
}
|
||||
#endif
|
||||
15
drivers/mcu/Kconfig
Executable file
15
drivers/mcu/Kconfig
Executable file
@@ -0,0 +1,15 @@
|
||||
menu "MCU Support"
|
||||
|
||||
config DM_MCU
|
||||
bool "Enable driver model for mcu device support"
|
||||
depends on DM
|
||||
help
|
||||
This adds a simple uclass for mcu device.
|
||||
|
||||
config MCU_HC32fX
|
||||
bool "Enable HC32fX MCU support"
|
||||
depends on DM_MCU
|
||||
help
|
||||
This adds a driver for the HC32fX MCU support.
|
||||
|
||||
endmenu
|
||||
9
drivers/mcu/Makefile
Executable file
9
drivers/mcu/Makefile
Executable file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
# Written by Simon Glass <sjg@chromium.org>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DM_MCU) += mcu-uclass.o
|
||||
obj-$(CONFIG_MCU_HC32fX) += mcu_hc32fx.o
|
||||
65
drivers/mcu/mcu-uclass.c
Executable file
65
drivers/mcu/mcu-uclass.c
Executable file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <mcu/mcu-uclass.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
int _mcu_shutdown(struct udevice *dev)
|
||||
{
|
||||
struct mcu_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops->shutdown)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->shutdown(dev);
|
||||
}
|
||||
|
||||
int _mcu_poweron(struct udevice *dev)
|
||||
{
|
||||
struct mcu_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops->poweron)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->poweron(dev);
|
||||
}
|
||||
|
||||
int mcu_poweron(void)
|
||||
{
|
||||
struct udevice *mcu;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_MCU, 0, &mcu);
|
||||
if (ret) {
|
||||
printf("Get UCLASS_MCU failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return _mcu_poweron(mcu);
|
||||
}
|
||||
|
||||
int mcu_shutdown(void)
|
||||
{
|
||||
struct udevice *mcu;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_MCU, 0, &mcu);
|
||||
if (ret) {
|
||||
printf("Get charge display failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return _mcu_shutdown(mcu);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(mcu) = {
|
||||
.id = UCLASS_MCU,
|
||||
.name = "mcu",
|
||||
};
|
||||
97
drivers/mcu/mcu_hc32fx.c
Executable file
97
drivers/mcu/mcu_hc32fx.c
Executable file
@@ -0,0 +1,97 @@
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <mcu/mcu-uclass.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define HC32FX_POWEROFF_20 0x20
|
||||
#define HC32FX_POWERON_30 0x30
|
||||
#define POWER_OFF 0x55
|
||||
#define POWER_ON 0x01
|
||||
|
||||
struct hc32fx_info {
|
||||
struct udevice *dev;
|
||||
};
|
||||
|
||||
static u8 hc32fx_read(struct hc32fx_info *hc32fx, u8 reg)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(hc32fx->dev, reg, &val, 1);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
hc32fx->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hc32fx_write(struct hc32fx_info *hc32fx, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_write(hc32fx->dev, reg, &val, 1);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
hc32fx->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcu_hc32fx_poweron(struct udevice *dev)
|
||||
{
|
||||
struct hc32fx_info *hc32fx = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = hc32fx_write(hc32fx, HC32FX_POWERON_30, POWER_ON);
|
||||
if(ret)
|
||||
printf("set mcu POWERON fail\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcu_hc32fx_shutdown(struct udevice *dev)
|
||||
{
|
||||
struct hc32fx_info *hc32fx = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = hc32fx_write(hc32fx, HC32FX_POWEROFF_20, POWER_OFF);
|
||||
if(ret)
|
||||
printf("set mcu POWEROFF fail\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcu_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct hc32fx_info *priv = dev_get_priv(dev);
|
||||
priv->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mcu_ops mcu_hc32fx_ops = {
|
||||
.poweron = mcu_hc32fx_poweron,
|
||||
.shutdown = mcu_hc32fx_shutdown,
|
||||
};
|
||||
|
||||
static const struct udevice_id hc32fx_ops_ids[] = {
|
||||
{ .compatible = "mcu_hc32fx" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mcu_gpio) = {
|
||||
.name = "hc32fx-mcu",
|
||||
.id = UCLASS_MCU,
|
||||
.of_match = hc32fx_ops_ids,
|
||||
.ops = &mcu_hc32fx_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct hc32fx_info),
|
||||
.probe = mcu_gpio_probe,
|
||||
};
|
||||
@@ -2,10 +2,22 @@ menu "Power"
|
||||
|
||||
source "drivers/power/domain/Kconfig"
|
||||
|
||||
source "drivers/power/fuel_gauge/Kconfig"
|
||||
|
||||
source "drivers/power/pmic/Kconfig"
|
||||
|
||||
source "drivers/power/regulator/Kconfig"
|
||||
|
||||
source "drivers/power/charge/Kconfig"
|
||||
|
||||
source "drivers/power/power_delivery/Kconfig"
|
||||
|
||||
config DM_CHARGE_DISPLAY
|
||||
bool "Enable driver model for charge display support"
|
||||
depends on DM
|
||||
help
|
||||
This adds a simple uclass for charge display.
|
||||
|
||||
choice
|
||||
prompt "Select Sunxi PMIC Variant"
|
||||
depends on ARCH_SUNXI
|
||||
@@ -72,6 +84,13 @@ config SY8106A_POWER
|
||||
|
||||
endchoice
|
||||
|
||||
config CHARGE_ANIMATION
|
||||
bool "Enable charge animation"
|
||||
depends on DM_CHARGE_DISPLAY && DM_FUEL_GAUGE
|
||||
select ARM_CPU_SUSPEND
|
||||
help
|
||||
This adds a simple function for charge animation display.
|
||||
|
||||
config AXP_DCDC1_VOLT
|
||||
int "axp pmic dcdc1 voltage"
|
||||
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
# Copyright (c) 2009 Wind River Systems, Inc.
|
||||
# Tom Rix <Tom.Rix at windriver.com>
|
||||
|
||||
obj-$(CONFIG_DM_CHARGE_DISPLAY) += charge-display-uclass.o
|
||||
obj-$(CONFIG_AXP152_POWER) += axp152.o
|
||||
obj-$(CONFIG_AXP209_POWER) += axp209.o
|
||||
obj-$(CONFIG_AXP221_POWER) += axp221.o
|
||||
obj-$(CONFIG_AXP809_POWER) += axp809.o
|
||||
obj-$(CONFIG_AXP818_POWER) += axp818.o
|
||||
obj-$(CONFIG_CHARGE_ANIMATION) += charge_animation.o
|
||||
obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o
|
||||
obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o
|
||||
obj-$(CONFIG_SY8106A_POWER) += sy8106a.o
|
||||
|
||||
40
drivers/power/charge-display-uclass.c
Executable file
40
drivers/power/charge-display-uclass.c
Executable file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <command.h>
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <power/charge_display.h>
|
||||
|
||||
int charge_display_show(struct udevice *dev)
|
||||
{
|
||||
const struct dm_charge_display_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->show)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->show(dev);
|
||||
}
|
||||
|
||||
int charge_display(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct udevice *fg_dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device(UCLASS_CHARGE_DISPLAY, 0, &dev);
|
||||
if (ret) {
|
||||
debug("Get charge display failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return charge_display_show(dev);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(charge_display) = {
|
||||
.id = UCLASS_CHARGE_DISPLAY,
|
||||
.name = "charge_display",
|
||||
};
|
||||
5
drivers/power/charge/Kconfig
Executable file
5
drivers/power/charge/Kconfig
Executable file
@@ -0,0 +1,5 @@
|
||||
config CHARGER_BQ25700
|
||||
bool "BQ25700 charger support"
|
||||
depends on DM_FUEL_GAUGE
|
||||
help
|
||||
This adds support for BQ25700 charger support.
|
||||
7
drivers/power/charge/Makefile
Executable file
7
drivers/power/charge/Makefile
Executable file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CHARGER_BQ25700) += bq25700_charger.o
|
||||
334
drivers/power/charge/bq25700_charger.c
Executable file
334
drivers/power/charge/bq25700_charger.c
Executable file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* (C) Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <power/fuel_gauge.h>
|
||||
#include <power/pmic.h>
|
||||
#include <power/power_delivery/power_delivery.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define BQ25700_ID 0x25700
|
||||
#define BQ25703_ID 0x25703
|
||||
|
||||
#define COMPAT_BQ25700 "ti,bq25700"
|
||||
#define COMPAT_BQ25703 "ti,bq25703"
|
||||
|
||||
#define BQ25700_I2C_SPEED 100000
|
||||
#define BQ25700_CHARGE_CURRENT_1500MA 0x5C0
|
||||
#define BQ25700_SDP_INPUT_CURRENT_500MA 0xA00
|
||||
#define BQ25700_DCP_INPUT_CURRENT_1500MA 0x1E00
|
||||
#define BQ25700_DCP_INPUT_CURRENT_2000MA 0x2800
|
||||
#define BQ25700_DCP_INPUT_CURRENT_3000MA 0x3C00
|
||||
|
||||
#define WATCHDOG_ENSABLE (0x03 << 13)
|
||||
|
||||
#define BQ25700_CHARGEOPTION0_REG 0x12
|
||||
#define BQ25700_CHARGECURREN_REG 0x14
|
||||
#define BQ25700_CHARGERSTAUS_REG 0x20
|
||||
#define BQ25700_INPUTVOLTAGE_REG 0x3D
|
||||
#define BQ25700_INPUTCURREN_REG 0x3F
|
||||
|
||||
#define BQ25703_CHARGEOPTION0_REG 0x00
|
||||
#define BQ25703_CHARGECURREN_REG 0x02
|
||||
#define BQ25703_CHARGERSTAUS_REG 0x20
|
||||
#define BQ25703_INPUTVOLTAGE_REG 0x0A
|
||||
#define BQ25703_INPUTCURREN_REG 0x0E
|
||||
#define PD_MUN 2
|
||||
#define TYPEC0_I2C "i2c@ffe7f20000"
|
||||
#define TYPEC1_I2C "i2c@ffe7f24000"
|
||||
|
||||
enum bq25700_table_ids {
|
||||
/* range tables */
|
||||
TBL_ICHG,
|
||||
TBL_CHGMAX,
|
||||
TBL_INPUTVOL,
|
||||
TBL_INPUTCUR,
|
||||
TBL_SYSVMIN,
|
||||
TBL_OTGVOL,
|
||||
TBL_OTGCUR,
|
||||
TBL_EXTCON,
|
||||
};
|
||||
|
||||
struct bq25700 {
|
||||
struct udevice *dev;
|
||||
u32 ichg;
|
||||
u32 chip_id;
|
||||
struct udevice *pd[PD_MUN];
|
||||
};
|
||||
|
||||
struct bq25700_range {
|
||||
u32 min;
|
||||
u32 max;
|
||||
u32 step;
|
||||
};
|
||||
|
||||
static int bq25700_read(struct bq25700 *charger, uint reg)
|
||||
{
|
||||
u16 val;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(charger->dev, reg, (u8 *)&val, 2);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
charger->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int bq25700_write(struct bq25700 *charger, uint reg, u16 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_write(charger->dev, reg, (u8 *)&val, 2);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
charger->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const union {
|
||||
struct bq25700_range rt;
|
||||
} bq25700_tables[] = {
|
||||
/* range tables */
|
||||
[TBL_ICHG] = {.rt = {0, 8128000, 64000}},
|
||||
/* uV */
|
||||
[TBL_CHGMAX] = {.rt = {0, 19200000, 16000}},
|
||||
/* uV max charge voltage*/
|
||||
[TBL_INPUTVOL] = {.rt = {3200000, 19520000, 64000}},
|
||||
/* uV input charge voltage*/
|
||||
[TBL_INPUTCUR] = {.rt = {0, 6350000, 50000}},
|
||||
/*uA input current*/
|
||||
[TBL_SYSVMIN] = {.rt = {1024000, 16182000, 256000}},
|
||||
/* uV min system voltage*/
|
||||
[TBL_OTGVOL] = {.rt = {4480000, 20800000, 64000}},
|
||||
/*uV OTG volage*/
|
||||
[TBL_OTGCUR] = {.rt = {0, 6350000, 50000}},
|
||||
};
|
||||
|
||||
static u32 bq25700_find_idx(u32 value, enum bq25700_table_ids id)
|
||||
{
|
||||
const struct bq25700_range *rtbl = &bq25700_tables[id].rt;
|
||||
u32 rtbl_size;
|
||||
u32 idx;
|
||||
|
||||
rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
|
||||
|
||||
for (idx = 1;
|
||||
idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
|
||||
idx++)
|
||||
;
|
||||
|
||||
return idx - 1;
|
||||
}
|
||||
|
||||
static bool bq25700_charger_status(struct bq25700 *charger)
|
||||
{
|
||||
int state_of_charger;
|
||||
u16 value;
|
||||
|
||||
value = bq25700_read(charger, BQ25700_CHARGERSTAUS_REG);
|
||||
state_of_charger = value >> 15;
|
||||
|
||||
return state_of_charger;
|
||||
}
|
||||
|
||||
static bool bq25703_charger_status(struct bq25700 *charger)
|
||||
{
|
||||
int state_of_charger;
|
||||
u16 value;
|
||||
|
||||
value = bq25700_read(charger, BQ25703_CHARGERSTAUS_REG);
|
||||
state_of_charger = value >> 15;
|
||||
|
||||
return state_of_charger;
|
||||
}
|
||||
|
||||
static bool bq257xx_charger_status(struct udevice *dev)
|
||||
{
|
||||
struct bq25700 *charger = dev_get_priv(dev);
|
||||
|
||||
if (charger->chip_id == BQ25700_ID)
|
||||
return bq25700_charger_status(charger);
|
||||
else
|
||||
return bq25703_charger_status(charger);
|
||||
}
|
||||
|
||||
static int bq25700_charger_capability(struct udevice *dev)
|
||||
{
|
||||
return FG_CAP_CHARGER;
|
||||
}
|
||||
|
||||
static int bq25700_get_usb_type(void)
|
||||
{
|
||||
#ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2
|
||||
return rockchip_chg_get_type();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int bq25700_get_pd_output_val(struct bq25700 *charger,
|
||||
int *vol, int *cur)
|
||||
{
|
||||
struct power_delivery_data pd_data;
|
||||
int ret;
|
||||
|
||||
if (!charger->pd[0] && !charger->pd[1]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&pd_data, 0, sizeof(pd_data));
|
||||
int i = 0;
|
||||
for (i = 0; i < PD_MUN; i++) {
|
||||
if (!charger->pd[i]) {
|
||||
continue;
|
||||
}
|
||||
ret = power_delivery_get_data(charger->pd[i], &pd_data);
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
if (!pd_data.online || !pd_data.voltage || !pd_data.current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*vol = pd_data.voltage;
|
||||
*cur = pd_data.current;
|
||||
printf("voltage is %d current is %d\n", *vol, *cur);
|
||||
goto end;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bq25703_charger_current_init(struct bq25700 *charger)
|
||||
{
|
||||
u16 charge_current = BQ25700_CHARGE_CURRENT_1500MA;
|
||||
u16 sdp_inputcurrent = BQ25700_SDP_INPUT_CURRENT_500MA;
|
||||
u16 dcp_inputcurrent = BQ25700_DCP_INPUT_CURRENT_1500MA;
|
||||
int pd_inputvol, pd_inputcurrent;
|
||||
u16 vol_idx = 0, cur_idx;
|
||||
u16 temp;
|
||||
|
||||
temp = bq25700_read(charger, BQ25703_CHARGEOPTION0_REG);
|
||||
temp &= (~WATCHDOG_ENSABLE);
|
||||
bq25700_write(charger, BQ25703_CHARGEOPTION0_REG, temp);
|
||||
|
||||
if (!bq25700_get_pd_output_val(charger, &pd_inputvol,
|
||||
&pd_inputcurrent)) {
|
||||
if (pd_inputvol > 5000000) {
|
||||
vol_idx = bq25700_find_idx(pd_inputvol - 1280000 - 3200000,
|
||||
TBL_INPUTVOL);
|
||||
vol_idx = vol_idx << 6;
|
||||
}
|
||||
cur_idx = bq25700_find_idx(pd_inputcurrent,
|
||||
TBL_INPUTCUR);
|
||||
cur_idx = cur_idx << 8;
|
||||
if (pd_inputcurrent != 0)
|
||||
{
|
||||
bq25700_write(charger, BQ25703_INPUTCURREN_REG,
|
||||
cur_idx);
|
||||
if (vol_idx)
|
||||
bq25700_write(charger, BQ25703_INPUTVOLTAGE_REG,
|
||||
vol_idx);
|
||||
charge_current = bq25700_find_idx(pd_inputcurrent,
|
||||
TBL_ICHG);
|
||||
charge_current = charge_current << 6;
|
||||
}
|
||||
} else {
|
||||
bq25700_write(charger, BQ25703_INPUTCURREN_REG,
|
||||
dcp_inputcurrent);
|
||||
}
|
||||
|
||||
if (bq25703_charger_status(charger)) {
|
||||
bq25700_write(charger, BQ25703_CHARGECURREN_REG,
|
||||
charge_current);
|
||||
}
|
||||
}
|
||||
|
||||
static int bq25700_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct bq25700 *charger = dev_get_priv(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node, node1;
|
||||
|
||||
charger->dev = dev;
|
||||
|
||||
node = fdt_node_offset_by_compatible(blob, 0, COMPAT_BQ25700);
|
||||
node1 = fdt_node_offset_by_compatible(blob, 0, COMPAT_BQ25703);
|
||||
if ((node < 0) && (node1 < 0)) {
|
||||
printf("Can't find dts node for charger bq25700\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (node < 0) {
|
||||
node = node1;
|
||||
charger->chip_id = BQ25703_ID;
|
||||
} else {
|
||||
charger->chip_id = BQ25700_ID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_probe(struct udevice *dev)
|
||||
{
|
||||
struct bq25700 *charger = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
struct udevice *pd_tmp;
|
||||
struct udevice *dev_tmp;
|
||||
|
||||
for (uclass_first_device(UCLASS_PD, &pd_tmp);
|
||||
pd_tmp;
|
||||
uclass_next_device(&pd_tmp))
|
||||
{
|
||||
dev_tmp = dev_get_parent(pd_tmp);
|
||||
if (!strncmp(TYPEC0_I2C, dev_tmp->name, strlen(TYPEC0_I2C))) { // Ensure that typec0 has the highest priority
|
||||
charger->pd[0] = pd_tmp;
|
||||
} else if (!strncmp(TYPEC1_I2C, dev_tmp->name, strlen(TYPEC1_I2C))) {
|
||||
charger->pd[1] = pd_tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (charger->chip_id == BQ25703_ID) {
|
||||
bq25703_charger_current_init(charger);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id charger_ids[] = {
|
||||
{.compatible = "ti,bq25700"},
|
||||
{.compatible = "ti,bq25703"},
|
||||
{},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct dm_fuel_gauge_ops charger_ops = {
|
||||
.get_chrg_online = bq257xx_charger_status,
|
||||
.capability = bq25700_charger_capability,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(bq25700_charger) = {
|
||||
.name = "bq25700_charger",
|
||||
.id = UCLASS_FG,
|
||||
.probe = bq25700_probe,
|
||||
.of_match = charger_ids,
|
||||
.ops = &charger_ops,
|
||||
.ofdata_to_platdata = bq25700_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct bq25700),
|
||||
};
|
||||
366
drivers/power/charge_animation.c
Executable file
366
drivers/power/charge_animation.c
Executable file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <led.h>
|
||||
#include <rtc.h>
|
||||
#include <pwm.h>
|
||||
#include <power/charge_display.h>
|
||||
#include <power/charge_animation.h>
|
||||
#include <power/fuel_gauge.h>
|
||||
#include <power/pmic.h>
|
||||
#include <mcu/mcu-uclass.h>
|
||||
#ifdef CONFIG_IRQ
|
||||
#include <irq-generic.h>
|
||||
#include <rk_timer_irq.h>
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define IMAGE_RECALC_IDX -1
|
||||
#define IMAGE_SOC_100_IDX(n) ((n) - 2)
|
||||
#define IMAGE_LOWPOWER_IDX(n) ((n) - 1)
|
||||
#define SYSTEM_SUSPEND_DELAY_MS 5000
|
||||
#define FUEL_GAUGE_POLL_MS 1000
|
||||
|
||||
#define LED_CHARGING_NAME "battery_charging"
|
||||
#define LED_CHARGING_FULL_NAME "battery_full"
|
||||
#define LED_CHARGING_START_NAME "battery_start"
|
||||
|
||||
struct charge_image {
|
||||
const char *name;
|
||||
int soc;
|
||||
int period; /* ms */
|
||||
};
|
||||
|
||||
struct charge_animation_priv {
|
||||
struct udevice *fg;
|
||||
struct udevice *charger;
|
||||
struct udevice *mcu;
|
||||
#ifdef CONFIG_LED
|
||||
struct udevice *led_charging;
|
||||
struct udevice *led_full;
|
||||
struct udevice *led_start;
|
||||
#endif
|
||||
const struct charge_image *image;
|
||||
int image_num;
|
||||
|
||||
int auto_wakeup_key_state;
|
||||
ulong auto_screen_off_timeout; /* ms */
|
||||
ulong suspend_delay_timeout; /* ms */
|
||||
};
|
||||
|
||||
struct gpio_desc powerkey_gpio;
|
||||
static int leds_switch = 0;
|
||||
|
||||
#ifdef CONFIG_LED
|
||||
static int leds_update(struct udevice *dev, int soc)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
static int old_soc = -1;
|
||||
int ret, ledst;
|
||||
|
||||
if (old_soc == soc)
|
||||
return 0;
|
||||
|
||||
old_soc = soc;
|
||||
if (priv->led_charging) {
|
||||
ledst = (soc < 100) ? LEDST_ON : LEDST_OFF;
|
||||
ret = led_set_state(priv->led_charging, ledst);
|
||||
if (ret) {
|
||||
printf("set charging led %s failed, ret=%d\n",
|
||||
(ledst == LEDST_ON) ? "ON" : "OFF", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->led_full) {
|
||||
ledst = (soc == 100) ? LEDST_ON : LEDST_OFF;
|
||||
ret = led_set_state(priv->led_full, ledst);
|
||||
if (ret) {
|
||||
printf("set charging full led %s failed, ret=%d\n",
|
||||
ledst == LEDST_ON ? "ON" : "OFF", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_charge_on(struct udevice *dev, int soc)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
int ret, ledst;
|
||||
|
||||
ledst = LEDST_ON;
|
||||
ret = led_set_state(priv->led_full, ledst);
|
||||
if (ret) {
|
||||
printf("set charging full led %s failed, ret=%d\n",
|
||||
ledst == LEDST_ON ? "ON" : "OFF", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_charge_off(struct udevice *dev, int soc)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
int ret, ledst;
|
||||
ledst = LEDST_OFF;
|
||||
ret = led_set_state(priv->led_charging, ledst);
|
||||
if (ret) {
|
||||
printf("set charging full led %s failed, ret=%d\n",
|
||||
ledst == LEDST_ON ? "ON" : "OFF", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = led_set_state(priv->led_full, ledst);
|
||||
if (ret) {
|
||||
printf("set charging full led %s failed, ret=%d\n",
|
||||
ledst == LEDST_ON ? "ON" : "OFF", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_charge_update(struct udevice *dev, int soc)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
int ret, ledst;
|
||||
if (leds_switch > 5){
|
||||
leds_charge_on(dev, soc);
|
||||
} else {
|
||||
leds_charge_off(dev, soc);
|
||||
}
|
||||
leds_switch++;
|
||||
if (leds_switch > 10)
|
||||
leds_switch = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
static int leds_update(struct udevice *dev, int soc) { return 0; }
|
||||
|
||||
static int leds_charge_on(struct udevice *dev, int soc) { return 0; }
|
||||
|
||||
static int leds_charge_off(struct udevice *dev, int soc) { return 0; }
|
||||
|
||||
static int leds_charge_update(struct udevice *dev, int soc) { return 0; }
|
||||
#endif
|
||||
|
||||
static int charge_animation_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct charge_animation_pdata *pdata = dev_get_platdata(dev);
|
||||
pdata->low_power_voltage =
|
||||
dev_read_u32_default(dev, "uboot-low-power-voltage", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fg_charger_get_chrg_online(struct udevice *dev)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *charger;
|
||||
|
||||
charger = priv->charger ? : priv->fg;
|
||||
|
||||
return fuel_gauge_get_chrg_online(charger);
|
||||
}
|
||||
|
||||
static int get_reboot_state(void){
|
||||
const char *var_name = "battery_charge";
|
||||
char *value = env_get(var_name);
|
||||
if (value)
|
||||
if (strcmp(value, "0") == 0) {
|
||||
env_set(var_name, "1");
|
||||
env_save();
|
||||
return 0;
|
||||
}
|
||||
|
||||
env_set(var_name, "1");
|
||||
env_save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int charge_animation_show(struct udevice *dev)
|
||||
{
|
||||
int soc, voltage, ret, charging = 0;
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
struct charge_animation_pdata *pdata = dev_get_platdata(dev);
|
||||
struct udevice *fg = priv->fg;
|
||||
struct udevice *mcu = priv->mcu;
|
||||
|
||||
voltage = fuel_gauge_get_voltage(fg);
|
||||
if (voltage < 0)
|
||||
return -EINVAL;
|
||||
|
||||
while (voltage < pdata->low_power_voltage + 50) {
|
||||
soc = fuel_gauge_update_get_soc(fg);
|
||||
if (soc < 0 || soc > 100) {
|
||||
printf("get soc failed: %d\n", soc);
|
||||
continue;
|
||||
} else if (soc >= 1) {
|
||||
printf("soc is: %d\n", soc);
|
||||
break;
|
||||
}
|
||||
|
||||
voltage = fuel_gauge_get_voltage(fg);
|
||||
if (voltage < 0) {
|
||||
printf("get voltage failed: %d\n", voltage);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = leds_update(dev, soc);
|
||||
if (ret)
|
||||
printf("update led failed: %d\n", ret);
|
||||
|
||||
printf("soc is: %d voltage is :%d\n", soc, voltage);
|
||||
|
||||
charging = fg_charger_get_chrg_online(dev);
|
||||
if (charging <= 0) {
|
||||
mcu_shutdown(); // shutdown system power
|
||||
}
|
||||
mdelay(100);
|
||||
};
|
||||
leds_charge_off(dev, soc);
|
||||
|
||||
ret = get_reboot_state();
|
||||
|
||||
charging = fg_charger_get_chrg_online(dev);
|
||||
|
||||
if (!(charging <= 0) && ret != 0)
|
||||
while(1){
|
||||
ret = dm_gpio_get_value(&powerkey_gpio);
|
||||
if (ret == 0){
|
||||
break;
|
||||
}
|
||||
|
||||
charging = fg_charger_get_chrg_online(dev);
|
||||
if (charging <= 0) {
|
||||
mcu_shutdown(); // shutdown system power
|
||||
}
|
||||
|
||||
soc = fuel_gauge_update_get_soc(fg);
|
||||
if (soc == 100){
|
||||
leds_charge_on(dev, soc);
|
||||
}else if (soc < 100){
|
||||
leds_charge_update(dev, soc);
|
||||
}
|
||||
mdelay(300);
|
||||
}
|
||||
|
||||
leds_charge_off(dev, soc);
|
||||
|
||||
ret = led_set_state(priv->led_start, LEDST_ON);
|
||||
if (!ret)
|
||||
printf("Found Charging-Start LED\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fg_charger_get_device(struct udevice **fuel_gauge,
|
||||
struct udevice **charger)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int ret, cap;
|
||||
|
||||
*fuel_gauge = NULL,
|
||||
*charger = NULL;
|
||||
|
||||
ret = uclass_get(UCLASS_FG, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (uclass_first_device(UCLASS_FG, &dev);
|
||||
dev;
|
||||
uclass_next_device(&dev)) {
|
||||
cap = fuel_gauge_capability(dev);
|
||||
if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) {
|
||||
*fuel_gauge = dev;
|
||||
*charger = NULL;
|
||||
} else if (cap == FG_CAP_FUEL_GAUGE) {
|
||||
*fuel_gauge = dev;
|
||||
} else if (cap == FG_CAP_CHARGER) {
|
||||
*charger = dev;
|
||||
}
|
||||
}
|
||||
|
||||
return (*fuel_gauge) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static const struct dm_charge_display_ops charge_animation_ops = {
|
||||
.show = charge_animation_show,
|
||||
};
|
||||
|
||||
static int charge_animation_probe(struct udevice *dev)
|
||||
{
|
||||
struct charge_animation_priv *priv = dev_get_priv(dev);
|
||||
int ret, soc;
|
||||
/* Get PMIC: used for power off system */
|
||||
ret = uclass_get_device(UCLASS_MCU, 0, &priv->mcu);
|
||||
if (ret) {
|
||||
if (ret == -ENODEV)
|
||||
printf("Can't find MCU\n");
|
||||
else
|
||||
printf("Get UCLASS MCU failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/* Get fuel gauge and charger(If need) */
|
||||
ret = fg_charger_get_device(&priv->fg, &priv->charger);
|
||||
if (ret) {
|
||||
if (ret == -ENODEV)
|
||||
debug("Can't find FG\n");
|
||||
else
|
||||
debug("Get UCLASS FG failed: %d\n", ret);
|
||||
// return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_LED
|
||||
ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging);
|
||||
if (!ret)
|
||||
printf("Found Charging LED \n");
|
||||
|
||||
ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full);
|
||||
if (!ret)
|
||||
printf("Found Charging-Full LED\n");
|
||||
|
||||
ret = led_get_by_label(LED_CHARGING_START_NAME, &priv->led_start);
|
||||
if (!ret)
|
||||
printf("Found Charging-Start LED\n");
|
||||
#endif
|
||||
ret = gpio_request_by_name(dev, "powerkey-gpio", 0, &powerkey_gpio, 0);
|
||||
if (dm_gpio_is_valid(&powerkey_gpio)) {
|
||||
dm_gpio_set_dir_flags(&powerkey_gpio, GPIOD_IS_IN);
|
||||
}
|
||||
|
||||
printf("Enable charge animation display\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id charge_animation_ids[] = {
|
||||
{ .compatible = "rockchip,uboot-charge" },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(charge_animation) = {
|
||||
.name = "charge-animation",
|
||||
.id = UCLASS_CHARGE_DISPLAY,
|
||||
.probe = charge_animation_probe,
|
||||
.of_match = charge_animation_ids,
|
||||
.ops = &charge_animation_ops,
|
||||
.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
|
||||
.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
|
||||
};
|
||||
11
drivers/power/fuel_gauge/Kconfig
Executable file
11
drivers/power/fuel_gauge/Kconfig
Executable file
@@ -0,0 +1,11 @@
|
||||
config DM_FUEL_GAUGE
|
||||
bool "Enable driver model fuel gauge support"
|
||||
depends on DM
|
||||
help
|
||||
This adds a simple uclass for fuel gauge.
|
||||
|
||||
config POWER_FG_CW201X
|
||||
bool "CW201X Fuel gauge support"
|
||||
depends on DM_FUEL_GAUGE
|
||||
help
|
||||
This adds support for CW201X fuel gauge support.
|
||||
@@ -2,5 +2,6 @@
|
||||
#
|
||||
# Copyright (C) 2012 Samsung Electronics
|
||||
# Lukasz Majewski <l.majewski@samsung.com>
|
||||
|
||||
obj-$(CONFIG_$(SPL_)DM_FUEL_GAUGE) += fuel_gauge_uclass.o
|
||||
obj-$(CONFIG_POWER_FG_MAX17042) += fg_max17042.o
|
||||
obj-$(CONFIG_POWER_FG_CW201X) += fg_cw201x.o
|
||||
415
drivers/power/fuel_gauge/fg_cw201x.c
Executable file
415
drivers/power/fuel_gauge/fg_cw201x.c
Executable file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* (C) Copyright 2008-2015 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
// #include <linux/usb/phy-rockchip-usb2.h>
|
||||
#include <malloc.h>
|
||||
#include <power/battery.h>
|
||||
#include <power/fuel_gauge.h>
|
||||
// #include <power/pmic.h>
|
||||
#include "fg_regs.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define COMPAT_ROCKCHIP_CW201X "cw201x"
|
||||
|
||||
#define REG_VERSION 0x0
|
||||
#define REG_VCELL 0x2
|
||||
#define REG_SOC 0x4
|
||||
#define REG_RRT_ALERT 0x6
|
||||
#define REG_CONFIG 0x8
|
||||
#define REG_MODE 0xA
|
||||
#define REG_BATINFO 0x10
|
||||
|
||||
#define MODE_SLEEP_MASK (0x3 << 6)
|
||||
#define MODE_SLEEP (0x3 << 6)
|
||||
#define MODE_NORMAL (0x0 << 6)
|
||||
#define MODE_QUICK_START (0x3 << 4)
|
||||
#define MODE_RESTART (0xf << 0)
|
||||
|
||||
#define CONFIG_UPDATE_FLG (0x1 << 1)
|
||||
#define ATHD (0x0 << 3)
|
||||
|
||||
enum charger_type {
|
||||
CHARGER_TYPE_NO = 0,
|
||||
CHARGER_TYPE_USB,
|
||||
CHARGER_TYPE_AC,
|
||||
CHARGER_TYPE_DC,
|
||||
CHARGER_TYPE_UNDEF,
|
||||
};
|
||||
|
||||
struct cw201x_info {
|
||||
struct udevice *dev;
|
||||
int capacity;
|
||||
u32 *cw_bat_config_info;
|
||||
int divider_res1;
|
||||
int divider_res2;
|
||||
int hw_id_check;
|
||||
struct gpio_desc hw_id0;
|
||||
struct gpio_desc hw_id1;
|
||||
int support_dc_adp;
|
||||
struct gpio_desc dc_det_gpio;
|
||||
int dc_det_flag;
|
||||
bool dual_cell;
|
||||
};
|
||||
|
||||
static u8 cw201x_read(struct cw201x_info *cw201x, u8 reg)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(cw201x->dev, reg, &val, 1);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
cw201x->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cw201x_write(struct cw201x_info *cw201x, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_write(cw201x->dev, reg, &val, 1);
|
||||
if (ret) {
|
||||
printf("write error to device: %p register: %#x!",
|
||||
cw201x->dev, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 cw201x_read_half_word(struct cw201x_info *cw201x, int reg)
|
||||
{
|
||||
u8 vall, valh;
|
||||
u16 val;
|
||||
|
||||
valh = cw201x_read(cw201x, reg);
|
||||
vall = cw201x_read(cw201x, reg + 1);
|
||||
val = ((u16)valh << 8) | vall;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cw201x_parse_config_info(struct cw201x_info *cw201x)
|
||||
{
|
||||
int ret;
|
||||
int i, len, size;
|
||||
const u8 *info;
|
||||
struct udevice *dev = cw201x->dev;
|
||||
|
||||
if (dev_read_prop(dev, "bat_config_info", &len)) {
|
||||
len /= sizeof(u32);
|
||||
size = sizeof(*cw201x->cw_bat_config_info) * len;
|
||||
cw201x->cw_bat_config_info = calloc(size, 1);
|
||||
if (!cw201x->cw_bat_config_info) {
|
||||
printf("calloc cw_bat_config_info fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = dev_read_u32_array(dev, "bat_config_info",
|
||||
cw201x->cw_bat_config_info, len);
|
||||
if (ret) {
|
||||
printf("fdtdec_get cw_bat_config_info fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_read_prop(dev, "cellwise,battery-profile", &len))
|
||||
return -EINVAL;
|
||||
|
||||
size = sizeof(*cw201x->cw_bat_config_info) * len;
|
||||
cw201x->cw_bat_config_info = calloc(size, 1);
|
||||
if (!cw201x->cw_bat_config_info) {
|
||||
printf("calloc cw_bat_config_info fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = dev_read_u8_array_ptr(dev, "cellwise,battery-profile", len);
|
||||
if (!info) {
|
||||
printf("fdtdec_get battery profile fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
cw201x->cw_bat_config_info[i] = info[i];
|
||||
printf("%#x ", cw201x->cw_bat_config_info[i]);
|
||||
if ((i+1) % 8 == 0)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cw201x_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct cw201x_info *cw201x = dev_get_priv(dev);
|
||||
int ret;
|
||||
int hw_id0_val, hw_id1_val;
|
||||
|
||||
cw201x->dev = dev;
|
||||
ret = cw201x_parse_config_info(cw201x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cw201x->dual_cell = dev_read_bool(dev, "cellwise,dual-cell");
|
||||
ret = gpio_request_by_name_nodev(dev_ofnode(dev), "dc_det_gpio",
|
||||
0, &cw201x->dc_det_gpio, GPIOD_IS_IN);
|
||||
if (!ret) {
|
||||
cw201x->support_dc_adp = 1;
|
||||
printf("DC is valid\n");
|
||||
} else {
|
||||
printf("DC is invalid, ret=%d\n", ret);
|
||||
}
|
||||
|
||||
cw201x->hw_id_check = dev_read_u32_default(dev, "hw_id_check", 0);
|
||||
if (cw201x->hw_id_check) {
|
||||
ret = gpio_request_by_name_nodev(dev_ofnode(dev),
|
||||
"hw_id0_gpio", 0,
|
||||
&cw201x->hw_id0, GPIOD_IS_IN);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
hw_id0_val = dm_gpio_get_value(&cw201x->hw_id0);
|
||||
|
||||
ret = gpio_request_by_name_nodev(dev_ofnode(dev),
|
||||
"hw_id1_gpio", 0,
|
||||
&cw201x->hw_id1, GPIOD_IS_IN);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
hw_id1_val = dm_gpio_get_value(&cw201x->hw_id1);
|
||||
|
||||
/* ID1 = 0, ID0 = 1 : Battery */
|
||||
if (!hw_id0_val || hw_id1_val)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cw201x->divider_res1 = dev_read_u32_default(dev, "divider_res1", 0);
|
||||
cw201x->divider_res2 = dev_read_u32_default(dev, "divider_res2", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cw201x_get_vol(struct cw201x_info *cw201x)
|
||||
{
|
||||
u16 value16, value16_1, value16_2, value16_3;
|
||||
int voltage;
|
||||
int res1, res2;
|
||||
int retry = 0;
|
||||
|
||||
__retry:
|
||||
value16 = cw201x_read_half_word(cw201x, REG_VCELL);
|
||||
if (value16 < 0)
|
||||
return -1;
|
||||
|
||||
value16_1 = cw201x_read_half_word(cw201x, REG_VCELL);
|
||||
if (value16_1 < 0)
|
||||
return -1;
|
||||
|
||||
value16_2 = cw201x_read_half_word(cw201x, REG_VCELL);
|
||||
if (value16_2 < 0)
|
||||
return -1;
|
||||
|
||||
if (value16 > value16_1) {
|
||||
value16_3 = value16;
|
||||
value16 = value16_1;
|
||||
value16_1 = value16_3;
|
||||
}
|
||||
|
||||
if (value16_1 > value16_2) {
|
||||
value16_3 = value16_1;
|
||||
value16_1 = value16_2;
|
||||
value16_2 = value16_3;
|
||||
}
|
||||
|
||||
if (value16 > value16_1) {
|
||||
value16_3 = value16;
|
||||
value16 = value16_1;
|
||||
value16_1 = value16_3;
|
||||
}
|
||||
|
||||
voltage = value16_1 * 312 / 1024;
|
||||
if (voltage <= 0 && retry < 10) {
|
||||
retry++;
|
||||
mdelay(20);
|
||||
goto __retry;
|
||||
}
|
||||
|
||||
if (cw201x->divider_res1 &&
|
||||
cw201x->divider_res2) {
|
||||
res1 = cw201x->divider_res1;
|
||||
res2 = cw201x->divider_res2;
|
||||
voltage = voltage * (res1 + res2) / res2;
|
||||
}
|
||||
|
||||
if (cw201x->dual_cell)
|
||||
voltage *= 2;
|
||||
|
||||
// printf("the cw201x voltage=%d\n", voltage);
|
||||
return voltage;
|
||||
}
|
||||
|
||||
static int cw201x_dwc_otg_check_dpdm(void)
|
||||
{
|
||||
#if defined(CONFIG_PHY_ROCKCHIP_INNO_USB2) && !defined(CONFIG_SPL_BUILD)
|
||||
return rockchip_chg_get_type();
|
||||
#else
|
||||
printf("rockchip_chg_get_type() is not implement\n");
|
||||
return CHARGER_TYPE_NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int cw201x_get_usb_state(struct cw201x_info *cw201x)
|
||||
{
|
||||
int charger_type;
|
||||
|
||||
switch (cw201x_dwc_otg_check_dpdm()) {
|
||||
case 0:
|
||||
charger_type = CHARGER_TYPE_NO;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
charger_type = CHARGER_TYPE_USB;
|
||||
break;
|
||||
case 2:
|
||||
charger_type = CHARGER_TYPE_AC;
|
||||
break;
|
||||
default:
|
||||
charger_type = CHARGER_TYPE_NO;
|
||||
break;
|
||||
}
|
||||
|
||||
return charger_type;
|
||||
}
|
||||
|
||||
static bool cw201x_get_dc_state(struct cw201x_info *cw201x)
|
||||
{
|
||||
if (dm_gpio_get_value(&cw201x->dc_det_gpio))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cw201x_check_charge(struct cw201x_info *cw201x)
|
||||
{
|
||||
if (cw201x_get_usb_state(cw201x) != CHARGER_TYPE_NO)
|
||||
return true;
|
||||
if (cw201x_get_dc_state(cw201x))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cw201x_get_soc(struct cw201x_info *cw201x)
|
||||
{
|
||||
int cap, i = 0;
|
||||
|
||||
while (i < 10) {
|
||||
mdelay(30);
|
||||
cap = cw201x_read(cw201x, REG_SOC);
|
||||
if ((cap < 0) || (cap > 100))
|
||||
cap = cw201x->capacity;
|
||||
i++;
|
||||
if (cap)
|
||||
break;
|
||||
}
|
||||
cw201x->capacity = cap;
|
||||
|
||||
return cw201x->capacity;
|
||||
}
|
||||
|
||||
static int cw201x_update_get_soc(struct udevice *dev)
|
||||
{
|
||||
struct cw201x_info *cw201x = dev_get_priv(dev);
|
||||
|
||||
return cw201x_get_soc(cw201x);
|
||||
}
|
||||
|
||||
static int cw201x_update_get_voltage(struct udevice *dev)
|
||||
{
|
||||
struct cw201x_info *cw201x = dev_get_priv(dev);
|
||||
|
||||
return cw201x_get_vol(cw201x);
|
||||
}
|
||||
|
||||
static int cw201x_update_get_current(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cw201x_update_get_chrg_online(struct udevice *dev)
|
||||
{
|
||||
struct cw201x_info *cw201x = dev_get_priv(dev);
|
||||
|
||||
return cw201x_check_charge(cw201x);
|
||||
}
|
||||
|
||||
static int cw201x_capability(struct udevice *dev)
|
||||
{
|
||||
return FG_CAP_FUEL_GAUGE;
|
||||
}
|
||||
|
||||
static struct dm_fuel_gauge_ops cw201x_fg_ops = {
|
||||
.capability = cw201x_capability,
|
||||
.get_soc = cw201x_update_get_soc,
|
||||
.get_voltage = cw201x_update_get_voltage,
|
||||
.get_current = cw201x_update_get_current,
|
||||
.get_chrg_online = cw201x_update_get_chrg_online,
|
||||
};
|
||||
|
||||
static int cw201x_fg_cfg(struct cw201x_info *cw201x)
|
||||
{
|
||||
u8 val = MODE_SLEEP;
|
||||
int i;
|
||||
|
||||
if ((val & MODE_SLEEP_MASK) == MODE_SLEEP) {
|
||||
val = MODE_NORMAL;
|
||||
cw201x_write(cw201x, REG_MODE, val);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
cw201x_write(cw201x, REG_BATINFO + i,
|
||||
(u8)cw201x->cw_bat_config_info[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cw201x_fg_probe(struct udevice *dev)
|
||||
{
|
||||
struct cw201x_info *cw201x = dev_get_priv(dev);
|
||||
|
||||
cw201x->dev = dev;
|
||||
cw201x_fg_cfg(cw201x);
|
||||
|
||||
printf("vol: %d, soc: %d\n",
|
||||
cw201x_get_vol(cw201x), cw201x_get_soc(cw201x));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id cw201x_ids[] = {
|
||||
{ .compatible = "cw201x" },
|
||||
{ .compatible = "cellwise,cw2015" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cw201x_fg) = {
|
||||
.name = "cw201x_fg",
|
||||
.id = UCLASS_FG,
|
||||
.of_match = cw201x_ids,
|
||||
.probe = cw201x_fg_probe,
|
||||
.ofdata_to_platdata = cw201x_ofdata_to_platdata,
|
||||
.ops = &cw201x_fg_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct cw201x_info),
|
||||
};
|
||||
95
drivers/power/fuel_gauge/fg_regs.h
Executable file
95
drivers/power/fuel_gauge/fg_regs.h
Executable file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _FG_RK8XX_H_
|
||||
#define _FG_RK8XX_H_
|
||||
|
||||
/* register definition */
|
||||
#define SECONDS_REG 0X00
|
||||
#define VB_MON_REG 0x21
|
||||
#define THERMAL_REG 0x22
|
||||
#define SUP_STS_REG 0xA0
|
||||
#define USB_CTRL_REG 0xA1
|
||||
#define CHRG_CTRL_REG1 0xA3
|
||||
#define CHRG_CTRL_REG2 0xA4
|
||||
#define CHRG_CTRL_REG3 0xA5
|
||||
#define BAT_CTRL_REG 0xA6
|
||||
#define BAT_HTS_TS_REG 0xA8
|
||||
#define BAT_LTS_TS_REG 0xA9
|
||||
#define TS_CTRL_REG 0xAC
|
||||
#define ADC_CTRL_REG 0xAD
|
||||
#define GGCON_REG 0xB0
|
||||
#define GGSTS_REG 0xB1
|
||||
#define ZERO_CUR_ADC_REGH 0xB2
|
||||
#define ZERO_CUR_ADC_REGL 0xB3
|
||||
#define GASCNT_CAL_REG3 0xB4
|
||||
#define GASCNT_CAL_REG2 0xB5
|
||||
#define GASCNT_CAL_REG1 0xB6
|
||||
#define GASCNT_CAL_REG0 0xB7
|
||||
#define GASCNT_REG3 0xB8
|
||||
#define GASCNT_REG2 0xB9
|
||||
#define GASCNT_REG1 0xBA
|
||||
#define GASCNT_REG0 0xBB
|
||||
#define BAT_CUR_AVG_REGH 0xBC
|
||||
#define BAT_CUR_AVG_REGL 0xBD
|
||||
#define TS_ADC_REGH 0xBE
|
||||
#define TS_ADC_REGL 0xBF
|
||||
#define RK818_TS2_ADC_REGH 0xC0
|
||||
#define RK818_TS2_ADC_REGL 0xC1
|
||||
#define RK816_USB_ADC_REGH 0xC0
|
||||
#define RK816_USB_ADC_REGL 0xC1
|
||||
#define BAT_OCV_REGH 0xC2
|
||||
#define BAT_OCV_REGL 0xC3
|
||||
#define BAT_VOL_REGH 0xC4
|
||||
#define BAT_VOL_REGL 0xC5
|
||||
#define RELAX_ENTRY_THRES_REGH 0xC6
|
||||
#define RELAX_ENTRY_THRES_REGL 0xC7
|
||||
#define RELAX_EXIT_THRES_REGH 0xC8
|
||||
#define RELAX_EXIT_THRES_REGL 0xC9
|
||||
#define RELAX_VOL1_REGH 0xCA
|
||||
#define RELAX_VOL1_REGL 0xCB
|
||||
#define RELAX_VOL2_REGH 0xCC
|
||||
#define RELAX_VOL2_REGL 0xCD
|
||||
#define RELAX_CUR1_REGH 0xCE
|
||||
#define RELAX_CUR1_REGL 0xCF
|
||||
#define RELAX_CUR2_REGH 0xD0
|
||||
#define RELAX_CUR2_REGL 0xD1
|
||||
#define CAL_OFFSET_REGH 0xD2
|
||||
#define CAL_OFFSET_REGL 0xD3
|
||||
#define NON_ACT_TIMER_CNT_REG 0xD4
|
||||
#define VCALIB0_REGH 0xD5
|
||||
#define VCALIB0_REGL 0xD6
|
||||
#define VCALIB1_REGH 0xD7
|
||||
#define VCALIB1_REGL 0xD8
|
||||
#define FCC_GASCNT_REG3 0xD9
|
||||
#define FCC_GASCNT_REG2 0xDA
|
||||
#define FCC_GASCNT_REG1 0xDB
|
||||
#define FCC_GASCNT_REG0 0xDC
|
||||
#define IOFFSET_REGH 0xDD
|
||||
#define IOFFSET_REGL 0xDE
|
||||
#define SLEEP_CON_SAMP_CUR_REG 0xDF
|
||||
#define SOC_REG 0xE0
|
||||
#define REMAIN_CAP_REG3 0xE1
|
||||
#define REMAIN_CAP_REG2 0xE2
|
||||
#define REMAIN_CAP_REG1 0xE3
|
||||
#define REMAIN_CAP_REG0 0xE4
|
||||
#define UPDAT_LEVE_REG 0xE5
|
||||
#define NEW_FCC_REG3 0xE6
|
||||
#define NEW_FCC_REG2 0xE7
|
||||
#define NEW_FCC_REG1 0xE8
|
||||
#define NEW_FCC_REG0 0xE9
|
||||
#define NON_ACT_TIMER_CNT_SAVE_REG 0xEA
|
||||
#define OCV_VOL_VALID_REG 0xEB
|
||||
#define REBOOT_CNT_REG 0xEC
|
||||
#define POFFSET_REG 0xED
|
||||
#define MISC_MARK_REG 0xEE
|
||||
#define HALT_CNT_REG 0xEF
|
||||
#define DATA15_REG 0xEF
|
||||
#define DATA16_REG 0xF0
|
||||
#define DATA17_REG 0xF1
|
||||
#define DATA18_REG 0xF2
|
||||
|
||||
#endif
|
||||
136
drivers/power/fuel_gauge/fuel_gauge_uclass.c
Executable file
136
drivers/power/fuel_gauge/fuel_gauge_uclass.c
Executable file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <power/fuel_gauge.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int fuel_gauge_capability(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->capability)
|
||||
return (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE);
|
||||
|
||||
return ops->capability(dev);
|
||||
}
|
||||
|
||||
int fuel_gauge_bat_is_exist(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->bat_is_exist)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->bat_is_exist(dev);
|
||||
}
|
||||
|
||||
int fuel_gauge_get_current(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_current)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_current(dev);
|
||||
}
|
||||
|
||||
int fuel_gauge_get_voltage(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_voltage)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_voltage(dev);
|
||||
}
|
||||
|
||||
int fuel_gauge_update_get_soc(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_soc)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_soc(dev);
|
||||
}
|
||||
|
||||
bool fuel_gauge_get_chrg_online(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_chrg_online)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_chrg_online(dev);
|
||||
}
|
||||
|
||||
int fuel_gauge_get_temperature(struct udevice *dev, int *temp)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_temperature)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_temperature(dev, temp);
|
||||
}
|
||||
|
||||
int charger_set_charger_voltage(struct udevice *dev, int uV)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->set_charger_voltage)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_charger_voltage(dev, uV);
|
||||
}
|
||||
|
||||
int charger_set_current(struct udevice *dev, int ichrg_uA)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->set_charger_current)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_charger_current(dev, ichrg_uA);
|
||||
}
|
||||
|
||||
int charger_set_iprechg_current(struct udevice *dev, int iprechrg_uA)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->set_iprechg_current)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_iprechg_current(dev, iprechrg_uA);
|
||||
}
|
||||
|
||||
int charger_set_enable(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->set_charger_enable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_charger_enable(dev);
|
||||
}
|
||||
|
||||
int charger_set_disable(struct udevice *dev)
|
||||
{
|
||||
const struct dm_fuel_gauge_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->set_charger_disable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_charger_disable(dev);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(fuel_guage) = {
|
||||
.id = UCLASS_FG,
|
||||
.name = "fuel_gauge",
|
||||
};
|
||||
45
drivers/power/power_delivery/Kconfig
Executable file
45
drivers/power/power_delivery/Kconfig
Executable file
@@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config DM_POWER_DELIVERY
|
||||
bool "Enable driver model power delivery support"
|
||||
depends on DM
|
||||
help
|
||||
This adds a simple uclass for power delivery.
|
||||
|
||||
config TYPEC_TCPM
|
||||
tristate "USB Type-C Port Controller Manager"
|
||||
depends on DM && DM_POWER_DELIVERY
|
||||
help
|
||||
The Type-C Port Controller Manager provides a USB PD and USB Type-C
|
||||
state machine for use with Type-C Port Controllers.
|
||||
|
||||
config TYPEC_TCPCI
|
||||
tristate "Type-C Port Controller Interface driver"
|
||||
depends on DM && DM_POWER_DELIVERY && DM_I2C
|
||||
help
|
||||
Type-C Port Controller driver for TCPCI-compliant controller.
|
||||
|
||||
config RV_BOOK
|
||||
tristate "RV Book Type-C support"
|
||||
help
|
||||
Enable support for rvbook device's Type-C Port Controller.
|
||||
|
||||
if TYPEC_TCPCI
|
||||
|
||||
config TYPEC_HUSB311
|
||||
tristate "Hynetek HUSB311 Type-C chip driver"
|
||||
depends on DM && DM_POWER_DELIVERY && DM_I2C
|
||||
help
|
||||
Hynetek HUSB311 Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
|
||||
endif # TYPEC_TCPCI
|
||||
|
||||
config TYPEC_FUSB302
|
||||
tristate "Fairchild FUSB302 Type-C chip driver"
|
||||
depends on DM && DM_POWER_DELIVERY && DM_I2C
|
||||
help
|
||||
The Fairchild FUSB302 Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
7
drivers/power/power_delivery/Makefile
Executable file
7
drivers/power/power_delivery/Makefile
Executable file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_$(SPL_)DM_POWER_DELIVERY) += power_delivery_uclass.o
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
|
||||
obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
|
||||
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
|
||||
obj-$(CONFIG_TYPEC_HUSB311) += tcpci_husb311.o
|
||||
31
drivers/power/power_delivery/power_delivery_uclass.c
Executable file
31
drivers/power/power_delivery/power_delivery_uclass.c
Executable file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* (C) Copyright 2022 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <power/power_delivery/power_delivery.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int power_delivery_get_data(struct udevice *dev, struct power_delivery_data *pd_data)
|
||||
{
|
||||
const struct dm_power_delivery_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops || !ops->get_current || !ops->get_voltage || !ops->get_online)
|
||||
return -ENOSYS;
|
||||
|
||||
pd_data->voltage = ops->get_voltage(dev);
|
||||
pd_data->current = ops->get_current(dev);
|
||||
pd_data->online = ops->get_online(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(power_delivery) = {
|
||||
.id = UCLASS_PD,
|
||||
.name = "power_delivery",
|
||||
};
|
||||
778
drivers/power/power_delivery/tcpci.c
Executable file
778
drivers/power/power_delivery/tcpci.c
Executable file
@@ -0,0 +1,778 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2015-2017 Google, Inc
|
||||
*
|
||||
* USB Type-C Port Controller Interface.
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <power/power_delivery/pd.h>
|
||||
#include <power/power_delivery/tcpm.h>
|
||||
#include <power/power_delivery/typec.h>
|
||||
#include <power/power_delivery/power_delivery.h>
|
||||
|
||||
#include "tcpci.h"
|
||||
|
||||
#define PD_RETRY_COUNT 3
|
||||
|
||||
#define tcpc_presenting_cc1_rd(reg) \
|
||||
(!(TCPC_ROLE_CTRL_DRP & (reg)) && \
|
||||
(((reg) & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) == \
|
||||
(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT)))
|
||||
#define tcpc_presenting_cc2_rd(reg) \
|
||||
(!(TCPC_ROLE_CTRL_DRP & (reg)) && \
|
||||
(((reg) & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) == \
|
||||
(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT)))
|
||||
|
||||
struct tcpci {
|
||||
struct udevice *dev;
|
||||
|
||||
struct tcpm_port *port;
|
||||
|
||||
bool controls_vbus;
|
||||
bool gpio_cc_int_present;
|
||||
|
||||
struct tcpc_dev tcpc;
|
||||
struct tcpci_data *data;
|
||||
struct gpio_desc gpio_cc_int;
|
||||
};
|
||||
|
||||
struct tcpci_chip {
|
||||
struct udevice *udev;
|
||||
struct tcpci *tcpci;
|
||||
struct tcpci_data data;
|
||||
};
|
||||
|
||||
static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
|
||||
{
|
||||
return container_of(tcpc, struct tcpci, tcpc);
|
||||
}
|
||||
|
||||
static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 buffer[2];
|
||||
|
||||
ret = dm_i2c_read(tcpci->dev, reg, buffer, 2);
|
||||
if (ret) {
|
||||
printf("%s: cannot read %02x, ret=%d\n",
|
||||
__func__, reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ((buffer[1] << 8) & 0xFF00) | (buffer[0] & 0xFF);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcpci_block_read(struct tcpci *tcpci, unsigned int reg,
|
||||
u8 *data, u8 length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = dm_i2c_read(tcpci->dev, reg, data, length);
|
||||
if (ret)
|
||||
printf("%s: cannot block read 0x%02x, len=%d, ret=%d\n",
|
||||
__func__, reg, length, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 buffer[2];
|
||||
|
||||
buffer[0] = val & 0xFF;
|
||||
buffer[1] = (val >> 8) & 0xFF;
|
||||
ret = dm_i2c_write(tcpci->dev, reg, buffer, 2);
|
||||
if (ret)
|
||||
printf("%s: cannot write 0x%02x, ret=%d\n",
|
||||
__func__, reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcpci_block_write(struct tcpci *tcpci, unsigned int reg,
|
||||
u8 *data, u8 length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = dm_i2c_write(tcpci->dev, reg, data, length);
|
||||
if (ret)
|
||||
printf("%s: cannot block write 0x%02x, len=%d, ret=%d\n",
|
||||
__func__, reg, length, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
switch (cc) {
|
||||
case TYPEC_CC_RA:
|
||||
reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RD:
|
||||
reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RP_DEF:
|
||||
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_RP_VAL_DEF <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RP_1_5:
|
||||
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_RP_VAL_1_5 <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RP_3_0:
|
||||
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_RP_VAL_3_0 <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_OPEN:
|
||||
default:
|
||||
reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_ROLE_CTRL, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_start_toggling(struct tcpc_dev *tcpc,
|
||||
enum typec_port_type port_type,
|
||||
enum typec_cc_status cc)
|
||||
{
|
||||
int ret;
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg = TCPC_ROLE_CTRL_DRP;
|
||||
|
||||
if (port_type != TYPEC_PORT_DRP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Handle vendor drp toggling */
|
||||
if (tcpci->data->start_drp_toggling) {
|
||||
ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (cc) {
|
||||
default:
|
||||
case TYPEC_CC_RP_DEF:
|
||||
reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RP_1_5:
|
||||
reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
case TYPEC_CC_RP_3_0:
|
||||
reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
|
||||
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cc == TYPEC_CC_RD)
|
||||
reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
else
|
||||
reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
|
||||
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_ROLE_CTRL, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_LOOK4CONNECTION);
|
||||
}
|
||||
|
||||
static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
|
||||
{
|
||||
switch (cc) {
|
||||
case 0x1:
|
||||
return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA;
|
||||
case 0x2:
|
||||
return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD;
|
||||
case 0x3:
|
||||
if (sink)
|
||||
return TYPEC_CC_RP_3_0;
|
||||
/* fall through */
|
||||
case 0x0:
|
||||
default:
|
||||
return TYPEC_CC_OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
static int tcpci_get_cc(struct tcpc_dev *tcpc,
|
||||
enum typec_cc_status *cc1, enum typec_cc_status *cc2)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg, role_control;
|
||||
|
||||
role_control = dm_i2c_reg_read(tcpci->dev, TCPC_ROLE_CTRL);
|
||||
if (role_control < 0)
|
||||
return role_control;
|
||||
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_CC_STATUS);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
*cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
|
||||
TCPC_CC_STATUS_CC1_MASK,
|
||||
reg & TCPC_CC_STATUS_TERM ||
|
||||
tcpc_presenting_cc1_rd(role_control));
|
||||
*cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
|
||||
TCPC_CC_STATUS_CC2_MASK,
|
||||
reg & TCPC_CC_STATUS_TERM ||
|
||||
tcpc_presenting_cc2_rd(role_control));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_set_polarity(struct tcpc_dev *tcpc,
|
||||
enum typec_cc_polarity polarity)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
enum typec_cc_status cc1, cc2;
|
||||
|
||||
/* Obtain Rp setting from role control */
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_ROLE_CTRL);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
ret = tcpci_get_cc(tcpc, &cc1, &cc2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* When port has drp toggling enabled, ROLE_CONTROL would only have the initial
|
||||
* terminations for the toggling and does not indicate the final cc
|
||||
* terminations when ConnectionResult is 0 i.e. drp toggling stops and
|
||||
* the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the
|
||||
* terminations seen. The port role is then used to set the cc terminations.
|
||||
*/
|
||||
if (reg & TCPC_ROLE_CTRL_DRP) {
|
||||
/* Disable DRP for the OPEN setting to take effect */
|
||||
reg = reg & ~TCPC_ROLE_CTRL_DRP;
|
||||
|
||||
if (polarity == TYPEC_POLARITY_CC2) {
|
||||
reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
|
||||
/* Local port is source */
|
||||
if (cc2 == TYPEC_CC_RD)
|
||||
/* Role control would have the Rp setting when DRP was enabled */
|
||||
reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
|
||||
else
|
||||
reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
|
||||
} else {
|
||||
reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
|
||||
/* Local port is source */
|
||||
if (cc1 == TYPEC_CC_RD)
|
||||
/* Role control would have the Rp setting when DRP was enabled */
|
||||
reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
|
||||
else
|
||||
reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (polarity == TYPEC_POLARITY_CC2)
|
||||
reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
|
||||
else
|
||||
reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT;
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_ROLE_CTRL, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return dm_i2c_reg_write(tcpci->dev, TCPC_TCPC_CTRL,
|
||||
(polarity == TYPEC_POLARITY_CC2) ?
|
||||
TCPC_TCPC_CTRL_ORIENTATION : 0);
|
||||
}
|
||||
|
||||
static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
/* Handle vendor set vconn */
|
||||
if (tcpci->data->set_vconn) {
|
||||
ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_POWER_CTRL);
|
||||
if (reg)
|
||||
return reg;
|
||||
reg &= ~TCPC_POWER_CTRL_VCONN_ENABLE;
|
||||
reg |= enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0;
|
||||
return dm_i2c_reg_write(tcpci->dev, TCPC_POWER_CTRL, reg);
|
||||
}
|
||||
|
||||
static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
|
||||
enum typec_role role, enum typec_data_role data)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
|
||||
if (role == TYPEC_SOURCE)
|
||||
reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
|
||||
if (data == TYPEC_HOST)
|
||||
reg |= TCPC_MSG_HDR_INFO_DATA_ROLE;
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_MSG_HDR_INFO, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg = 0;
|
||||
int ret;
|
||||
|
||||
if (enable)
|
||||
reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET;
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_RX_DETECT, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_get_vbus(struct tcpc_dev *tcpc)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg;
|
||||
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_POWER_STATUS);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
|
||||
}
|
||||
|
||||
static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
int ret;
|
||||
|
||||
/* Disable both source and sink first before enabling anything */
|
||||
|
||||
if (!source) {
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_DISABLE_SRC_VBUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!sink) {
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_DISABLE_SINK_VBUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (source) {
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_SRC_VBUS_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sink) {
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_SINK_VBUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
|
||||
enum tcpm_transmit_type type,
|
||||
const struct pd_message *msg,
|
||||
unsigned int negotiated_rev)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
u16 header = msg ? le16_to_cpu(msg->header) : 0;
|
||||
unsigned int reg, cnt;
|
||||
int ret;
|
||||
|
||||
cnt = msg ? pd_header_cnt(header) * 4 : 0;
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_TX_BYTE_CNT, cnt + 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (cnt > 0) {
|
||||
ret = tcpci_block_write(tcpci, TCPC_TX_DATA,
|
||||
(u8 *)&msg->payload, cnt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
|
||||
(type << TCPC_TRANSMIT_TYPE_SHIFT);
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_TRANSMIT, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_init(struct tcpc_dev *tcpc)
|
||||
{
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int timeout = 0; /* XXX */
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
while (timeout < 100) {
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_POWER_STATUS);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
if (!(reg & TCPC_POWER_STATUS_UNINIT))
|
||||
break;
|
||||
timeout++;
|
||||
udelay(200);
|
||||
}
|
||||
if (timeout >= 100)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Handle vendor init */
|
||||
if (tcpci->data->init) {
|
||||
ret = tcpci->data->init(tcpci, tcpci->data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear all events */
|
||||
ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (tcpci->controls_vbus)
|
||||
reg = TCPC_POWER_STATUS_VBUS_PRES;
|
||||
else
|
||||
reg = 0;
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_POWER_STATUS_MASK, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable Vbus detection */
|
||||
ret = dm_i2c_reg_write(tcpci->dev, TCPC_COMMAND,
|
||||
TCPC_CMD_ENABLE_VBUS_DETECT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
|
||||
TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
|
||||
TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS;
|
||||
if (tcpci->controls_vbus)
|
||||
reg |= TCPC_ALERT_POWER_STATUS;
|
||||
return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
|
||||
}
|
||||
|
||||
static void tcpci_poll_event(struct tcpc_dev *tcpc)
|
||||
{
|
||||
u16 status;
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
|
||||
tcpci_read16(tcpci, TCPC_ALERT, &status);
|
||||
|
||||
/*
|
||||
* Clear alert status for everything except RX_STATUS, which shouldn't
|
||||
* be cleared until we have successfully retrieved message.
|
||||
*/
|
||||
if (status & ~TCPC_ALERT_RX_STATUS)
|
||||
tcpci_write16(tcpci, TCPC_ALERT,
|
||||
status & ~TCPC_ALERT_RX_STATUS);
|
||||
|
||||
if (status & TCPC_ALERT_CC_STATUS)
|
||||
tcpm_cc_change(tcpci->port);
|
||||
|
||||
if (status & TCPC_ALERT_POWER_STATUS) {
|
||||
unsigned int reg;
|
||||
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_POWER_STATUS_MASK);
|
||||
if (reg < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If power status mask has been reset, then the TCPC
|
||||
* has reset.
|
||||
*/
|
||||
if (reg == 0xff)
|
||||
tcpm_tcpc_reset(tcpci->port);
|
||||
else
|
||||
tcpm_vbus_change(tcpci->port);
|
||||
}
|
||||
|
||||
if (status & TCPC_ALERT_RX_STATUS) {
|
||||
struct pd_message msg;
|
||||
unsigned int cnt, payload_cnt;
|
||||
u16 header;
|
||||
|
||||
cnt = dm_i2c_reg_read(tcpci->dev, TCPC_RX_BYTE_CNT);
|
||||
if (cnt < 0)
|
||||
return;
|
||||
/*
|
||||
* 'cnt' corresponds to READABLE_BYTE_COUNT in section 4.4.14
|
||||
* of the TCPCI spec [Rev 2.0 Ver 1.0 October 2017] and is
|
||||
* defined in table 4-36 as one greater than the number of
|
||||
* bytes received. And that number includes the header. So:
|
||||
*/
|
||||
if (cnt > 3)
|
||||
payload_cnt = cnt - (1 + sizeof(msg.header));
|
||||
else
|
||||
payload_cnt = 0;
|
||||
|
||||
tcpci_read16(tcpci, TCPC_RX_HDR, &header);
|
||||
msg.header = cpu_to_le16(header);
|
||||
|
||||
if ((payload_cnt > sizeof(msg.payload)))
|
||||
payload_cnt = sizeof(msg.payload);
|
||||
|
||||
if (payload_cnt > 0)
|
||||
tcpci_block_read(tcpci, TCPC_RX_DATA,
|
||||
(u8 *)&msg.payload, payload_cnt);
|
||||
|
||||
/* Read complete, clear RX status alert bit */
|
||||
tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
|
||||
|
||||
tcpm_pd_receive(tcpci->port, &msg);
|
||||
}
|
||||
|
||||
if (status & TCPC_ALERT_RX_HARD_RST)
|
||||
tcpm_pd_hard_reset(tcpci->port);
|
||||
|
||||
if (status & TCPC_ALERT_TX_SUCCESS)
|
||||
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS);
|
||||
else if (status & TCPC_ALERT_TX_DISCARDED)
|
||||
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED);
|
||||
else if (status & TCPC_ALERT_TX_FAILED)
|
||||
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
|
||||
}
|
||||
|
||||
static int tcpci_enter_low_power_mode(struct tcpc_dev *tcpc,
|
||||
bool attached, bool pd_capable)
|
||||
{
|
||||
int ret;
|
||||
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
|
||||
unsigned int reg;
|
||||
|
||||
/* Disable chip interrupts before unregistering port */
|
||||
ret = tcpci_write16(tcpci, TCPC_ALERT_MASK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg = dm_i2c_reg_read(tcpci->dev, TCPC_BMCIO_CTRL);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
/*
|
||||
* For Type-C devices with PD capability, Only disable VBUS detect,
|
||||
* do not diable 24M oscillator for BMC communication. Otherwise,
|
||||
* data packets cannot be received.
|
||||
*/
|
||||
if (attached && pd_capable)
|
||||
reg &= ~TCPC_BMCIO_VBUS_DETECT_MASK;
|
||||
else
|
||||
reg &= ~(TCPC_BMCIO_VBUS_DETECT_MASK | TCPC_BMCIO_24M_OSC_MASK);
|
||||
return dm_i2c_reg_write(tcpci->dev, TCPC_BMCIO_CTRL, reg);
|
||||
}
|
||||
|
||||
static int tcpci_parse_config(struct tcpci *tcpci)
|
||||
{
|
||||
tcpci->controls_vbus = true; /* XXX */
|
||||
|
||||
tcpci->tcpc.connector_node = dev_read_subnode(tcpci->dev, "connector");
|
||||
if (!ofnode_valid(tcpci->tcpc.connector_node)) {
|
||||
printf("%s: 'connector' node is not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tcpci *tcpci_register_port(struct udevice *dev, struct tcpci_data *data)
|
||||
{
|
||||
struct tcpci *tcpci;
|
||||
int err;
|
||||
|
||||
tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL);
|
||||
if (!tcpci)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = gpio_request_by_name(dev, "int-n-gpios", 0, &tcpci->gpio_cc_int, GPIOD_IS_IN);
|
||||
if (err) {
|
||||
printf("%s: fail to get int GPIO: err=%d\n", __func__, err);
|
||||
tcpci->gpio_cc_int_present = false;
|
||||
} else {
|
||||
printf("%s: success to get int GPIO: err=%d\n", __func__, err);
|
||||
tcpci->gpio_cc_int_present = true;
|
||||
}
|
||||
|
||||
tcpci->dev = dev;
|
||||
tcpci->data = data;
|
||||
|
||||
tcpci->tcpc.init = tcpci_init;
|
||||
tcpci->tcpc.get_vbus = tcpci_get_vbus;
|
||||
tcpci->tcpc.set_vbus = tcpci_set_vbus;
|
||||
tcpci->tcpc.set_cc = tcpci_set_cc;
|
||||
tcpci->tcpc.get_cc = tcpci_get_cc;
|
||||
tcpci->tcpc.set_polarity = tcpci_set_polarity;
|
||||
tcpci->tcpc.set_vconn = tcpci_set_vconn;
|
||||
tcpci->tcpc.start_toggling = tcpci_start_toggling;
|
||||
|
||||
tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
|
||||
tcpci->tcpc.set_roles = tcpci_set_roles;
|
||||
tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
|
||||
tcpci->tcpc.poll_event = tcpci_poll_event;
|
||||
tcpci->tcpc.enter_low_power_mode = tcpci_enter_low_power_mode;
|
||||
|
||||
err = tcpci_parse_config(tcpci);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
tcpci->port = tcpm_port_init(tcpci->dev, &tcpci->tcpc);
|
||||
if (IS_ERR(tcpci->port)) {
|
||||
printf("%s: failed to tcpm port init\n", __func__);
|
||||
return ERR_CAST(tcpci->port);
|
||||
}
|
||||
|
||||
// tcpm_tcpc_reset(tcpci->port);
|
||||
|
||||
tcpm_poll_event(tcpci->port);
|
||||
|
||||
return tcpci;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcpci_register_port);
|
||||
|
||||
void tcpci_unregister_port(struct tcpci *tcpci)
|
||||
{
|
||||
tcpm_uninit_port(tcpci->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcpci_unregister_port);
|
||||
|
||||
int tcpci_get_voltage_fun(struct tcpci *tcpci)
|
||||
{
|
||||
return tcpm_get_voltage(tcpci->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcpci_get_voltage_fun);
|
||||
|
||||
int tcpci_get_current_fun(struct tcpci *tcpci)
|
||||
{
|
||||
return tcpm_get_current(tcpci->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcpci_get_current_fun);
|
||||
|
||||
int tcpci_get_online_fun(struct tcpci *tcpci)
|
||||
{
|
||||
return tcpm_get_online(tcpci->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tcpci_get_online_fun);
|
||||
|
||||
static int tcpci_probe(struct udevice *dev)
|
||||
{
|
||||
struct tcpci_chip *chip = dev_get_priv(dev);
|
||||
int err;
|
||||
u16 val = 0;
|
||||
|
||||
chip->udev = dev;
|
||||
|
||||
/* Disable chip interrupts before requesting irq */
|
||||
err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
chip->tcpci = tcpci_register_port(chip->udev, &chip->data);
|
||||
if (IS_ERR(chip->tcpci))
|
||||
return PTR_ERR(chip->tcpci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_remove(struct udevice *dev)
|
||||
{
|
||||
struct tcpci_chip *chip = dev_get_priv(dev);
|
||||
int err;
|
||||
|
||||
/* Disable chip interrupts before unregistering port */
|
||||
err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
tcpci_unregister_port(chip->tcpci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcpci_get_voltage(struct udevice *dev)
|
||||
{
|
||||
struct tcpci_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpm_get_voltage(chip->tcpci->port);
|
||||
}
|
||||
|
||||
static int tcpci_get_current(struct udevice *dev)
|
||||
{
|
||||
struct tcpci_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpm_get_current(chip->tcpci->port);
|
||||
}
|
||||
|
||||
static int tcpci_get_online(struct udevice *dev)
|
||||
{
|
||||
struct tcpci_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpm_get_online(chip->tcpci->port);
|
||||
}
|
||||
|
||||
static struct dm_power_delivery_ops tcpci_ops = {
|
||||
.get_voltage = tcpci_get_voltage,
|
||||
.get_current = tcpci_get_current,
|
||||
.get_online = tcpci_get_online,
|
||||
};
|
||||
|
||||
static const struct udevice_id tcpci_ids[] = {
|
||||
{ .compatible = "nxp,ptn5110", },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tcpci) = {
|
||||
.name = "tcpci",
|
||||
.id = UCLASS_PD,
|
||||
.of_match = tcpci_ids,
|
||||
.ops = &tcpci_ops,
|
||||
.probe = tcpci_probe,
|
||||
.remove = tcpci_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct tcpci_chip),
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
150
drivers/power/power_delivery/tcpci.h
Executable file
150
drivers/power/power_delivery/tcpci.h
Executable file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2015-2017 Google, Inc
|
||||
*
|
||||
* USB Type-C Port Controller Interface.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_TCPCI_H
|
||||
#define __LINUX_USB_TCPCI_H
|
||||
|
||||
#define TCPC_VENDOR_ID 0x0
|
||||
#define TCPC_PRODUCT_ID 0x2
|
||||
#define TCPC_BCD_DEV 0x4
|
||||
#define TCPC_TC_REV 0x6
|
||||
#define TCPC_PD_REV 0x8
|
||||
#define TCPC_PD_INT_REV 0xa
|
||||
|
||||
#define TCPC_ALERT 0x10
|
||||
#define TCPC_ALERT_VBUS_DISCNCT BIT(11)
|
||||
#define TCPC_ALERT_RX_BUF_OVF BIT(10)
|
||||
#define TCPC_ALERT_FAULT BIT(9)
|
||||
#define TCPC_ALERT_V_ALARM_LO BIT(8)
|
||||
#define TCPC_ALERT_V_ALARM_HI BIT(7)
|
||||
#define TCPC_ALERT_TX_SUCCESS BIT(6)
|
||||
#define TCPC_ALERT_TX_DISCARDED BIT(5)
|
||||
#define TCPC_ALERT_TX_FAILED BIT(4)
|
||||
#define TCPC_ALERT_RX_HARD_RST BIT(3)
|
||||
#define TCPC_ALERT_RX_STATUS BIT(2)
|
||||
#define TCPC_ALERT_POWER_STATUS BIT(1)
|
||||
#define TCPC_ALERT_CC_STATUS BIT(0)
|
||||
|
||||
#define TCPC_ALERT_MASK 0x12
|
||||
#define TCPC_POWER_STATUS_MASK 0x14
|
||||
#define TCPC_FAULT_STATUS_MASK 0x15
|
||||
#define TCPC_CONFIG_STD_OUTPUT 0x18
|
||||
|
||||
#define TCPC_TCPC_CTRL 0x19
|
||||
#define TCPC_TCPC_CTRL_ORIENTATION BIT(0)
|
||||
|
||||
#define TCPC_ROLE_CTRL 0x1a
|
||||
#define TCPC_ROLE_CTRL_DRP BIT(6)
|
||||
#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4
|
||||
#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3
|
||||
#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0
|
||||
#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1
|
||||
#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2
|
||||
#define TCPC_ROLE_CTRL_CC2_SHIFT 2
|
||||
#define TCPC_ROLE_CTRL_CC2_MASK 0x3
|
||||
#define TCPC_ROLE_CTRL_CC1_SHIFT 0
|
||||
#define TCPC_ROLE_CTRL_CC1_MASK 0x3
|
||||
#define TCPC_ROLE_CTRL_CC_RA 0x0
|
||||
#define TCPC_ROLE_CTRL_CC_RP 0x1
|
||||
#define TCPC_ROLE_CTRL_CC_RD 0x2
|
||||
#define TCPC_ROLE_CTRL_CC_OPEN 0x3
|
||||
|
||||
#define TCPC_FAULT_CTRL 0x1b
|
||||
|
||||
#define TCPC_POWER_CTRL 0x1c
|
||||
#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0)
|
||||
|
||||
#define TCPC_CC_STATUS 0x1d
|
||||
#define TCPC_CC_STATUS_TOGGLING BIT(5)
|
||||
#define TCPC_CC_STATUS_TERM BIT(4)
|
||||
#define TCPC_CC_STATUS_CC2_SHIFT 2
|
||||
#define TCPC_CC_STATUS_CC2_MASK 0x3
|
||||
#define TCPC_CC_STATUS_CC1_SHIFT 0
|
||||
#define TCPC_CC_STATUS_CC1_MASK 0x3
|
||||
|
||||
#define TCPC_POWER_STATUS 0x1e
|
||||
#define TCPC_POWER_STATUS_UNINIT BIT(6)
|
||||
#define TCPC_POWER_STATUS_VBUS_DET BIT(3)
|
||||
#define TCPC_POWER_STATUS_VBUS_PRES BIT(2)
|
||||
|
||||
#define TCPC_FAULT_STATUS 0x1f
|
||||
|
||||
#define TCPC_COMMAND 0x23
|
||||
#define TCPC_CMD_WAKE_I2C 0x11
|
||||
#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22
|
||||
#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33
|
||||
#define TCPC_CMD_DISABLE_SINK_VBUS 0x44
|
||||
#define TCPC_CMD_SINK_VBUS 0x55
|
||||
#define TCPC_CMD_DISABLE_SRC_VBUS 0x66
|
||||
#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77
|
||||
#define TCPC_CMD_SRC_VBUS_HIGH 0x88
|
||||
#define TCPC_CMD_LOOK4CONNECTION 0x99
|
||||
#define TCPC_CMD_RXONEMORE 0xAA
|
||||
#define TCPC_CMD_I2C_IDLE 0xFF
|
||||
|
||||
#define TCPC_DEV_CAP_1 0x24
|
||||
#define TCPC_DEV_CAP_2 0x26
|
||||
#define TCPC_STD_INPUT_CAP 0x28
|
||||
#define TCPC_STD_OUTPUT_CAP 0x29
|
||||
|
||||
#define TCPC_MSG_HDR_INFO 0x2e
|
||||
#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3)
|
||||
#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0)
|
||||
#define TCPC_MSG_HDR_INFO_REV_SHIFT 1
|
||||
#define TCPC_MSG_HDR_INFO_REV_MASK 0x3
|
||||
|
||||
#define TCPC_RX_DETECT 0x2f
|
||||
#define TCPC_RX_DETECT_HARD_RESET BIT(5)
|
||||
#define TCPC_RX_DETECT_SOP BIT(0)
|
||||
|
||||
#define TCPC_RX_BYTE_CNT 0x30
|
||||
#define TCPC_RX_BUF_FRAME_TYPE 0x31
|
||||
#define TCPC_RX_HDR 0x32
|
||||
#define TCPC_RX_DATA 0x34 /* through 0x4f */
|
||||
|
||||
#define TCPC_TRANSMIT 0x50
|
||||
#define TCPC_TRANSMIT_RETRY_SHIFT 4
|
||||
#define TCPC_TRANSMIT_RETRY_MASK 0x3
|
||||
#define TCPC_TRANSMIT_TYPE_SHIFT 0
|
||||
#define TCPC_TRANSMIT_TYPE_MASK 0x7
|
||||
|
||||
#define TCPC_TX_BYTE_CNT 0x51
|
||||
#define TCPC_TX_HDR 0x52
|
||||
#define TCPC_TX_DATA 0x54 /* through 0x6f */
|
||||
|
||||
#define TCPC_VBUS_VOLTAGE 0x70
|
||||
#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72
|
||||
#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74
|
||||
#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76
|
||||
#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78
|
||||
|
||||
#define TCPC_BMCIO_CTRL 0x90
|
||||
#define TCPC_BMCIO_VBUS_DETECT_MASK BIT(1)
|
||||
#define TCPC_BMCIO_VBUS_DETECT_ENABLE BIT(1)
|
||||
#define TCPC_BMCIO_VBUS_DETECT_DISABLE 0
|
||||
#define TCPC_BMCIO_24M_OSC_MASK BIT(0)
|
||||
#define TCPC_BMCIO_ENABLE_24M_OSC BIT(0)
|
||||
#define TCPC_BMCIO_DISABLE_24M_OSC 0
|
||||
|
||||
struct tcpci;
|
||||
struct tcpci_data {
|
||||
struct regmap *regmap;
|
||||
int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
|
||||
int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
|
||||
bool enable);
|
||||
int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
|
||||
enum typec_cc_status cc);
|
||||
};
|
||||
|
||||
struct tcpci *tcpci_register_port(struct udevice *dev, struct tcpci_data *data);
|
||||
void tcpci_unregister_port(struct tcpci *tcpci);
|
||||
int tcpci_get_voltage_fun(struct tcpci *tcpci);
|
||||
int tcpci_get_current_fun(struct tcpci *tcpci);
|
||||
int tcpci_get_online_fun(struct tcpci *tcpci);
|
||||
irqreturn_t tcpci_irq(struct tcpci *tcpci);
|
||||
|
||||
#endif /* __LINUX_USB_TCPCI_H */
|
||||
229
drivers/power/power_delivery/tcpci_husb311.c
Executable file
229
drivers/power/power_delivery/tcpci_husb311.c
Executable file
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2021 Rockchip Co.,Ltd.
|
||||
* Author: Wang Jie <dave.wang@rock-chips.com>
|
||||
*
|
||||
* Hynetek Husb311 Type-C Chip Driver
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <power/power_delivery/tcpm.h>
|
||||
#include <power/power_delivery/power_delivery.h>
|
||||
#include "tcpci.h"
|
||||
|
||||
#define HUSB311_VID 0x2E99
|
||||
#define HUSB311_PID 0x0311
|
||||
#define HUSB311_TCPC_I2C_RESET 0x9E
|
||||
#define HUSB311_TCPC_SOFTRESET 0xA0
|
||||
#define HUSB311_TCPC_FILTER 0xA1
|
||||
#define HUSB311_TCPC_TDRP 0xA2
|
||||
#define HUSB311_TCPC_DCSRCDRP 0xA3
|
||||
#define HUSB311_I2C_RETRY_MAX_CNT 3
|
||||
|
||||
struct husb311_chip {
|
||||
struct udevice *udev;
|
||||
struct tcpci_data data;
|
||||
struct tcpci *tcpci;
|
||||
};
|
||||
|
||||
static int husb311_read16(struct husb311_chip *chip, unsigned int reg)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 buffer[2];
|
||||
|
||||
ret = dm_i2c_read(chip->udev, reg, buffer, 2);
|
||||
if (ret < 0) {
|
||||
printf("%s: cannot read %02x, ret=%d\n",
|
||||
__func__, reg, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ((buffer[1] << 8) & 0xFF00) + (buffer[0] & 0xFF);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int husb311_write8(struct husb311_chip *chip, unsigned int reg, u8 val)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HUSB311_I2C_RETRY_MAX_CNT; i++) {
|
||||
ret = dm_i2c_write(chip->udev, reg, &val, 1);
|
||||
if (!ret)
|
||||
break;
|
||||
else
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
printf("%s: cannot write 0x%02x to 0x%02x, ret=%d\n",
|
||||
__func__, val, reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int husb311_write16(struct husb311_chip *chip, unsigned int reg, u16 val)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 buffer[2];
|
||||
|
||||
buffer[0] = val & 0xFF;
|
||||
buffer[1] = (val >> 8) & 0xFF;
|
||||
ret = dm_i2c_write(chip->udev, reg, buffer, 2);
|
||||
if (ret)
|
||||
printf("%s: cannot write 0x%02x, len=%d, ret=%d\n",
|
||||
__func__, reg, 2, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct husb311_chip *tdata_to_husb311(struct tcpci_data *tdata)
|
||||
{
|
||||
return container_of(tdata, struct husb311_chip, data);
|
||||
}
|
||||
|
||||
static int husb311_sw_reset(struct husb311_chip *chip)
|
||||
{
|
||||
/* soft reset */
|
||||
return husb311_write8(chip, HUSB311_TCPC_SOFTRESET, 0x01);
|
||||
}
|
||||
|
||||
static int husb311_init(struct tcpci *tcpci, struct tcpci_data *tdata)
|
||||
{
|
||||
int ret;
|
||||
struct husb311_chip *chip = tdata_to_husb311(tdata);
|
||||
|
||||
/* I2C reset : (val + 1) * 12.5ms */
|
||||
ret = husb311_write8(chip, HUSB311_TCPC_I2C_RESET, 0x8F);
|
||||
/* tTCPCfilter : (26.7 * val) us */
|
||||
ret |= husb311_write8(chip, HUSB311_TCPC_FILTER, 0x0F);
|
||||
/* tDRP : (51.2 + 6.4 * val) ms */
|
||||
ret |= husb311_write8(chip, HUSB311_TCPC_TDRP, 0x04);
|
||||
/* dcSRC.DRP : 33% */
|
||||
ret |= husb311_write16(chip, HUSB311_TCPC_DCSRCDRP, 330);
|
||||
|
||||
if (ret)
|
||||
printf("%s: fail to init registers(%d)\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int husb311_check_revision(struct husb311_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = husb311_read16(chip, TCPC_VENDOR_ID);
|
||||
if (ret < 0) {
|
||||
printf("%s: fail to read Vendor id(%d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != HUSB311_VID) {
|
||||
printf("%s: vid is not correct, 0x%04x\n", __func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = husb311_read16(chip, TCPC_PRODUCT_ID);
|
||||
if (ret < 0) {
|
||||
printf("%s: fail to read Product id(%d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != HUSB311_PID) {
|
||||
printf("%s: pid is not correct, 0x%04x\n", __func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int husb311_probe(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct husb311_chip *chip = dev_get_priv(dev);
|
||||
|
||||
chip->udev = dev;
|
||||
|
||||
ret = husb311_check_revision(chip);
|
||||
if (ret < 0) {
|
||||
printf("%s: check vid/pid fail(%d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = husb311_sw_reset(chip);
|
||||
if (ret) {
|
||||
printf("%s: fail to soft reset, ret = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->data.init = husb311_init;
|
||||
chip->tcpci = tcpci_register_port(chip->udev, &chip->data);
|
||||
if (IS_ERR(chip->tcpci))
|
||||
return PTR_ERR(chip->tcpci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int husb311_remove(struct udevice *dev)
|
||||
{
|
||||
struct husb311_chip *chip = dev_get_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
printf("PD chip husb311 remove\n");
|
||||
/* Disable chip interrupts before unregistering port */
|
||||
ret = husb311_write16(chip, TCPC_ALERT_MASK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tcpci_unregister_port(chip->tcpci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int husb311_get_voltage(struct udevice *dev)
|
||||
{
|
||||
struct husb311_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpci_get_voltage_fun(chip->tcpci);
|
||||
}
|
||||
|
||||
static int husb311_get_current(struct udevice *dev)
|
||||
{
|
||||
struct husb311_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpci_get_current_fun(chip->tcpci);
|
||||
}
|
||||
|
||||
static int husb311_get_online(struct udevice *dev)
|
||||
{
|
||||
struct husb311_chip *chip = dev_get_priv(dev);
|
||||
|
||||
return tcpci_get_online_fun(chip->tcpci);
|
||||
}
|
||||
|
||||
static struct dm_power_delivery_ops husb311_ops = {
|
||||
.get_voltage = husb311_get_voltage,
|
||||
.get_current = husb311_get_current,
|
||||
.get_online = husb311_get_online,
|
||||
};
|
||||
|
||||
static const struct udevice_id husb311_ids[] = {
|
||||
{ .compatible = "hynetek,husb311" },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(husb311) = {
|
||||
.name = "husb311",
|
||||
.id = UCLASS_PD,
|
||||
.of_match = husb311_ids,
|
||||
.ops = &husb311_ops,
|
||||
.probe = husb311_probe,
|
||||
.remove = husb311_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct husb311_chip),
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Wang Jie <dave.wang@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Husb311 USB Type-C Port Controller Interface Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
3520
drivers/power/power_delivery/tcpm.c
Executable file
3520
drivers/power/power_delivery/tcpm.c
Executable file
File diff suppressed because it is too large
Load Diff
@@ -145,6 +145,18 @@ config TPM2_TIS_SPI
|
||||
to the device using the standard TPM Interface Specification (TIS)
|
||||
protocol.
|
||||
|
||||
config TPM_Z32H330TC_SPI
|
||||
bool "STMicroelectronics Z32H330TC SPI TPM"
|
||||
depends on TPM_V2 && DM_SPI
|
||||
---help---
|
||||
This driver supports STMicroelectronics TPM devices connected on the SPI bus.
|
||||
The usual tpm operations and the 'tpm' command can be used to talk
|
||||
to the device using the standard TPM Interface Specification (TIS)
|
||||
protocol
|
||||
|
||||
config CMD_MEASURED_BOOT
|
||||
bool "Enable support for measured_boot to bootcmd"
|
||||
|
||||
endif # TPM_V2
|
||||
|
||||
endmenu
|
||||
|
||||
Reference in New Issue
Block a user