997 lines
29 KiB
C
997 lines
29 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* MFD core driver for Rockchip RK806
|
|
*
|
|
* Copyright (c) 2021 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* Author: Xu Shengfei <xsf@rock-chips.com>
|
|
*/
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/rk806.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/spi/spi.h>
|
|
|
|
#define TSD_TEMP_140 0x00
|
|
#define TSD_TEMP_160 0x01
|
|
#define VB_LO_ACT_SD 0x00
|
|
#define VB_LO_ACT_INT 0x01
|
|
#define VB_LO_3400MV 3400
|
|
|
|
static const struct reg_field rk806_reg_fields[] = {
|
|
[POWER_EN0] = REG_FIELD(0x00, 0, 7),
|
|
[POWER_EN1] = REG_FIELD(0x01, 0, 7),
|
|
[POWER_EN2] = REG_FIELD(0x02, 0, 7),
|
|
[POWER_EN3] = REG_FIELD(0x03, 0, 7),
|
|
[POWER_EN4] = REG_FIELD(0x04, 0, 7),
|
|
[POWER_EN5] = REG_FIELD(0x05, 0, 7),
|
|
|
|
[BUCK4_EN_MASK] = REG_FIELD(0x00, 7, 7),
|
|
[BUCK3_EN_MASK] = REG_FIELD(0x00, 6, 6),
|
|
[BUCK2_EN_MASK] = REG_FIELD(0x00, 5, 5),
|
|
[BUCK1_EN_MASK] = REG_FIELD(0x00, 4, 4),
|
|
[BUCK4_EN] = REG_FIELD(0X00, 3, 3),
|
|
[BUCK3_EN] = REG_FIELD(0X00, 2, 2),
|
|
[BUCK2_EN] = REG_FIELD(0X00, 1, 1),
|
|
[BUCK1_EN] = REG_FIELD(0X00, 0, 0),
|
|
[BUCK8_EN_MASK] = REG_FIELD(0x01, 7, 7),
|
|
[BUCK7_EN_MASK] = REG_FIELD(0x01, 6, 6),
|
|
[BUCK6_EN_MASK] = REG_FIELD(0x01, 5, 5),
|
|
[BUCK5_EN_MASK] = REG_FIELD(0x01, 4, 4),
|
|
[BUCK8_EN] = REG_FIELD(0x01, 3, 3),
|
|
[BUCK7_EN] = REG_FIELD(0x01, 2, 2),
|
|
[BUCK6_EN] = REG_FIELD(0x01, 1, 1),
|
|
[BUCK5_EN] = REG_FIELD(0x01, 0, 0),
|
|
[BUCK10_EN_MASK] = REG_FIELD(0x02, 5, 5),
|
|
[BUCK9_EN_MASK] = REG_FIELD(0x02, 4, 4),
|
|
[BUCK10_EN] = REG_FIELD(0x02, 1, 1),
|
|
[BUCK9_EN] = REG_FIELD(0x02, 0, 0),
|
|
[NLDO4_EN_MASK] = REG_FIELD(0x03, 7, 7),
|
|
[NLDO3_EN_MASK] = REG_FIELD(0x03, 6, 6),
|
|
[NLDO2_EN_MASK] = REG_FIELD(0x03, 5, 5),
|
|
[NLDO1_EN_MASK] = REG_FIELD(0x03, 4, 4),
|
|
[NLDO4_EN] = REG_FIELD(0x03, 3, 3),
|
|
[NLDO3_EN] = REG_FIELD(0x03, 2, 2),
|
|
[NLDO2_EN] = REG_FIELD(0x03, 1, 1),
|
|
[NLDO1_EN] = REG_FIELD(0x03, 0, 0),
|
|
|
|
[PLDO3_EN_MASK] = REG_FIELD(0x04, 7, 7),
|
|
[PLDO2_EN_MASK] = REG_FIELD(0x04, 6, 6),
|
|
[PLDO1_EN_MASK] = REG_FIELD(0x04, 5, 5),
|
|
[PLDO6_EN_MASK] = REG_FIELD(0x04, 4, 4),
|
|
[PLDO3_EN] = REG_FIELD(0x04, 3, 3),
|
|
[PLDO2_EN] = REG_FIELD(0x04, 2, 2),
|
|
[PLDO1_EN] = REG_FIELD(0x04, 1, 1),
|
|
[PLDO6_EN] = REG_FIELD(0x04, 0, 0),
|
|
|
|
[NLDO5_EN_MASK] = REG_FIELD(0x05, 6, 6),
|
|
[PLDO5_EN_MASK] = REG_FIELD(0x05, 5, 5),
|
|
[PLDO4_EN_MASK] = REG_FIELD(0x05, 4, 4),
|
|
[NLDO5_EN] = REG_FIELD(0x05, 2, 2),
|
|
[PLDO5_EN] = REG_FIELD(0x05, 1, 1),
|
|
[PLDO4_EN] = REG_FIELD(0x05, 0, 0),
|
|
|
|
[BUCK8_SLP_EN] = REG_FIELD(0x06, 7, 7),
|
|
[BUCK7_SLP_EN] = REG_FIELD(0x06, 6, 6),
|
|
[BUCK6_SLP_EN] = REG_FIELD(0x06, 5, 5),
|
|
[BUCK5_SLP_EN] = REG_FIELD(0x06, 4, 4),
|
|
[BUCK4_SLP_EN] = REG_FIELD(0x06, 3, 3),
|
|
[BUCK3_SLP_EN] = REG_FIELD(0x06, 2, 2),
|
|
[BUCK2_SLP_EN] = REG_FIELD(0x06, 1, 1),
|
|
[BUCK1_SLP_EN] = REG_FIELD(0x06, 0, 0),
|
|
|
|
[BUCK10_SLP_EN] = REG_FIELD(0x07, 7, 7),
|
|
[BUCK9_SLP_EN] = REG_FIELD(0x07, 6, 6),
|
|
[NLDO5_SLP_EN] = REG_FIELD(0x07, 4, 4),
|
|
[NLDO4_SLP_EN] = REG_FIELD(0x07, 3, 3),
|
|
[NLDO3_SLP_EN] = REG_FIELD(0x07, 2, 2),
|
|
[NLDO2_SLP_EN] = REG_FIELD(0x07, 1, 1),
|
|
[NLDO1_SLP_EN] = REG_FIELD(0x07, 0, 0),
|
|
|
|
[PLDO5_SLP_EN] = REG_FIELD(0x08, 5, 5),
|
|
[PLDO4_SLP_EN] = REG_FIELD(0x08, 4, 4),
|
|
[PLDO3_SLP_EN] = REG_FIELD(0x08, 3, 3),
|
|
[PLDO2_SLP_EN] = REG_FIELD(0x08, 2, 2),
|
|
[PLDO1_SLP_EN] = REG_FIELD(0x08, 1, 1),
|
|
[PLDO6_SLP_EN] = REG_FIELD(0x08, 0, 0),
|
|
|
|
[BUCK1_RATE] = REG_FIELD(0x10, 6, 7),
|
|
[BUCK2_RATE] = REG_FIELD(0x11, 6, 7),
|
|
[BUCK3_RATE] = REG_FIELD(0x12, 6, 7),
|
|
[BUCK4_RATE] = REG_FIELD(0x13, 6, 7),
|
|
[BUCK5_RATE] = REG_FIELD(0x14, 6, 7),
|
|
[BUCK6_RATE] = REG_FIELD(0x15, 6, 7),
|
|
[BUCK7_RATE] = REG_FIELD(0x16, 6, 7),
|
|
[BUCK8_RATE] = REG_FIELD(0x17, 6, 7),
|
|
[BUCK9_RATE] = REG_FIELD(0x18, 6, 7),
|
|
[BUCK10_RATE] = REG_FIELD(0x19, 6, 7),
|
|
|
|
[BUCK1_ON_VSEL] = REG_FIELD(0x1A, 0, 7),
|
|
[BUCK2_ON_VSEL] = REG_FIELD(0x1B, 0, 7),
|
|
[BUCK3_ON_VSEL] = REG_FIELD(0x1C, 0, 7),
|
|
[BUCK4_ON_VSEL] = REG_FIELD(0x1D, 0, 7),
|
|
[BUCK5_ON_VSEL] = REG_FIELD(0x1E, 0, 7),
|
|
[BUCK6_ON_VSEL] = REG_FIELD(0x1F, 0, 7),
|
|
[BUCK7_ON_VSEL] = REG_FIELD(0x20, 0, 7),
|
|
[BUCK8_ON_VSEL] = REG_FIELD(0x21, 0, 7),
|
|
[BUCK9_ON_VSEL] = REG_FIELD(0x22, 0, 7),
|
|
[BUCK10_ON_VSEL] = REG_FIELD(0x23, 0, 7),
|
|
|
|
[BUCK1_SLP_VSEL] = REG_FIELD(0x24, 0, 7),
|
|
[BUCK2_SLP_VSEL] = REG_FIELD(0x25, 0, 7),
|
|
[BUCK3_SLP_VSEL] = REG_FIELD(0x26, 0, 7),
|
|
[BUCK4_SLP_VSEL] = REG_FIELD(0x27, 0, 7),
|
|
[BUCK5_SLP_VSEL] = REG_FIELD(0x28, 0, 7),
|
|
[BUCK6_SLP_VSEL] = REG_FIELD(0x29, 0, 7),
|
|
[BUCK7_SLP_VSEL] = REG_FIELD(0x2A, 0, 7),
|
|
[BUCK8_SLP_VSEL] = REG_FIELD(0x2B, 0, 7),
|
|
[BUCK9_SLP_VSEL] = REG_FIELD(0x2C, 0, 7),
|
|
[BUCK10_SLP_VSEL] = REG_FIELD(0x2D, 0, 7),
|
|
|
|
[NLDO1_ON_VSEL] = REG_FIELD(0x43, 0, 7),
|
|
[NLDO2_ON_VSEL] = REG_FIELD(0x44, 0, 7),
|
|
[NLDO3_ON_VSEL] = REG_FIELD(0x45, 0, 7),
|
|
[NLDO4_ON_VSEL] = REG_FIELD(0x46, 0, 7),
|
|
[NLDO5_ON_VSEL] = REG_FIELD(0x47, 0, 7),
|
|
[NLDO1_SLP_VSEL] = REG_FIELD(0x48, 0, 7),
|
|
[NLDO2_SLP_VSEL] = REG_FIELD(0x49, 0, 7),
|
|
[NLDO3_SLP_VSEL] = REG_FIELD(0x4A, 0, 7),
|
|
[NLDO4_SLP_VSEL] = REG_FIELD(0x4B, 0, 7),
|
|
[NLDO5_SLP_VSEL] = REG_FIELD(0x4C, 0, 7),
|
|
|
|
[PLDO1_ON_VSEL] = REG_FIELD(0x4E, 0, 7),
|
|
[PLDO2_ON_VSEL] = REG_FIELD(0x4F, 0, 7),
|
|
[PLDO3_ON_VSEL] = REG_FIELD(0x50, 0, 7),
|
|
[PLDO4_ON_VSEL] = REG_FIELD(0x51, 0, 7),
|
|
[PLDO5_ON_VSEL] = REG_FIELD(0x52, 0, 7),
|
|
[PLDO6_ON_VSEL] = REG_FIELD(0x53, 0, 7),
|
|
|
|
[PLDO1_SLP_VSEL] = REG_FIELD(0x54, 0, 7),
|
|
[PLDO2_SLP_VSEL] = REG_FIELD(0x55, 0, 7),
|
|
[PLDO3_SLP_VSEL] = REG_FIELD(0x56, 0, 7),
|
|
[PLDO4_SLP_VSEL] = REG_FIELD(0x57, 0, 7),
|
|
[PLDO5_SLP_VSEL] = REG_FIELD(0x58, 0, 7),
|
|
[PLDO6_SLP_VSEL] = REG_FIELD(0x59, 0, 7),
|
|
|
|
[CHIP_NAME_H] = REG_FIELD(0x5A, 0, 7),
|
|
[CHIP_NAME_L] = REG_FIELD(0x5B, 4, 7),
|
|
[CHIP_VER] = REG_FIELD(0x5B, 0, 3),
|
|
[OTP_VER] = REG_FIELD(0x5C, 0, 3),
|
|
/* SYS_STS */
|
|
[PWRON_STS] = REG_FIELD(0x5D, 7, 7),
|
|
[VDC_STS] = REG_FIELD(0x5D, 6, 6),
|
|
[VB_UV_STSS] = REG_FIELD(0x5D, 5, 5),
|
|
[VB_LO_STS] = REG_FIELD(0x5D, 4, 4),
|
|
[HOTDIE_STS] = REG_FIELD(0x5D, 3, 3),
|
|
[TSD_STS] = REG_FIELD(0x5D, 2, 2),
|
|
[VB_OV_STS] = REG_FIELD(0x5D, 0, 0),
|
|
/* SYS_CFG0 */
|
|
[VB_UV_DLY] = REG_FIELD(0x5E, 7, 7),
|
|
[VB_UV_SEL] = REG_FIELD(0x5E, 4, 6),
|
|
[VB_LO_ACT] = REG_FIELD(0x5E, 3, 3),
|
|
[VB_LO_SEL] = REG_FIELD(0x5E, 0, 2),
|
|
/* SYS_CFG1 */
|
|
[ABNORDET_EN] = REG_FIELD(0x5F, 7, 7),
|
|
[TSD_TEMP] = REG_FIELD(0x5F, 6, 6),
|
|
[HOTDIE_TMP] = REG_FIELD(0x5F, 4, 5),
|
|
[SYS_OV_SD_EN] = REG_FIELD(0x5F, 3, 3),
|
|
[SYS_OV_SD_DLY_SEL] = REG_FIELD(0x5F, 2, 2),
|
|
[DLY_ABN_SHORT] = REG_FIELD(0x5F, 0, 1),
|
|
/* SYS_OPTION */
|
|
[VCCXDET_DIS] = REG_FIELD(0x61, 4, 5),
|
|
[OSC_TC] = REG_FIELD(0x61, 2, 3),
|
|
[ENB2_2M] = REG_FIELD(0x61, 1, 1),
|
|
[ENB_32K] = REG_FIELD(0x61, 0, 0),
|
|
/* SLEEP_CONFIG0 */
|
|
[PWRCTRL2_POL] = REG_FIELD(0x62, 7, 7),
|
|
[PWRCTRL2_FUN] = REG_FIELD(0x62, 4, 6),
|
|
[PWRCTRL1_POL] = REG_FIELD(0x62, 3, 3),
|
|
[PWRCTRL1_FUN] = REG_FIELD(0x62, 0, 2),
|
|
/* SLEEP_CONFIG1 */
|
|
[PWRCTRL3_POL] = REG_FIELD(0x63, 3, 3),
|
|
[PWRCTRL3_FUN] = REG_FIELD(0x63, 0, 2),
|
|
/* SLEEP_VSEL_CTR_SEL0 */
|
|
[BUCK4_VSEL_CTR_SEL] = REG_FIELD(0x65, 4, 5),
|
|
[BUCK3_VSEL_CTR_SEL] = REG_FIELD(0x65, 0, 1),
|
|
[BUCK2_VSEL_CTR_SEL] = REG_FIELD(0x64, 4, 5),
|
|
[BUCK1_VSEL_CTR_SEL] = REG_FIELD(0x64, 0, 1),
|
|
/* SLEEP_VSEL_CTR_SEL1 */
|
|
[BUCK8_VSEL_CTR_SEL] = REG_FIELD(0x67, 4, 5),
|
|
[BUCK7_VSEL_CTR_SEL] = REG_FIELD(0x67, 0, 1),
|
|
[BUCK6_VSEL_CTR_SEL] = REG_FIELD(0x66, 4, 5),
|
|
[BUCK5_VSEL_CTR_SEL] = REG_FIELD(0x66, 0, 1),
|
|
/* SLEEP_VSEL_CTR_SEL2 */
|
|
[NLDO2_VSEL_CTR_SEL] = REG_FIELD(0x69, 4, 5),
|
|
[NLDO1_VSEL_CTR_SEL] = REG_FIELD(0x69, 0, 1),
|
|
[BUCK10_VSEL_CTR_SEL] = REG_FIELD(0x68, 4, 5),
|
|
[BUCK9_VSEL_CTR_SEL] = REG_FIELD(0x68, 0, 1),
|
|
/* SLEEP_VSEL_CTR_SEL3 */
|
|
[NLDO5_VSEL_CTR_SEL] = REG_FIELD(0x6b, 0, 1),
|
|
[NLDO4_VSEL_CTR_SEL] = REG_FIELD(0x6a, 4, 5),
|
|
[NLDO3_VSEL_CTR_SEL] = REG_FIELD(0x6a, 0, 1),
|
|
/* SLEEP_VSEL_CTR_SEL4 */
|
|
[PLDO4_VSEL_CTR_SEL] = REG_FIELD(0x6d, 4, 5),
|
|
[PLDO3_VSEL_CTR_SEL] = REG_FIELD(0x6d, 0, 1),
|
|
[PLDO2_VSEL_CTR_SEL] = REG_FIELD(0x6c, 4, 5),
|
|
[PLDO1_VSEL_CTR_SEL] = REG_FIELD(0x6c, 0, 1),
|
|
/* SLEEP_VSEL_CTR_SEL5 */
|
|
[PLDO6_VSEL_CTR_SEL] = REG_FIELD(0x6e, 4, 5),
|
|
[PLDO5_VSEL_CTR_SEL] = REG_FIELD(0x6e, 0, 1),
|
|
/* DVS_CTRL_SEL0 */
|
|
[BUCK4_DVS_CTR_SEL] = REG_FIELD(0x65, 6, 7),
|
|
[BUCK3_DVS_CTR_SEL] = REG_FIELD(0x65, 2, 3),
|
|
[BUCK2_DVS_CTR_SEL] = REG_FIELD(0x64, 6, 7),
|
|
[BUCK1_DVS_CTR_SEL] = REG_FIELD(0x64, 2, 3),
|
|
/* DVS_CTRL_SEL1*/
|
|
[BUCK8_DVS_CTR_SEL] = REG_FIELD(0x67, 6, 7),
|
|
[BUCK7_DVS_CTR_SEL] = REG_FIELD(0x67, 2, 3),
|
|
[BUCK6_DVS_CTR_SEL] = REG_FIELD(0x66, 6, 7),
|
|
[BUCK5_DVS_CTR_SEL] = REG_FIELD(0x66, 2, 3),
|
|
/* DVS_CTRL_SEL2 */
|
|
[NLDO2_DVS_CTR_SEL] = REG_FIELD(0x69, 6, 7),
|
|
[NLDO1_DVS_CTR_SEL] = REG_FIELD(0x69, 2, 3),
|
|
[BUCK10_DVS_CTR_SEL] = REG_FIELD(0x68, 6, 7),
|
|
[BUCK9_DVS_CTR_SEL] = REG_FIELD(0x68, 2, 3),
|
|
/* DVS_CTRL_SEL3 */
|
|
[NLDO5_DVS_CTR_SEL] = REG_FIELD(0x6b, 2, 3),
|
|
[NLDO4_DVS_CTR_SEL] = REG_FIELD(0x6a, 6, 7),
|
|
[NLDO3_DVS_CTR_SEL] = REG_FIELD(0x6a, 2, 3),
|
|
/* DVS_CTRL_SEL4 */
|
|
[PLDO4_DVS_CTR_SEL] = REG_FIELD(0x6d, 6, 7),
|
|
[PLDO3_DVS_CTR_SEL] = REG_FIELD(0x6d, 2, 3),
|
|
[PLDO2_DVS_CTR_SEL] = REG_FIELD(0x6c, 6, 7),
|
|
[PLDO1_DVS_CTR_SEL] = REG_FIELD(0x6c, 2, 3),
|
|
/* DVS_CTRL_SEL5 */
|
|
[PLDO6_DVS_CTR_SEL] = REG_FIELD(0x6e, 6, 7),
|
|
[PLDO5_DVS_CTR_SEL] = REG_FIELD(0x6e, 2, 3),
|
|
/* DVS_START_CTRL */
|
|
[DVS_START3] = REG_FIELD(0x70, 2, 2),
|
|
[DVS_START2] = REG_FIELD(0x70, 1, 1),
|
|
[DVS_START1] = REG_FIELD(0x70, 0, 0),
|
|
/* SLEEP_GPIO */
|
|
[SLP3_DATA] = REG_FIELD(0x71, 6, 6),
|
|
[SLP2_DATA] = REG_FIELD(0x71, 5, 5),
|
|
[SLP1_DATA] = REG_FIELD(0x71, 4, 4),
|
|
[SLP3_DR] = REG_FIELD(0x71, 2, 2),
|
|
[SLP2_DR] = REG_FIELD(0x71, 1, 1),
|
|
[SLP1_DR] = REG_FIELD(0x71, 0, 0),
|
|
/* SYS_CFG3 */
|
|
[RST_FUN] = REG_FIELD(0x72, 6, 7),
|
|
[DEV_RST] = REG_FIELD(0x72, 5, 5),
|
|
[DEV_SLP] = REG_FIELD(0x72, 4, 4),
|
|
[SLAVE_RESTART_FUN] = REG_FIELD(0x72, 1, 1),
|
|
[DEV_OFF] = REG_FIELD(0x72, 0, 0),
|
|
[WDT_CLR] = REG_FIELD(0x73, 4, 4),
|
|
[WDT_EN] = REG_FIELD(0x73, 3, 3),
|
|
[WDT_SET] = REG_FIELD(0x73, 0, 3),
|
|
[ON_SOURCE] = REG_FIELD(0x74, 0, 7),
|
|
[OFF_SOURCE] = REG_FIELD(0x75, 0, 7),
|
|
/* PWRON_KEY */
|
|
[PWRON_ON_TIME] = REG_FIELD(0x76, 7, 7),
|
|
[PWRON_LP_ACT] = REG_FIELD(0x76, 6, 6),
|
|
[PWRON_LP_OFF_TIME] = REG_FIELD(0x76, 4, 5),
|
|
[PWRON_LP_TM_SEL] = REG_FIELD(0x76, 2, 3),
|
|
[PWRON_DB_SEL] = REG_FIELD(0x76, 0, 1),
|
|
|
|
/* GPIO_INT_CONFIG */
|
|
[INT_FUNCTION] = REG_FIELD(0x7b, 2, 2),
|
|
[INT_POL] = REG_FIELD(0x7b, 1, 1),
|
|
[INT_FC_EN] = REG_FIELD(0x7b, 0, 0),
|
|
|
|
[BUCK1_SEQ] = REG_FIELD(0xB2, 0, 5),
|
|
[BUCK2_SEQ] = REG_FIELD(0xB3, 0, 5),
|
|
[BUCK3_SEQ] = REG_FIELD(0xB4, 0, 5),
|
|
[BUCK4_SEQ] = REG_FIELD(0xB5, 0, 5),
|
|
[BUCK5_SEQ] = REG_FIELD(0xB6, 0, 5),
|
|
[BUCK6_SEQ] = REG_FIELD(0xB7, 0, 5),
|
|
[BUCK7_SEQ] = REG_FIELD(0xB8, 0, 5),
|
|
[BUCK8_SEQ] = REG_FIELD(0xB9, 0, 5),
|
|
[BUCK9_SEQ] = REG_FIELD(0xBA, 0, 5),
|
|
[BUCK10_SEQ] = REG_FIELD(0xBB, 0, 5),
|
|
|
|
[NLDO1_SEQ] = REG_FIELD(0xBC, 0, 5),
|
|
[NLDO2_SEQ] = REG_FIELD(0xBD, 0, 5),
|
|
[NLDO3_SEQ] = REG_FIELD(0xBE, 0, 5),
|
|
[NLDO4_SEQ] = REG_FIELD(0xBF, 0, 5),
|
|
[NLDO5_SEQ] = REG_FIELD(0xC0, 0, 5),
|
|
|
|
[PLDO6_45_SEQ] = REG_FIELD(0xB5, 6, 7),
|
|
[PLDO6_23_SEQ] = REG_FIELD(0xB6, 6, 7),
|
|
[PLDO6_01_SEQ] = REG_FIELD(0xB7, 6, 7),
|
|
|
|
[PLDO1_45_SEQ] = REG_FIELD(0xB8, 6, 7),
|
|
[PLDO1_23_SEQ] = REG_FIELD(0xB9, 6, 7),
|
|
[PLDO1_01_SEQ] = REG_FIELD(0xBA, 6, 7),
|
|
|
|
[PLDO2_45_SEQ] = REG_FIELD(0xBB, 6, 7),
|
|
[PLDO2_23_SEQ] = REG_FIELD(0xBC, 6, 7),
|
|
[PLDO2_01_SEQ] = REG_FIELD(0xBD, 6, 7),
|
|
|
|
[PLDO3_45_SEQ] = REG_FIELD(0xBE, 6, 7),
|
|
[PLDO3_23_SEQ] = REG_FIELD(0xBF, 6, 7),
|
|
[PLDO3_01_SEQ] = REG_FIELD(0xC0, 6, 7),
|
|
|
|
[PLDO4_SEQ] = REG_FIELD(0xC1, 0, 5),
|
|
[PLDO5_SEQ] = REG_FIELD(0xC2, 0, 5),
|
|
|
|
[BUCK9_RATE2] = REG_FIELD(0xEA, 0, 0),
|
|
[BUCK10_RATE2] = REG_FIELD(0xEA, 1, 1),
|
|
[LDO_RATE] = REG_FIELD(0xEA, 3, 5),
|
|
[BUCK1_RATE2] = REG_FIELD(0xEB, 0, 0),
|
|
[BUCK2_RATE2] = REG_FIELD(0xEB, 1, 1),
|
|
[BUCK3_RATE2] = REG_FIELD(0xEB, 2, 2),
|
|
[BUCK4_RATE2] = REG_FIELD(0xEB, 3, 3),
|
|
[BUCK5_RATE2] = REG_FIELD(0xEB, 4, 4),
|
|
[BUCK6_RATE2] = REG_FIELD(0xEB, 5, 5),
|
|
[BUCK7_RATE2] = REG_FIELD(0xEB, 6, 6),
|
|
[BUCK8_RATE2] = REG_FIELD(0xEB, 7, 7),
|
|
};
|
|
|
|
static struct resource rk806_pwrkey_resources[] = {
|
|
DEFINE_RES_IRQ(RK806_IRQ_PWRON_FALL),
|
|
DEFINE_RES_IRQ(RK806_IRQ_PWRON_RISE),
|
|
};
|
|
|
|
static const struct mfd_cell rk806_cells[] = {
|
|
{ .name = "rk806-pinctrl", },
|
|
{
|
|
.name = "rk805-pwrkey",
|
|
.num_resources = ARRAY_SIZE(rk806_pwrkey_resources),
|
|
.resources = &rk806_pwrkey_resources[0],
|
|
},
|
|
{ .name = "rk806-regulator", },
|
|
|
|
};
|
|
|
|
static const struct regmap_irq rk806_irqs[] = {
|
|
/* INT_STS0 IRQs */
|
|
REGMAP_IRQ_REG(RK806_IRQ_PWRON_FALL, 0, RK806_INT_STS_PWRON_FALL),
|
|
REGMAP_IRQ_REG(RK806_IRQ_PWRON_RISE, 0, RK806_INT_STS_PWRON_RISE),
|
|
REGMAP_IRQ_REG(RK806_IRQ_PWRON, 0, RK806_INT_STS_PWRON),
|
|
REGMAP_IRQ_REG(RK806_IRQ_PWRON_LP, 0, RK806_INT_STS_PWRON_LP),
|
|
REGMAP_IRQ_REG(RK806_IRQ_HOTDIE, 0, RK806_INT_STS_HOTDIE),
|
|
REGMAP_IRQ_REG(RK806_IRQ_VDC_RISE, 0, RK806_INT_STS_VDC_RISE),
|
|
REGMAP_IRQ_REG(RK806_IRQ_VDC_FALL, 0, RK806_INT_STS_VDC_FALL),
|
|
REGMAP_IRQ_REG(RK806_IRQ_VB_LO, 0, RK806_INT_STS_VB_LO),
|
|
/* INT_STS1 IRQs */
|
|
REGMAP_IRQ_REG(RK806_IRQ_REV0, 1, RK806_INT_STS_REV0),
|
|
REGMAP_IRQ_REG(RK806_IRQ_REV1, 1, RK806_INT_STS_REV1),
|
|
REGMAP_IRQ_REG(RK806_IRQ_REV2, 1, RK806_INT_STS_REV2),
|
|
REGMAP_IRQ_REG(RK806_IRQ_CRC_ERROR, 1, RK806_INT_STS_CRC_ERROR),
|
|
REGMAP_IRQ_REG(RK806_IRQ_SLP3_GPIO, 1, RK806_INT_STS_SLP3_GPIO),
|
|
REGMAP_IRQ_REG(RK806_IRQ_SLP2_GPIO, 1, RK806_INT_STS_SLP2_GPIO),
|
|
REGMAP_IRQ_REG(RK806_IRQ_SLP1_GPIO, 1, RK806_INT_STS_SLP1_GPIO),
|
|
REGMAP_IRQ_REG(RK806_IRQ_WDT, 1, RK806_INT_STS_WDT),
|
|
};
|
|
|
|
static struct regmap_irq_chip rk806_irq_chip = {
|
|
.name = "rk806",
|
|
.irqs = rk806_irqs,
|
|
.num_irqs = ARRAY_SIZE(rk806_irqs),
|
|
.num_regs = 2,
|
|
.irq_reg_stride = 2,
|
|
.mask_base = RK806_INT_MSK0,
|
|
.status_base = RK806_INT_STS0,
|
|
.ack_base = RK806_INT_STS0,
|
|
.init_ack_masked = true,
|
|
};
|
|
|
|
static const struct regmap_range rk806_yes_ranges[] = {
|
|
/* regmap_reg_range(RK806_INT_STS0, RK806_GPIO_INT_CONFIG), */
|
|
regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5),
|
|
regmap_reg_range(0x70, 0x7a),
|
|
};
|
|
|
|
static const struct regmap_access_table rk806_volatile_table = {
|
|
.yes_ranges = rk806_yes_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(rk806_yes_ranges),
|
|
};
|
|
|
|
const struct regmap_config rk806_regmap_config = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.cache_type = REGCACHE_RBTREE,
|
|
.volatile_table = &rk806_volatile_table,
|
|
};
|
|
EXPORT_SYMBOL_GPL(rk806_regmap_config);
|
|
|
|
static struct kobject *rk806_kobj[2];
|
|
static struct rk806 *rk806_master;
|
|
static struct rk806 *rk806_slaver;
|
|
|
|
static ssize_t rk806_master_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
u32 input[2], addr, data;
|
|
struct rk806 *rk806;
|
|
char cmd;
|
|
int ret;
|
|
|
|
ret = sscanf(buf, "%c ", &cmd);
|
|
if (ret != 1) {
|
|
pr_err("Unknown command\n");
|
|
goto out;
|
|
}
|
|
|
|
rk806 = rk806_master;
|
|
if (!rk806) {
|
|
pr_err("error! rk806 master is NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case 'w':
|
|
ret = sscanf(buf, "%c %x %x", &cmd, &input[0], &input[1]);
|
|
if (ret != 3) {
|
|
pr_err("error! cmd format: echo w [addr] [value]\n");
|
|
goto out;
|
|
};
|
|
|
|
addr = input[0] & 0xff;
|
|
data = input[1] & 0xff;
|
|
pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]);
|
|
|
|
regmap_write(rk806->regmap, addr, data);
|
|
regmap_read(rk806->regmap, addr, &data);
|
|
pr_info("new: %x %x\n", addr, data);
|
|
break;
|
|
case 'r':
|
|
ret = sscanf(buf, "%c %x ", &cmd, &input[0]);
|
|
if (ret != 2) {
|
|
pr_err("error! cmd format: echo r [addr]\n");
|
|
goto out;
|
|
};
|
|
|
|
pr_info("cmd : %c %x\n\n", cmd, input[0]);
|
|
addr = input[0] & 0xff;
|
|
|
|
regmap_read(rk806->regmap, addr, &data);
|
|
pr_info("%x %x\n", input[0], data);
|
|
break;
|
|
default:
|
|
pr_err("Unknown command\n");
|
|
break;
|
|
}
|
|
|
|
out:
|
|
return count;
|
|
}
|
|
|
|
static ssize_t rk806_slaver_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t count)
|
|
{
|
|
u32 input[2], addr, data;
|
|
struct rk806 *rk806;
|
|
char cmd;
|
|
int ret;
|
|
|
|
ret = sscanf(buf, "%c ", &cmd);
|
|
if (ret != 1) {
|
|
pr_err("Unknown command\n");
|
|
goto out;
|
|
}
|
|
|
|
rk806 = rk806_slaver;
|
|
if (!rk806) {
|
|
pr_err("error! rk806 slaver is NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case 'w':
|
|
ret = sscanf(buf, "%c %x %x", &cmd, &input[0], &input[1]);
|
|
if (ret != 3) {
|
|
pr_err("error! cmd format: echo w [addr] [value]\n");
|
|
goto out;
|
|
};
|
|
|
|
addr = input[0] & 0xff;
|
|
data = input[1] & 0xff;
|
|
pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]);
|
|
|
|
regmap_write(rk806->regmap, addr, data);
|
|
regmap_read(rk806->regmap, addr, &data);
|
|
pr_info("new: %x %x\n", addr, data);
|
|
break;
|
|
case 'r':
|
|
ret = sscanf(buf, "%c %x ", &cmd, &input[0]);
|
|
if (ret != 2) {
|
|
pr_err("error! cmd format: echo r [addr]\n");
|
|
goto out;
|
|
};
|
|
pr_info("cmd : %c %x\n\n", cmd, input[0]);
|
|
|
|
addr = input[0] & 0xff;
|
|
regmap_read(rk806->regmap, addr, &data);
|
|
pr_info("%x %x\n", input[0], data);
|
|
break;
|
|
default:
|
|
pr_err("Unknown command\n");
|
|
break;
|
|
}
|
|
|
|
out:
|
|
return count;
|
|
}
|
|
|
|
static struct device_attribute rk806_master_attrs =
|
|
__ATTR(debug, 0200, NULL, rk806_master_store);
|
|
|
|
static struct device_attribute rk806_slaver_attrs =
|
|
__ATTR(debug, 0200, NULL, rk806_slaver_store);
|
|
|
|
int rk806_field_read(struct rk806 *rk806,
|
|
enum rk806_fields field_id)
|
|
{
|
|
int ret;
|
|
int val;
|
|
|
|
ret = regmap_field_read(rk806->rmap_fields[field_id], &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return val;
|
|
}
|
|
EXPORT_SYMBOL_GPL(rk806_field_read);
|
|
|
|
int rk806_field_write(struct rk806 *rk806,
|
|
enum rk806_fields field_id,
|
|
unsigned int val)
|
|
{
|
|
return regmap_field_write(rk806->rmap_fields[field_id], val);
|
|
}
|
|
EXPORT_SYMBOL_GPL(rk806_field_write);
|
|
|
|
static void rk806_vb_requence_config(struct rk806 *rk806)
|
|
{
|
|
struct rk806_platform_data *pdata = rk806->pdata;
|
|
int i;
|
|
|
|
if (!pdata->support_vb_sequence || !pdata->vb_shutdown_sequence)
|
|
return;
|
|
|
|
for (i = RK806_ID_DCDC1; i <= RK806_ID_DCDC10; i++)
|
|
rk806_field_write(rk806, BUCK1_SEQ + i, pdata->vb_shutdown_sequence[i]);
|
|
|
|
for (i = RK806_ID_NLDO1; i <= RK806_ID_NLDO5; i++)
|
|
rk806_field_write(rk806,
|
|
NLDO1_SEQ + (i - RK806_ID_NLDO1),
|
|
pdata->vb_shutdown_sequence[i]);
|
|
|
|
rk806_field_write(rk806, PLDO1_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO1]);
|
|
rk806_field_write(rk806, PLDO2_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO2]);
|
|
rk806_field_write(rk806, PLDO3_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO3]);
|
|
rk806_field_write(rk806, PLDO4_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO4]);
|
|
rk806_field_write(rk806, PLDO5_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO5]);
|
|
rk806_field_write(rk806, PLDO6_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO6]);
|
|
}
|
|
|
|
void rk806_shutdown_requence_config(struct rk806 *rk806)
|
|
{
|
|
struct rk806_platform_data *pdata = rk806->pdata;
|
|
int i;
|
|
|
|
if (!pdata->support_shutdown_sequence || !pdata->shutdown_sequence)
|
|
return;
|
|
|
|
for (i = RK806_ID_DCDC1; i <= RK806_ID_DCDC10; i++)
|
|
rk806_field_write(rk806, BUCK1_SEQ + i, pdata->shutdown_sequence[i]);
|
|
|
|
for (i = RK806_ID_NLDO1; i <= RK806_ID_NLDO5; i++)
|
|
rk806_field_write(rk806,
|
|
NLDO1_SEQ + (i - RK806_ID_NLDO1),
|
|
pdata->shutdown_sequence[i]);
|
|
|
|
rk806_field_write(rk806, PLDO1_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO1]);
|
|
rk806_field_write(rk806, PLDO2_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO2]);
|
|
rk806_field_write(rk806, PLDO3_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO3]);
|
|
rk806_field_write(rk806, PLDO4_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO4]);
|
|
rk806_field_write(rk806, PLDO5_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO5]);
|
|
rk806_field_write(rk806, PLDO6_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO6]);
|
|
}
|
|
EXPORT_SYMBOL_GPL(rk806_shutdown_requence_config);
|
|
|
|
static void rk806_irq_init(struct rk806 *rk806)
|
|
{
|
|
/* INT pin polarity active low */
|
|
rk806_field_write(rk806, INT_POL, RK806_INT_POL_LOW);
|
|
}
|
|
|
|
static int rk806_pinctrl_init(struct rk806 *rk806)
|
|
{
|
|
struct device *dev = rk806->dev;
|
|
|
|
rk806->pins = devm_kzalloc(dev,
|
|
sizeof(struct rk806_pin_info),
|
|
GFP_KERNEL);
|
|
if (!rk806->pins)
|
|
return -ENOMEM;
|
|
|
|
rk806->pins->p = devm_pinctrl_get(dev);
|
|
if (IS_ERR(rk806->pins->p)) {
|
|
rk806->pins->p = NULL;
|
|
dev_err(dev, "no pinctrl handle\n");
|
|
return 0;
|
|
}
|
|
|
|
rk806->pins->default_st = pinctrl_lookup_state(rk806->pins->p,
|
|
PINCTRL_STATE_DEFAULT);
|
|
|
|
if (IS_ERR(rk806->pins->default_st))
|
|
dev_err(dev, "no default pinctrl state\n");
|
|
|
|
rk806->pins->power_off = pinctrl_lookup_state(rk806->pins->p,
|
|
"pmic-power-off");
|
|
if (IS_ERR(rk806->pins->power_off)) {
|
|
rk806->pins->power_off = NULL;
|
|
dev_err(dev, "no power-off pinctrl state\n");
|
|
}
|
|
|
|
rk806->pins->sleep = pinctrl_lookup_state(rk806->pins->p,
|
|
"pmic-sleep");
|
|
if (IS_ERR(rk806->pins->sleep)) {
|
|
rk806->pins->sleep = NULL;
|
|
dev_err(dev, "no sleep-setting state\n");
|
|
}
|
|
|
|
rk806->pins->reset = pinctrl_lookup_state(rk806->pins->p,
|
|
"pmic-reset");
|
|
if (IS_ERR(rk806->pins->reset)) {
|
|
rk806->pins->reset = NULL;
|
|
dev_err(dev, "no reset-setting pinctrl state\n");
|
|
}
|
|
|
|
rk806->pins->dvs = pinctrl_lookup_state(rk806->pins->p,
|
|
"pmic-dvs");
|
|
if (IS_ERR(rk806->pins->dvs)) {
|
|
rk806->pins->dvs = NULL;
|
|
dev_err(dev, "no dvs-setting pinctrl state\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rk806_vb_force_shutdown_init(struct rk806 *rk806)
|
|
{
|
|
struct rk806_platform_data *pdata = rk806->pdata;
|
|
|
|
rk806_vb_requence_config(rk806);
|
|
rk806_field_write(rk806, VB_LO_ACT, VB_LO_ACT_SD);
|
|
rk806_field_write(rk806, VB_LO_SEL,
|
|
(pdata->low_voltage_threshold - 2800) / 100);
|
|
}
|
|
|
|
static irqreturn_t rk806_vdc_irq(int irq, void *data)
|
|
{
|
|
struct rk806 *rk806 = data;
|
|
|
|
pm_wakeup_dev_event(rk806->dev, 2000, false);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void rk806_vdc_irqs_init(struct rk806 *rk806)
|
|
{
|
|
int ret, vdc_irq_fall, vdc_irq_rise;
|
|
|
|
if (!rk806->pdata->vdc_wakeup_enable)
|
|
return;
|
|
|
|
vdc_irq_rise = regmap_irq_get_virq(rk806->irq_data, RK806_IRQ_VDC_RISE);
|
|
if (vdc_irq_rise < 0) {
|
|
dev_err(rk806->dev, "RK806: IRQ_VDC_RISE get failed!\n");
|
|
return;
|
|
}
|
|
|
|
ret = devm_request_threaded_irq(rk806->dev, vdc_irq_rise,
|
|
NULL,
|
|
rk806_vdc_irq,
|
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
|
"rk806_vdc_rise", rk806);
|
|
if (ret) {
|
|
dev_err(rk806->dev, "rk806: vdc_irq_rise request failed!\n");
|
|
return;
|
|
}
|
|
enable_irq_wake(vdc_irq_rise);
|
|
|
|
vdc_irq_fall = regmap_irq_get_virq(rk806->irq_data, RK806_IRQ_VDC_FALL);
|
|
if (vdc_irq_fall < 0) {
|
|
dev_err(rk806->dev, "RK806: IRQ_VDC_FALL get failed!\n");
|
|
return;
|
|
}
|
|
|
|
ret = devm_request_threaded_irq(rk806->dev, vdc_irq_fall,
|
|
NULL,
|
|
rk806_vdc_irq,
|
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
|
"rk806_vdc_fall", rk806);
|
|
if (ret) {
|
|
dev_err(rk806->dev, "rk806: vdc_irq_fall request failed!\n");
|
|
return;
|
|
}
|
|
enable_irq_wake(vdc_irq_fall);
|
|
device_init_wakeup(rk806->dev, true);
|
|
}
|
|
|
|
|
|
static int rk806_parse_dt(struct rk806 *rk806)
|
|
{
|
|
struct rk806_platform_data *pdata;
|
|
struct device *dev = rk806->dev;
|
|
int rst_fun;
|
|
int ret;
|
|
|
|
pdata = rk806->pdata;
|
|
|
|
pdata->shutdown_voltage_threshold = 2700;
|
|
pdata->shutdown_temperture_threshold = 160;
|
|
pdata->hotdie_temperture_threshold = 115;
|
|
pdata->force_shutdown_enable = 1;
|
|
pdata->vdc_wakeup_enable = 0;
|
|
|
|
ret = device_property_read_u32(dev,
|
|
"low_voltage_threshold",
|
|
&pdata->low_voltage_threshold);
|
|
if (ret < 0) {
|
|
pdata->low_voltage_threshold = VB_LO_3400MV;
|
|
dev_info(dev, "low_voltage_threshold missing!\n");
|
|
} else {
|
|
if ((pdata->low_voltage_threshold > 3500) ||
|
|
(pdata->low_voltage_threshold < 2800)) {
|
|
dev_err(dev, "low_voltage_threshold out [2800 3500]!\n");
|
|
pdata->low_voltage_threshold = VB_LO_3400MV;
|
|
}
|
|
}
|
|
ret = device_property_read_u32(dev,
|
|
"shutdown_voltage_threshold",
|
|
&pdata->shutdown_voltage_threshold);
|
|
if (ret < 0) {
|
|
pdata->force_shutdown_enable = 0;
|
|
dev_info(dev, "shutdown_voltage_threshold missing!\n");
|
|
}
|
|
|
|
if ((pdata->shutdown_voltage_threshold > 3400) ||
|
|
(pdata->shutdown_voltage_threshold < 2700)) {
|
|
dev_err(dev, "shutdown_voltage_threshold out [2700 3400]!\n");
|
|
pdata->shutdown_voltage_threshold = 2700;
|
|
}
|
|
|
|
ret = device_property_read_u32(dev,
|
|
"shutdown_temperture_threshold",
|
|
&pdata->shutdown_temperture_threshold);
|
|
if (ret < 0)
|
|
dev_info(dev, "shutdown_temperture_threshold missing!\n");
|
|
|
|
ret = device_property_read_u32(dev,
|
|
"hotdie_temperture_threshold",
|
|
&pdata->hotdie_temperture_threshold);
|
|
if (ret < 0)
|
|
dev_info(dev, "hotdie_temperture_threshold missing!\n");
|
|
|
|
ret = device_property_read_u32(dev, "pmic-reset-func", &rst_fun);
|
|
if (ret < 0) {
|
|
dev_info(dev, "pmic-reset-func missing!\n");
|
|
rk806_field_write(rk806, RST_FUN, 0x00);
|
|
} else
|
|
rk806_field_write(rk806, RST_FUN, rst_fun);
|
|
|
|
/* PWRON_ON_TIME: 0:500mS; 1:20mS */
|
|
if (device_property_read_bool(dev, "pwron-on-time-500ms"))
|
|
rk806_field_write(rk806, PWRON_ON_TIME, 0x00);
|
|
|
|
if (device_property_read_bool(dev, "vdc-wakeup-enable"))
|
|
pdata->vdc_wakeup_enable = 1;
|
|
|
|
pdata->shutdown_sequence = devm_kzalloc(dev,
|
|
RK806_ID_END * sizeof(int),
|
|
GFP_KERNEL);
|
|
if (!pdata->shutdown_sequence)
|
|
return -EINVAL;
|
|
|
|
pdata->support_shutdown_sequence = 1;
|
|
ret = device_property_read_u32_array(dev,
|
|
"shutdown-sequence",
|
|
pdata->shutdown_sequence,
|
|
RK806_ID_END);
|
|
if (ret) {
|
|
dev_info(dev, "shutdown-sequence missing!\n");
|
|
pdata->support_shutdown_sequence = 0;
|
|
}
|
|
|
|
pdata->vb_shutdown_sequence = devm_kzalloc(dev,
|
|
RK806_ID_END * sizeof(int),
|
|
GFP_KERNEL);
|
|
if (!pdata->vb_shutdown_sequence)
|
|
return -EINVAL;
|
|
|
|
pdata->support_vb_sequence = 1;
|
|
ret = device_property_read_u32_array(dev,
|
|
"vb-shutdown-sequence",
|
|
pdata->vb_shutdown_sequence,
|
|
RK806_ID_END);
|
|
if (ret) {
|
|
pdata->support_vb_sequence = 0;
|
|
dev_info(dev, "vb-shutdown-sequence missing!\n");
|
|
}
|
|
|
|
pdata->dvs_control_suspend = devm_kzalloc(dev,
|
|
RK806_ID_END * sizeof(int),
|
|
GFP_KERNEL);
|
|
if (!pdata->dvs_control_suspend)
|
|
return -EINVAL;
|
|
|
|
pdata->support_dvs_control_suspend = 1;
|
|
ret = device_property_read_u32_array(dev,
|
|
"dvs-suspend-control-by",
|
|
pdata->dvs_control_suspend,
|
|
RK806_ID_END);
|
|
if (ret) {
|
|
pdata->support_dvs_control_suspend = 0;
|
|
dev_info(dev, "dvs-suspend-control-by missing!\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk806_init(struct rk806 *rk806)
|
|
{
|
|
struct rk806_platform_data *pdata;
|
|
int vb_uv_sel;
|
|
|
|
pdata = rk806->pdata;
|
|
|
|
if (pdata->force_shutdown_enable) {
|
|
if (pdata->shutdown_voltage_threshold <= 2700)
|
|
vb_uv_sel = VB_UV_SEL_2700;
|
|
else
|
|
vb_uv_sel = (pdata->shutdown_voltage_threshold - 2700) / 100;
|
|
|
|
rk806_field_write(rk806, VB_UV_SEL, vb_uv_sel);
|
|
}
|
|
|
|
if (pdata->hotdie_temperture_threshold >= 160)
|
|
rk806_field_write(rk806, TSD_TEMP, TSD_TEMP_160);
|
|
|
|
/* When the slave chip goes through a shutdown process, it will automatically trigger a restart */
|
|
rk806_field_write(rk806, SLAVE_RESTART_FUN, 0x01);
|
|
/* Digital output 2MHz clock force enable */
|
|
rk806_field_write(rk806, ENB2_2M, 0x01);
|
|
|
|
rk806_vb_force_shutdown_init(rk806);
|
|
rk806_vdc_irqs_init(rk806);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rk806_device_init(struct rk806 *rk806)
|
|
{
|
|
struct device_node *np = rk806->dev->of_node;
|
|
struct rk806_platform_data *pdata;
|
|
int name_h, name_l, chip_ver, otp_ver;
|
|
int on_source, off_source;
|
|
int ret;
|
|
int i;
|
|
|
|
pdata = devm_kzalloc(rk806->dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
return -ENOMEM;
|
|
|
|
rk806->pdata = pdata;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rk806_reg_fields); i++) {
|
|
const struct reg_field *reg_fields = rk806_reg_fields;
|
|
|
|
rk806->rmap_fields[i] =
|
|
devm_regmap_field_alloc(rk806->dev,
|
|
rk806->regmap,
|
|
reg_fields[i]);
|
|
if (IS_ERR(rk806->rmap_fields[i])) {
|
|
dev_err(rk806->dev, "cannot allocate regmap field\n");
|
|
return PTR_ERR(rk806->rmap_fields[i]);
|
|
}
|
|
}
|
|
|
|
name_h = rk806_field_read(rk806, CHIP_NAME_H);
|
|
name_l = rk806_field_read(rk806, CHIP_NAME_L);
|
|
chip_ver = rk806_field_read(rk806, CHIP_VER);
|
|
otp_ver = rk806_field_read(rk806, OTP_VER);
|
|
dev_info(rk806->dev, "chip id: RK%x%x,ver:0x%x, 0x%x\n",
|
|
name_h, name_l, chip_ver, otp_ver);
|
|
if (chip_ver == VERSION_AB)
|
|
rk806_field_write(rk806, ABNORDET_EN, 0x01);
|
|
|
|
on_source = rk806_field_read(rk806, ON_SOURCE);
|
|
off_source = rk806_field_read(rk806, OFF_SOURCE);
|
|
dev_info(rk806->dev, "ON: 0x%x OFF:0x%x\n", on_source, off_source);
|
|
|
|
rk806_parse_dt(rk806);
|
|
|
|
rk806_irq_init(rk806);
|
|
ret = devm_regmap_add_irq_chip(rk806->dev,
|
|
rk806->regmap,
|
|
rk806->irq,
|
|
IRQF_ONESHOT | IRQF_SHARED,
|
|
0,
|
|
&rk806_irq_chip,
|
|
&rk806->irq_data);
|
|
if (ret) {
|
|
dev_err(rk806->dev, "Failed to add IRQ chip: err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
enable_irq_wake(rk806->irq);
|
|
|
|
ret = devm_mfd_add_devices(rk806->dev,
|
|
PLATFORM_DEVID_AUTO,
|
|
rk806_cells,
|
|
ARRAY_SIZE(rk806_cells),
|
|
NULL,
|
|
0,
|
|
regmap_irq_get_domain(rk806->irq_data));
|
|
if (ret < 0) {
|
|
dev_err(rk806->dev, "mfd_add_devices failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
rk806_pinctrl_init(rk806);
|
|
rk806_init(rk806);
|
|
|
|
if (strcmp(np->name, "rk806slave")) {
|
|
rk806_kobj[0] = kobject_create_and_add(np->name, NULL);
|
|
if (rk806_kobj[0]) {
|
|
ret = sysfs_create_file(rk806_kobj[0], &rk806_master_attrs.attr);
|
|
if (ret)
|
|
dev_err(rk806->dev, "create %s sysfs error\n", np->name);
|
|
else
|
|
rk806_master = rk806;
|
|
}
|
|
} else {
|
|
rk806_kobj[1] = kobject_create_and_add(np->name, NULL);
|
|
if (rk806_kobj[1]) {
|
|
ret = sysfs_create_file(rk806_kobj[1], &rk806_slaver_attrs.attr);
|
|
if (ret)
|
|
dev_err(rk806->dev, "create %s sysfs error\n", np->name);
|
|
else
|
|
rk806_slaver = rk806;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(rk806_device_init);
|
|
|
|
int rk806_device_exit(struct rk806 *rk806)
|
|
{
|
|
struct device_node *np = rk806->dev->of_node;
|
|
|
|
if (strcmp(np->name, "rk806slave")) {
|
|
if (rk806_kobj[0]) {
|
|
sysfs_remove_file(rk806_kobj[0], &rk806_master_attrs.attr);
|
|
kobject_put(rk806_kobj[0]);
|
|
}
|
|
} else {
|
|
if (rk806_kobj[1]) {
|
|
sysfs_remove_file(rk806_kobj[1], &rk806_slaver_attrs.attr);
|
|
kobject_put(rk806_kobj[1]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(rk806_device_exit);
|
|
|
|
const struct of_device_id rk806_of_match[] = {
|
|
{ .compatible = "rockchip,rk806", },
|
|
{ }
|
|
};
|
|
EXPORT_SYMBOL_GPL(rk806_of_match);
|
|
|
|
MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
|
|
MODULE_DESCRIPTION("rk806 MFD Driver");
|
|
MODULE_LICENSE("GPL v2");
|