brintos

brintos / linux-shallow public Read only

0
0
Text · 12.9 KiB · e86b39d Raw
566 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Base driver for Dialog Semiconductor DA9030/DA90344 *5 * Copyright (C) 2008 Compulab, Ltd.6 *	Mike Rapoport <mike@compulab.co.il>7 *8 * Copyright (C) 2006-2008 Marvell International Ltd.9 *	Eric Miao <eric.miao@marvell.com>10 */11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/interrupt.h>15#include <linux/platform_device.h>16#include <linux/i2c.h>17#include <linux/mfd/da903x.h>18#include <linux/slab.h>19 20#define DA9030_CHIP_ID		0x0021#define DA9030_EVENT_A		0x0122#define DA9030_EVENT_B		0x0223#define DA9030_EVENT_C		0x0324#define DA9030_STATUS		0x0425#define DA9030_IRQ_MASK_A	0x0526#define DA9030_IRQ_MASK_B	0x0627#define DA9030_IRQ_MASK_C	0x0728#define DA9030_SYS_CTRL_A	0x0829#define DA9030_SYS_CTRL_B	0x0930#define DA9030_FAULT_LOG	0x0a31 32#define DA9034_CHIP_ID		0x0033#define DA9034_EVENT_A		0x0134#define DA9034_EVENT_B		0x0235#define DA9034_EVENT_C		0x0336#define DA9034_EVENT_D		0x0437#define DA9034_STATUS_A		0x0538#define DA9034_STATUS_B		0x0639#define DA9034_IRQ_MASK_A	0x0740#define DA9034_IRQ_MASK_B	0x0841#define DA9034_IRQ_MASK_C	0x0942#define DA9034_IRQ_MASK_D	0x0a43#define DA9034_SYS_CTRL_A	0x0b44#define DA9034_SYS_CTRL_B	0x0c45#define DA9034_FAULT_LOG	0x0d46 47struct da903x_chip;48 49struct da903x_chip_ops {50	int	(*init_chip)(struct da903x_chip *);51	int	(*unmask_events)(struct da903x_chip *, unsigned int events);52	int	(*mask_events)(struct da903x_chip *, unsigned int events);53	int	(*read_events)(struct da903x_chip *, unsigned int *events);54	int	(*read_status)(struct da903x_chip *, unsigned int *status);55};56 57struct da903x_chip {58	struct i2c_client	*client;59	struct device		*dev;60	const struct da903x_chip_ops *ops;61 62	int			type;63	uint32_t		events_mask;64 65	struct mutex		lock;66	struct work_struct	irq_work;67 68	struct blocking_notifier_head notifier_list;69};70 71static inline int __da903x_read(struct i2c_client *client,72				int reg, uint8_t *val)73{74	int ret;75 76	ret = i2c_smbus_read_byte_data(client, reg);77	if (ret < 0) {78		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);79		return ret;80	}81 82	*val = (uint8_t)ret;83	return 0;84}85 86static inline int __da903x_reads(struct i2c_client *client, int reg,87				 int len, uint8_t *val)88{89	int ret;90 91	ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);92	if (ret < 0) {93		dev_err(&client->dev, "failed reading from 0x%02x\n", reg);94		return ret;95	}96	return 0;97}98 99static inline int __da903x_write(struct i2c_client *client,100				 int reg, uint8_t val)101{102	int ret;103 104	ret = i2c_smbus_write_byte_data(client, reg, val);105	if (ret < 0) {106		dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",107				val, reg);108		return ret;109	}110	return 0;111}112 113static inline int __da903x_writes(struct i2c_client *client, int reg,114				  int len, uint8_t *val)115{116	int ret;117 118	ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);119	if (ret < 0) {120		dev_err(&client->dev, "failed writings to 0x%02x\n", reg);121		return ret;122	}123	return 0;124}125 126int da903x_register_notifier(struct device *dev, struct notifier_block *nb,127				unsigned int events)128{129	struct da903x_chip *chip = dev_get_drvdata(dev);130 131	chip->ops->unmask_events(chip, events);132	return blocking_notifier_chain_register(&chip->notifier_list, nb);133}134EXPORT_SYMBOL_GPL(da903x_register_notifier);135 136int da903x_unregister_notifier(struct device *dev, struct notifier_block *nb,137				unsigned int events)138{139	struct da903x_chip *chip = dev_get_drvdata(dev);140 141	chip->ops->mask_events(chip, events);142	return blocking_notifier_chain_unregister(&chip->notifier_list, nb);143}144EXPORT_SYMBOL_GPL(da903x_unregister_notifier);145 146int da903x_write(struct device *dev, int reg, uint8_t val)147{148	return __da903x_write(to_i2c_client(dev), reg, val);149}150EXPORT_SYMBOL_GPL(da903x_write);151 152int da903x_writes(struct device *dev, int reg, int len, uint8_t *val)153{154	return __da903x_writes(to_i2c_client(dev), reg, len, val);155}156EXPORT_SYMBOL_GPL(da903x_writes);157 158int da903x_read(struct device *dev, int reg, uint8_t *val)159{160	return __da903x_read(to_i2c_client(dev), reg, val);161}162EXPORT_SYMBOL_GPL(da903x_read);163 164int da903x_reads(struct device *dev, int reg, int len, uint8_t *val)165{166	return __da903x_reads(to_i2c_client(dev), reg, len, val);167}168EXPORT_SYMBOL_GPL(da903x_reads);169 170int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask)171{172	struct da903x_chip *chip = dev_get_drvdata(dev);173	uint8_t reg_val;174	int ret = 0;175 176	mutex_lock(&chip->lock);177 178	ret = __da903x_read(chip->client, reg, &reg_val);179	if (ret)180		goto out;181 182	if ((reg_val & bit_mask) != bit_mask) {183		reg_val |= bit_mask;184		ret = __da903x_write(chip->client, reg, reg_val);185	}186out:187	mutex_unlock(&chip->lock);188	return ret;189}190EXPORT_SYMBOL_GPL(da903x_set_bits);191 192int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)193{194	struct da903x_chip *chip = dev_get_drvdata(dev);195	uint8_t reg_val;196	int ret = 0;197 198	mutex_lock(&chip->lock);199 200	ret = __da903x_read(chip->client, reg, &reg_val);201	if (ret)202		goto out;203 204	if (reg_val & bit_mask) {205		reg_val &= ~bit_mask;206		ret = __da903x_write(chip->client, reg, reg_val);207	}208out:209	mutex_unlock(&chip->lock);210	return ret;211}212EXPORT_SYMBOL_GPL(da903x_clr_bits);213 214int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)215{216	struct da903x_chip *chip = dev_get_drvdata(dev);217	uint8_t reg_val;218	int ret = 0;219 220	mutex_lock(&chip->lock);221 222	ret = __da903x_read(chip->client, reg, &reg_val);223	if (ret)224		goto out;225 226	if ((reg_val & mask) != val) {227		reg_val = (reg_val & ~mask) | val;228		ret = __da903x_write(chip->client, reg, reg_val);229	}230out:231	mutex_unlock(&chip->lock);232	return ret;233}234EXPORT_SYMBOL_GPL(da903x_update);235 236int da903x_query_status(struct device *dev, unsigned int sbits)237{238	struct da903x_chip *chip = dev_get_drvdata(dev);239	unsigned int status = 0;240 241	chip->ops->read_status(chip, &status);242	return ((status & sbits) == sbits);243}244EXPORT_SYMBOL(da903x_query_status);245 246static int da9030_init_chip(struct da903x_chip *chip)247{248	uint8_t chip_id;249	int err;250 251	err = __da903x_read(chip->client, DA9030_CHIP_ID, &chip_id);252	if (err)253		return err;254 255	err = __da903x_write(chip->client, DA9030_SYS_CTRL_A, 0xE8);256	if (err)257		return err;258 259	dev_info(chip->dev, "DA9030 (CHIP ID: 0x%02x) detected\n", chip_id);260	return 0;261}262 263static int da9030_unmask_events(struct da903x_chip *chip, unsigned int events)264{265	uint8_t v[3];266 267	chip->events_mask &= ~events;268 269	v[0] = (chip->events_mask & 0xff);270	v[1] = (chip->events_mask >> 8) & 0xff;271	v[2] = (chip->events_mask >> 16) & 0xff;272 273	return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);274}275 276static int da9030_mask_events(struct da903x_chip *chip, unsigned int events)277{278	uint8_t v[3];279 280	chip->events_mask |= events;281 282	v[0] = (chip->events_mask & 0xff);283	v[1] = (chip->events_mask >> 8) & 0xff;284	v[2] = (chip->events_mask >> 16) & 0xff;285 286	return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);287}288 289static int da9030_read_events(struct da903x_chip *chip, unsigned int *events)290{291	uint8_t v[3] = {0, 0, 0};292	int ret;293 294	ret = __da903x_reads(chip->client, DA9030_EVENT_A, 3, v);295	if (ret < 0)296		return ret;297 298	*events = (v[2] << 16) | (v[1] << 8) | v[0];299	return 0;300}301 302static int da9030_read_status(struct da903x_chip *chip, unsigned int *status)303{304	return __da903x_read(chip->client, DA9030_STATUS, (uint8_t *)status);305}306 307static int da9034_init_chip(struct da903x_chip *chip)308{309	uint8_t chip_id;310	int err;311 312	err = __da903x_read(chip->client, DA9034_CHIP_ID, &chip_id);313	if (err)314		return err;315 316	err = __da903x_write(chip->client, DA9034_SYS_CTRL_A, 0xE8);317	if (err)318		return err;319 320	/* avoid SRAM power off during sleep*/321	__da903x_write(chip->client, 0x10, 0x07);322	__da903x_write(chip->client, 0x11, 0xff);323	__da903x_write(chip->client, 0x12, 0xff);324 325	/* Enable the ONKEY power down functionality */326	__da903x_write(chip->client, DA9034_SYS_CTRL_B, 0x20);327	__da903x_write(chip->client, DA9034_SYS_CTRL_A, 0x60);328 329	/* workaround to make LEDs work */330	__da903x_write(chip->client, 0x90, 0x01);331	__da903x_write(chip->client, 0xB0, 0x08);332 333	/* make ADTV1 and SDTV1 effective */334	__da903x_write(chip->client, 0x20, 0x00);335 336	dev_info(chip->dev, "DA9034 (CHIP ID: 0x%02x) detected\n", chip_id);337	return 0;338}339 340static int da9034_unmask_events(struct da903x_chip *chip, unsigned int events)341{342	uint8_t v[4];343 344	chip->events_mask &= ~events;345 346	v[0] = (chip->events_mask & 0xff);347	v[1] = (chip->events_mask >> 8) & 0xff;348	v[2] = (chip->events_mask >> 16) & 0xff;349	v[3] = (chip->events_mask >> 24) & 0xff;350 351	return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);352}353 354static int da9034_mask_events(struct da903x_chip *chip, unsigned int events)355{356	uint8_t v[4];357 358	chip->events_mask |= events;359 360	v[0] = (chip->events_mask & 0xff);361	v[1] = (chip->events_mask >> 8) & 0xff;362	v[2] = (chip->events_mask >> 16) & 0xff;363	v[3] = (chip->events_mask >> 24) & 0xff;364 365	return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);366}367 368static int da9034_read_events(struct da903x_chip *chip, unsigned int *events)369{370	uint8_t v[4] = {0, 0, 0, 0};371	int ret;372 373	ret = __da903x_reads(chip->client, DA9034_EVENT_A, 4, v);374	if (ret < 0)375		return ret;376 377	*events = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0];378	return 0;379}380 381static int da9034_read_status(struct da903x_chip *chip, unsigned int *status)382{383	uint8_t v[2] = {0, 0};384	int ret = 0;385 386	ret = __da903x_reads(chip->client, DA9034_STATUS_A, 2, v);387	if (ret)388		return ret;389 390	*status = (v[1] << 8) | v[0];391	return 0;392}393 394static void da903x_irq_work(struct work_struct *work)395{396	struct da903x_chip *chip =397		container_of(work, struct da903x_chip, irq_work);398	unsigned int events = 0;399 400	while (1) {401		if (chip->ops->read_events(chip, &events))402			break;403 404		events &= ~chip->events_mask;405		if (events == 0)406			break;407 408		blocking_notifier_call_chain(409				&chip->notifier_list, events, NULL);410	}411	enable_irq(chip->client->irq);412}413 414static irqreturn_t da903x_irq_handler(int irq, void *data)415{416	struct da903x_chip *chip = data;417 418	disable_irq_nosync(irq);419	(void)schedule_work(&chip->irq_work);420 421	return IRQ_HANDLED;422}423 424static const struct da903x_chip_ops da903x_ops[] = {425	[0] = {426		.init_chip	= da9030_init_chip,427		.unmask_events	= da9030_unmask_events,428		.mask_events	= da9030_mask_events,429		.read_events	= da9030_read_events,430		.read_status	= da9030_read_status,431	},432	[1] = {433		.init_chip	= da9034_init_chip,434		.unmask_events	= da9034_unmask_events,435		.mask_events	= da9034_mask_events,436		.read_events	= da9034_read_events,437		.read_status	= da9034_read_status,438	}439};440 441static const struct i2c_device_id da903x_id_table[] = {442	{ "da9030", 0 },443	{ "da9034", 1 },444	{ },445};446MODULE_DEVICE_TABLE(i2c, da903x_id_table);447 448static int __remove_subdev(struct device *dev, void *unused)449{450	platform_device_unregister(to_platform_device(dev));451	return 0;452}453 454static int da903x_remove_subdevs(struct da903x_chip *chip)455{456	return device_for_each_child(chip->dev, NULL, __remove_subdev);457}458 459static int da903x_add_subdevs(struct da903x_chip *chip,460					struct da903x_platform_data *pdata)461{462	struct da903x_subdev_info *subdev;463	struct platform_device *pdev;464	int i, ret = 0;465 466	for (i = 0; i < pdata->num_subdevs; i++) {467		subdev = &pdata->subdevs[i];468 469		pdev = platform_device_alloc(subdev->name, subdev->id);470		if (!pdev) {471			ret = -ENOMEM;472			goto failed;473		}474 475		pdev->dev.parent = chip->dev;476		pdev->dev.platform_data = subdev->platform_data;477 478		ret = platform_device_add(pdev);479		if (ret) {480			platform_device_put(pdev);481			goto failed;482		}483	}484	return 0;485 486failed:487	da903x_remove_subdevs(chip);488	return ret;489}490 491static int da903x_probe(struct i2c_client *client)492{493	const struct i2c_device_id *id = i2c_client_get_device_id(client);494	struct da903x_platform_data *pdata = dev_get_platdata(&client->dev);495	struct da903x_chip *chip;496	unsigned int tmp;497	int ret;498 499	chip = devm_kzalloc(&client->dev, sizeof(struct da903x_chip),500				GFP_KERNEL);501	if (chip == NULL)502		return -ENOMEM;503 504	chip->client = client;505	chip->dev = &client->dev;506	chip->ops = &da903x_ops[id->driver_data];507 508	mutex_init(&chip->lock);509	INIT_WORK(&chip->irq_work, da903x_irq_work);510	BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);511 512	i2c_set_clientdata(client, chip);513 514	ret = chip->ops->init_chip(chip);515	if (ret)516		return ret;517 518	/* mask and clear all IRQs */519	chip->events_mask = 0xffffffff;520	chip->ops->mask_events(chip, chip->events_mask);521	chip->ops->read_events(chip, &tmp);522 523	ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler,524			IRQF_TRIGGER_FALLING,525			"da903x", chip);526	if (ret) {527		dev_err(&client->dev, "failed to request irq %d\n",528				client->irq);529		return ret;530	}531 532	return da903x_add_subdevs(chip, pdata);533}534 535static void da903x_remove(struct i2c_client *client)536{537	struct da903x_chip *chip = i2c_get_clientdata(client);538 539	da903x_remove_subdevs(chip);540}541 542static struct i2c_driver da903x_driver = {543	.driver	= {544		.name	= "da903x",545	},546	.probe		= da903x_probe,547	.remove		= da903x_remove,548	.id_table	= da903x_id_table,549};550 551static int __init da903x_init(void)552{553	return i2c_add_driver(&da903x_driver);554}555subsys_initcall(da903x_init);556 557static void __exit da903x_exit(void)558{559	i2c_del_driver(&da903x_driver);560}561module_exit(da903x_exit);562 563MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034");564MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");565MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");566