// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2022 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include bool mt6338_probe_done; EXPORT_SYMBOL_GPL(mt6338_probe_done); #define MT6338_MFD_CELL(_name) \ { \ .name = #_name, \ .of_compatible = "mediatek," #_name, \ } static bool mt6338_is_volatile_reg(struct device *dev, unsigned int reg) { return true; } static struct regmap_config mt6338_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = MT6338_MAX_REGISTER, .cache_type = REGCACHE_FLAT, .volatile_reg = mt6338_is_volatile_reg, }; static const struct mfd_cell mt6338_devs[] = { MT6338_MFD_CELL(mt6338-accdet), MT6338_MFD_CELL(mt6338-auxadc), MT6338_MFD_CELL(mt6338-sound), MT6338_MFD_CELL(mt6338-efuse), /* debug dev */ /* { .name = "mt6360_dbg", },*/ }; static int mt6338_check_id(struct mt6338_pmic_info *mpi) { int ret = 0; unsigned int data = 0; ret = regmap_read(mpi->regmap, MT6338_SWCID_H, &data); if (ret < 0) { dev_info(mpi->dev, "device not found\n"); return ret; } if (data != MT6338_SWCID_H_CODE) { dev_info(mpi->dev, "not mt6338 chip\n"); return -ENODEV; } mpi->chip_rev = (data << 8); ret = regmap_read(mpi->regmap, MT6338_SWCID_L, &data); mpi->chip_rev |= data; return 0; } void mt6338_Keyunlock(struct mt6338_pmic_info *mpi) { regmap_write(mpi->regmap, MT6338_TOP_DIG_WPK, 0x38); regmap_write(mpi->regmap, MT6338_TOP_DIG_WPK_H, 0x63); regmap_write(mpi->regmap, MT6338_TOP_TMA_KEY, 0xc7); regmap_write(mpi->regmap, MT6338_TOP_TMA_KEY_H, 0x9c); regmap_write(mpi->regmap, MT6338_TOP2_ELR2, 0x2a); regmap_write(mpi->regmap, MT6338_TOP2_ELR3, 0x2a); } void mt6338_Keylock(struct mt6338_pmic_info *mpi) { regmap_write(mpi->regmap, MT6338_TOP_DIG_WPK, 0x0); regmap_write(mpi->regmap, MT6338_TOP_DIG_WPK_H, 0x0); regmap_write(mpi->regmap, MT6338_TOP_TMA_KEY, 0x0); regmap_write(mpi->regmap, MT6338_TOP_TMA_KEY_H, 0x0); regmap_write(mpi->regmap, MT6338_PSC_WPK_L, 0x0); regmap_write(mpi->regmap, MT6338_PSC_WPK_H, 0x0); regmap_write(mpi->regmap, MT6338_HK_TOP_WKEY_L, 0x0); regmap_write(mpi->regmap, MT6338_HK_TOP_WKEY_H, 0x0); } void mt6338_LP_Setting(struct mt6338_pmic_info *mpi) { #ifdef LP_SETTING /*---Turn ON hardware clock DCM mode to save more power---*/ regmap_update_bits(mpi->regmap, MT6338_LDO_VAUD18_CON2, RG_LDO_VAUD18_CK_SW_MODE_MASK_SFT, 0x0 << RG_LDO_VAUD18_CK_SW_MODE_MASK_SFT); /*---LP Voltage Set---*/ /*---Using PMRC_EN[15:0] in DVT---*/ /*---HW0 (SRCLKEN0), HW1 (SRCLKEN1), SCP_VAO (SSHUB/VOW)---*/ regmap_update_bits(mpi->regmap, MT6338_PMRC_CON1, RG_VR_SPM_MODE_MASK_SFT, 0x1 << RG_VR_SPM_MODE_SFT); /* Change PAD_PAD_SRCLKEN_IN0 into SW mode */ regmap_update_bits(mpi->regmap, MT6338_TOP_CON, RG_SRCLKEN_IN_HW_MODE_MASK_SFT, 0x0 << RG_SRCLKEN_IN_HW_MODE_SFT); regmap_update_bits(mpi->regmap, MT6338_TOP_CON, RG_SRCLKEN_IN_EN_MASK_SFT, 0x1 << RG_SRCLKEN_IN_EN_SFT); /*---Multi-User---*/ regmap_update_bits(mpi->regmap, MT6338_LDO_VAUD18_MULTI_SW_0, RG_LDO_VAUD18_EN_1_MASK_SFT, 0x0 << RG_LDO_VAUD18_EN_1_SFT); regmap_update_bits(mpi->regmap, MT6338_LDO_VAUD18_MULTI_SW_1, RG_LDO_VAUD18_EN_2_MASK_SFT, 0x0 << RG_LDO_VAUD18_EN_2_SFT); #endif } void mt6338_Suspend_Setting(struct mt6338_pmic_info *mpi) { regmap_write(mpi->regmap, MT6338_MTC_CTL0, 0x10); regmap_write(mpi->regmap, MT6338_MTC_CTL0, 0x11); regmap_write(mpi->regmap, MT6338_MTC_CTL0, 0x13); regmap_write(mpi->regmap, MT6338_DA_INTF_STTING3, 0x08); regmap_write(mpi->regmap, MT6338_LDO_VAUD18_CON2, 0x1C); regmap_write(mpi->regmap, MT6338_LDO_VAUD18_OP_EN0, 0x01); regmap_write(mpi->regmap, MT6338_LDO_VAUD18_OP_CFG0, 0x01); regmap_write(mpi->regmap, MT6338_DA_INTF_STTING1, 0x64); regmap_write(mpi->regmap, MT6338_DA_INTF_STTING1, 0x66); regmap_write(mpi->regmap, MT6338_DA_INTF_STTING1, 0x76); } void mt6338_InitSetting(struct mt6338_pmic_info *mpi) { regmap_write(mpi->regmap, MT6338_TOP_CON, 0x7); regmap_write(mpi->regmap, MT6338_TEST_CON0, 0x1f); regmap_write(mpi->regmap, MT6338_SMT_CON0, 0x3); regmap_write(mpi->regmap, MT6338_GPIO_PULLEN0, 0xf9); regmap_write(mpi->regmap, MT6338_GPIO_PULLEN1, 0x1f); regmap_write(mpi->regmap, MT6338_TOP_CKPDN_CON0, 0x5b); regmap_write(mpi->regmap, MT6338_HK_TOP_CLK_CON0, 0x15); regmap_write(mpi->regmap, MT6338_DA_INTF_STTING3, 0x0c); regmap_write(mpi->regmap, MT6338_MTC_CTL0, 0x13); regmap_write(mpi->regmap, MT6338_PLT_CON0, 0x0); regmap_write(mpi->regmap, MT6338_PLT_CON1, 0x0); regmap_write(mpi->regmap, MT6338_HK_TOP_CLK_CON0, 0x15); regmap_write(mpi->regmap, MT6338_AUXADC_CON0, 0x0); regmap_write(mpi->regmap, MT6338_AUXADC_TRIM_SEL2, 0x40); regmap_write(mpi->regmap, MT6338_TOP_TOP_CKHWEN_CON0, 0x0f); regmap_write(mpi->regmap, MT6338_LDO_TOP_CLK_DCM_CON0, 0x01); regmap_write(mpi->regmap, MT6338_LDO_TOP_VR_CLK_CON0, 0x00); regmap_write(mpi->regmap, MT6338_LDO_VAUD18_CON2, 0x1c); regmap_write(mpi->regmap, MT6338_PLT_CON0, 0x3a); regmap_write(mpi->regmap, MT6338_PLT_CON1, 0x0c); } static const unsigned short mt6338_slave_addr = MT6338_PMIC_SLAVEID; static int mt6338_pmic_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mt6338_pmic_info *mpi; struct regmap_config *regmap_config = &mt6338_regmap_config; int ret; mt6338_probe_done = false; dev_info(&client->dev, "+%s()\n", __func__); mpi = devm_kzalloc(&client->dev, sizeof(*mpi), GFP_KERNEL); if (!mpi) return -ENOMEM; mpi->i2c = client; mpi->dev = &client->dev; i2c_set_clientdata(client, mpi); mutex_init(&mpi->io_lock); dev_info(&client->dev, "+%s() mutex_init\n", __func__); /* regmap regiser */ regmap_config->lock_arg = &mpi->io_lock; mpi->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(mpi->regmap)) { dev_info(&client->dev, "regmap register fail\n"); return PTR_ERR(mpi->regmap); } /* chip id check */ ret = mt6338_check_id(mpi); if (ret < 0) { dev_info(&client->dev, "mt6338_check_id fail, return 0\n"); return ret; } /* mfd cell register */ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, mt6338_devs, ARRAY_SIZE(mt6338_devs), NULL, 0, NULL); if (ret < 0) { dev_info(&client->dev, "mfd add cells fail\n"); goto out; } dev_info(&client->dev, "execute InitSetting\n"); /* initial setting */ mt6338_Keyunlock(mpi); mt6338_LP_Setting(mpi); mt6338_InitSetting(mpi); mt6338_Suspend_Setting(mpi); dev_info(&client->dev, "Successfully probed\n"); mt6338_probe_done = true; return 0; out: i2c_unregister_device(mpi->i2c); return ret; } static int mt6338_pmic_remove(struct i2c_client *client) { struct mt6338_pmic_info *mpi = i2c_get_clientdata(client); i2c_unregister_device(mpi->i2c); return 0; } static const struct of_device_id __maybe_unused mt6338_pmic_of_id[] = { { .compatible = "mediatek,mt6338_pmic", }, {}, }; MODULE_DEVICE_TABLE(of, mt6338_pmic_of_id); static const struct i2c_device_id mt6338_pmic_id[] = { { "mt6338_pmic", 0 }, {}, }; MODULE_DEVICE_TABLE(i2c, mt6338_pmic_id); static struct i2c_driver mt6338_pmic_driver = { .driver = { .name = "mt6338_pmic", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mt6338_pmic_of_id), }, .probe = mt6338_pmic_probe, .remove = mt6338_pmic_remove, .id_table = mt6338_pmic_id, }; module_i2c_driver(mt6338_pmic_driver); MODULE_AUTHOR("Ting-Fang Hou"); MODULE_DESCRIPTION("MT6338 PMIC I2C Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0.0");