cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

da9052-i2c.c (4771B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * I2C access for DA9052 PMICs.
      4 *
      5 * Copyright(c) 2011 Dialog Semiconductor Ltd.
      6 *
      7 * Author: David Dajun Chen <dchen@diasemi.com>
      8 */
      9
     10#include <linux/device.h>
     11#include <linux/module.h>
     12#include <linux/input.h>
     13#include <linux/mfd/core.h>
     14#include <linux/i2c.h>
     15#include <linux/err.h>
     16
     17#include <linux/mfd/da9052/da9052.h>
     18#include <linux/mfd/da9052/reg.h>
     19
     20#ifdef CONFIG_OF
     21#include <linux/of.h>
     22#include <linux/of_device.h>
     23#endif
     24
     25/* I2C safe register check */
     26static inline bool i2c_safe_reg(unsigned char reg)
     27{
     28	switch (reg) {
     29	case DA9052_STATUS_A_REG:
     30	case DA9052_STATUS_B_REG:
     31	case DA9052_STATUS_C_REG:
     32	case DA9052_STATUS_D_REG:
     33	case DA9052_ADC_RES_L_REG:
     34	case DA9052_ADC_RES_H_REG:
     35	case DA9052_VDD_RES_REG:
     36	case DA9052_ICHG_AV_REG:
     37	case DA9052_TBAT_RES_REG:
     38	case DA9052_ADCIN4_RES_REG:
     39	case DA9052_ADCIN5_RES_REG:
     40	case DA9052_ADCIN6_RES_REG:
     41	case DA9052_TJUNC_RES_REG:
     42	case DA9052_TSI_X_MSB_REG:
     43	case DA9052_TSI_Y_MSB_REG:
     44	case DA9052_TSI_LSB_REG:
     45	case DA9052_TSI_Z_MSB_REG:
     46		return true;
     47	default:
     48		return false;
     49	}
     50}
     51
     52/*
     53 * There is an issue with DA9052 and DA9053_AA/BA/BB PMIC where the PMIC
     54 * gets lockup up or fails to respond following a system reset.
     55 * This fix is to follow any read or write with a dummy read to a safe
     56 * register.
     57 */
     58static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
     59{
     60	int val;
     61
     62	switch (da9052->chip_id) {
     63	case DA9052:
     64	case DA9053_AA:
     65	case DA9053_BA:
     66	case DA9053_BB:
     67		/* A dummy read to a safe register address. */
     68		if (!i2c_safe_reg(reg))
     69			return regmap_read(da9052->regmap,
     70					   DA9052_PARK_REGISTER,
     71					   &val);
     72		break;
     73	case DA9053_BC:
     74	default:
     75		/*
     76		 * For other chips parking of I2C register
     77		 * to a safe place is not required.
     78		 */
     79		break;
     80	}
     81
     82	return 0;
     83}
     84
     85/*
     86 * According to errata item 24, multiwrite mode should be avoided
     87 * in order to prevent register data corruption after power-down.
     88 */
     89static int da9052_i2c_disable_multiwrite(struct da9052 *da9052)
     90{
     91	int reg_val, ret;
     92
     93	ret = regmap_read(da9052->regmap, DA9052_CONTROL_B_REG, &reg_val);
     94	if (ret < 0)
     95		return ret;
     96
     97	if (!(reg_val & DA9052_CONTROL_B_WRITEMODE)) {
     98		reg_val |= DA9052_CONTROL_B_WRITEMODE;
     99		ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,
    100				   reg_val);
    101		if (ret < 0)
    102			return ret;
    103	}
    104
    105	return 0;
    106}
    107
    108static const struct i2c_device_id da9052_i2c_id[] = {
    109	{"da9052", DA9052},
    110	{"da9053-aa", DA9053_AA},
    111	{"da9053-ba", DA9053_BA},
    112	{"da9053-bb", DA9053_BB},
    113	{"da9053-bc", DA9053_BC},
    114	{}
    115};
    116MODULE_DEVICE_TABLE(i2c, da9052_i2c_id);
    117
    118#ifdef CONFIG_OF
    119static const struct of_device_id dialog_dt_ids[] = {
    120	{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
    121	{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
    122	{ .compatible = "dlg,da9053-ba", .data = &da9052_i2c_id[2] },
    123	{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
    124	{ .compatible = "dlg,da9053-bc", .data = &da9052_i2c_id[4] },
    125	{ /* sentinel */ }
    126};
    127#endif
    128
    129static int da9052_i2c_probe(struct i2c_client *client,
    130				       const struct i2c_device_id *id)
    131{
    132	struct da9052 *da9052;
    133	int ret;
    134
    135	da9052 = devm_kzalloc(&client->dev, sizeof(struct da9052), GFP_KERNEL);
    136	if (!da9052)
    137		return -ENOMEM;
    138
    139	da9052->dev = &client->dev;
    140	da9052->chip_irq = client->irq;
    141	da9052->fix_io = da9052_i2c_fix;
    142
    143	i2c_set_clientdata(client, da9052);
    144
    145	da9052->regmap = devm_regmap_init_i2c(client, &da9052_regmap_config);
    146	if (IS_ERR(da9052->regmap)) {
    147		ret = PTR_ERR(da9052->regmap);
    148		dev_err(&client->dev, "Failed to allocate register map: %d\n",
    149			ret);
    150		return ret;
    151	}
    152
    153	ret = da9052_i2c_disable_multiwrite(da9052);
    154	if (ret < 0)
    155		return ret;
    156
    157#ifdef CONFIG_OF
    158	if (!id)
    159		id = of_device_get_match_data(&client->dev);
    160#endif
    161
    162	if (!id) {
    163		ret = -ENODEV;
    164		dev_err(&client->dev, "id is null.\n");
    165		return ret;
    166	}
    167
    168	return da9052_device_init(da9052, id->driver_data);
    169}
    170
    171static int da9052_i2c_remove(struct i2c_client *client)
    172{
    173	struct da9052 *da9052 = i2c_get_clientdata(client);
    174
    175	da9052_device_exit(da9052);
    176	return 0;
    177}
    178
    179static struct i2c_driver da9052_i2c_driver = {
    180	.probe = da9052_i2c_probe,
    181	.remove = da9052_i2c_remove,
    182	.id_table = da9052_i2c_id,
    183	.driver = {
    184		.name = "da9052",
    185#ifdef CONFIG_OF
    186		.of_match_table = dialog_dt_ids,
    187#endif
    188	},
    189};
    190
    191static int __init da9052_i2c_init(void)
    192{
    193	int ret;
    194
    195	ret = i2c_add_driver(&da9052_i2c_driver);
    196	if (ret != 0) {
    197		pr_err("DA9052 I2C registration failed %d\n", ret);
    198		return ret;
    199	}
    200
    201	return 0;
    202}
    203subsys_initcall(da9052_i2c_init);
    204
    205static void __exit da9052_i2c_exit(void)
    206{
    207	i2c_del_driver(&da9052_i2c_driver);
    208}
    209module_exit(da9052_i2c_exit);
    210
    211MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
    212MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC");
    213MODULE_LICENSE("GPL");