brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · bd6f496 Raw
347 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs4 * LCD Backlight: drivers/video/backlight/adp5520_bl5 * LEDs		: drivers/led/leds-adp55206 * 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{209	const struct i2c_device_id *id = i2c_client_get_device_id(client);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 308static int adp5520_suspend(struct device *dev)309{310	struct i2c_client *client = to_i2c_client(dev);311	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);312 313	adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode);314	/* All other bits are W1C */315	chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY;316	adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);317	return 0;318}319 320static int adp5520_resume(struct device *dev)321{322	struct i2c_client *client = to_i2c_client(dev);323	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);324 325	adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);326	return 0;327}328 329static DEFINE_SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume);330 331static const struct i2c_device_id adp5520_id[] = {332	{ "pmic-adp5520", ID_ADP5520 },333	{ "pmic-adp5501", ID_ADP5501 },334	{ }335};336 337static struct i2c_driver adp5520_driver = {338	.driver = {339		.name			= "adp5520",340		.pm			= pm_sleep_ptr(&adp5520_pm),341		.suppress_bind_attrs	= true,342	},343	.probe		= adp5520_probe,344	.id_table	= adp5520_id,345};346builtin_i2c_driver(adp5520_driver);347