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

adp5520.c (8159B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
      4 * LCD Backlight: drivers/video/backlight/adp5520_bl
      5 * LEDs		: drivers/led/leds-adp5520
      6 * GPIO		: drivers/gpio/adp5520-gpio (ADP5520 only)
      7 * Keys		: drivers/input/keyboard/adp5520-keys (ADP5520 only)
      8 *
      9 * Copyright 2009 Analog Devices Inc.
     10 *
     11 * Author: Michael Hennerich <michael.hennerich@analog.com>
     12 *
     13 * Derived from da903x:
     14 * Copyright (C) 2008 Compulab, Ltd.
     15 *	Mike Rapoport <mike@compulab.co.il>
     16 *
     17 * Copyright (C) 2006-2008 Marvell International Ltd.
     18 *	Eric Miao <eric.miao@marvell.com>
     19 */
     20
     21#include <linux/kernel.h>
     22#include <linux/init.h>
     23#include <linux/platform_device.h>
     24#include <linux/slab.h>
     25#include <linux/interrupt.h>
     26#include <linux/irq.h>
     27#include <linux/err.h>
     28#include <linux/i2c.h>
     29
     30#include <linux/mfd/adp5520.h>
     31
     32struct adp5520_chip {
     33	struct i2c_client *client;
     34	struct device *dev;
     35	struct mutex lock;
     36	struct blocking_notifier_head notifier_list;
     37	int irq;
     38	unsigned long id;
     39	uint8_t mode;
     40};
     41
     42static int __adp5520_read(struct i2c_client *client,
     43				int reg, uint8_t *val)
     44{
     45	int ret;
     46
     47	ret = i2c_smbus_read_byte_data(client, reg);
     48	if (ret < 0) {
     49		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
     50		return ret;
     51	}
     52
     53	*val = (uint8_t)ret;
     54	return 0;
     55}
     56
     57static int __adp5520_write(struct i2c_client *client,
     58				 int reg, uint8_t val)
     59{
     60	int ret;
     61
     62	ret = i2c_smbus_write_byte_data(client, reg, val);
     63	if (ret < 0) {
     64		dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
     65				val, reg);
     66		return ret;
     67	}
     68	return 0;
     69}
     70
     71static int __adp5520_ack_bits(struct i2c_client *client, int reg,
     72			      uint8_t bit_mask)
     73{
     74	struct adp5520_chip *chip = i2c_get_clientdata(client);
     75	uint8_t reg_val;
     76	int ret;
     77
     78	mutex_lock(&chip->lock);
     79
     80	ret = __adp5520_read(client, reg, &reg_val);
     81
     82	if (!ret) {
     83		reg_val |= bit_mask;
     84		ret = __adp5520_write(client, reg, reg_val);
     85	}
     86
     87	mutex_unlock(&chip->lock);
     88	return ret;
     89}
     90
     91int adp5520_write(struct device *dev, int reg, uint8_t val)
     92{
     93	return __adp5520_write(to_i2c_client(dev), reg, val);
     94}
     95EXPORT_SYMBOL_GPL(adp5520_write);
     96
     97int adp5520_read(struct device *dev, int reg, uint8_t *val)
     98{
     99	return __adp5520_read(to_i2c_client(dev), reg, val);
    100}
    101EXPORT_SYMBOL_GPL(adp5520_read);
    102
    103int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
    104{
    105	struct adp5520_chip *chip = dev_get_drvdata(dev);
    106	uint8_t reg_val;
    107	int ret;
    108
    109	mutex_lock(&chip->lock);
    110
    111	ret = __adp5520_read(chip->client, reg, &reg_val);
    112
    113	if (!ret && ((reg_val & bit_mask) != bit_mask)) {
    114		reg_val |= bit_mask;
    115		ret = __adp5520_write(chip->client, reg, reg_val);
    116	}
    117
    118	mutex_unlock(&chip->lock);
    119	return ret;
    120}
    121EXPORT_SYMBOL_GPL(adp5520_set_bits);
    122
    123int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
    124{
    125	struct adp5520_chip *chip = dev_get_drvdata(dev);
    126	uint8_t reg_val;
    127	int ret;
    128
    129	mutex_lock(&chip->lock);
    130
    131	ret = __adp5520_read(chip->client, reg, &reg_val);
    132
    133	if (!ret && (reg_val & bit_mask)) {
    134		reg_val &= ~bit_mask;
    135		ret = __adp5520_write(chip->client, reg, reg_val);
    136	}
    137
    138	mutex_unlock(&chip->lock);
    139	return ret;
    140}
    141EXPORT_SYMBOL_GPL(adp5520_clr_bits);
    142
    143int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
    144				unsigned int events)
    145{
    146	struct adp5520_chip *chip = dev_get_drvdata(dev);
    147
    148	if (chip->irq) {
    149		adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
    150			events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
    151			ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
    152
    153		return blocking_notifier_chain_register(&chip->notifier_list,
    154			 nb);
    155	}
    156
    157	return -ENODEV;
    158}
    159EXPORT_SYMBOL_GPL(adp5520_register_notifier);
    160
    161int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
    162				unsigned int events)
    163{
    164	struct adp5520_chip *chip = dev_get_drvdata(dev);
    165
    166	adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
    167		events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
    168		ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
    169
    170	return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
    171}
    172EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
    173
    174static irqreturn_t adp5520_irq_thread(int irq, void *data)
    175{
    176	struct adp5520_chip *chip = data;
    177	unsigned int events;
    178	uint8_t reg_val;
    179	int ret;
    180
    181	ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
    182	if (ret)
    183		goto out;
    184
    185	events =  reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
    186		ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
    187
    188	blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
    189	/* ACK, Sticky bits are W1C */
    190	__adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
    191
    192out:
    193	return IRQ_HANDLED;
    194}
    195
    196static int __remove_subdev(struct device *dev, void *unused)
    197{
    198	platform_device_unregister(to_platform_device(dev));
    199	return 0;
    200}
    201
    202static int adp5520_remove_subdevs(struct adp5520_chip *chip)
    203{
    204	return device_for_each_child(chip->dev, NULL, __remove_subdev);
    205}
    206
    207static int adp5520_probe(struct i2c_client *client,
    208					const struct i2c_device_id *id)
    209{
    210	struct adp5520_platform_data *pdata = dev_get_platdata(&client->dev);
    211	struct platform_device *pdev;
    212	struct adp5520_chip *chip;
    213	int ret;
    214
    215	if (!i2c_check_functionality(client->adapter,
    216					I2C_FUNC_SMBUS_BYTE_DATA)) {
    217		dev_err(&client->dev, "SMBUS Word Data not Supported\n");
    218		return -EIO;
    219	}
    220
    221	if (pdata == NULL) {
    222		dev_err(&client->dev, "missing platform data\n");
    223		return -ENODEV;
    224	}
    225
    226	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
    227	if (!chip)
    228		return -ENOMEM;
    229
    230	i2c_set_clientdata(client, chip);
    231	chip->client = client;
    232
    233	chip->dev = &client->dev;
    234	chip->irq = client->irq;
    235	chip->id = id->driver_data;
    236	mutex_init(&chip->lock);
    237
    238	if (chip->irq) {
    239		BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
    240
    241		ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
    242				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    243				"adp5520", chip);
    244		if (ret) {
    245			dev_err(&client->dev, "failed to request irq %d\n",
    246					chip->irq);
    247			return ret;
    248		}
    249	}
    250
    251	ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
    252	if (ret) {
    253		dev_err(&client->dev, "failed to write\n");
    254		goto out_free_irq;
    255	}
    256
    257	if (pdata->keys) {
    258		pdev = platform_device_register_data(chip->dev, "adp5520-keys",
    259				chip->id, pdata->keys, sizeof(*pdata->keys));
    260		if (IS_ERR(pdev)) {
    261			ret = PTR_ERR(pdev);
    262			goto out_remove_subdevs;
    263		}
    264	}
    265
    266	if (pdata->gpio) {
    267		pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
    268				chip->id, pdata->gpio, sizeof(*pdata->gpio));
    269		if (IS_ERR(pdev)) {
    270			ret = PTR_ERR(pdev);
    271			goto out_remove_subdevs;
    272		}
    273	}
    274
    275	if (pdata->leds) {
    276		pdev = platform_device_register_data(chip->dev, "adp5520-led",
    277				chip->id, pdata->leds, sizeof(*pdata->leds));
    278		if (IS_ERR(pdev)) {
    279			ret = PTR_ERR(pdev);
    280			goto out_remove_subdevs;
    281		}
    282	}
    283
    284	if (pdata->backlight) {
    285		pdev = platform_device_register_data(chip->dev,
    286						"adp5520-backlight",
    287						chip->id,
    288						pdata->backlight,
    289						sizeof(*pdata->backlight));
    290		if (IS_ERR(pdev)) {
    291			ret = PTR_ERR(pdev);
    292			goto out_remove_subdevs;
    293		}
    294	}
    295
    296	return 0;
    297
    298out_remove_subdevs:
    299	adp5520_remove_subdevs(chip);
    300
    301out_free_irq:
    302	if (chip->irq)
    303		free_irq(chip->irq, chip);
    304
    305	return ret;
    306}
    307
    308#ifdef CONFIG_PM_SLEEP
    309static int adp5520_suspend(struct device *dev)
    310{
    311	struct i2c_client *client = to_i2c_client(dev);
    312	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
    313
    314	adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode);
    315	/* All other bits are W1C */
    316	chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY;
    317	adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
    318	return 0;
    319}
    320
    321static int adp5520_resume(struct device *dev)
    322{
    323	struct i2c_client *client = to_i2c_client(dev);
    324	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
    325
    326	adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);
    327	return 0;
    328}
    329#endif
    330
    331static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume);
    332
    333static const struct i2c_device_id adp5520_id[] = {
    334	{ "pmic-adp5520", ID_ADP5520 },
    335	{ "pmic-adp5501", ID_ADP5501 },
    336	{ }
    337};
    338
    339static struct i2c_driver adp5520_driver = {
    340	.driver = {
    341		.name			= "adp5520",
    342		.pm			= &adp5520_pm,
    343		.suppress_bind_attrs	= true,
    344	},
    345	.probe		= adp5520_probe,
    346	.id_table	= adp5520_id,
    347};
    348builtin_i2c_driver(adp5520_driver);