brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · 8ef510e Raw
461 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/mfd/aat2870-core.c4 *5 * Copyright (c) 2011, NVIDIA Corporation.6 * Author: Jin Park <jinyoungp@nvidia.com>7 */8 9#include <linux/kernel.h>10#include <linux/init.h>11#include <linux/debugfs.h>12#include <linux/slab.h>13#include <linux/uaccess.h>14#include <linux/i2c.h>15#include <linux/delay.h>16#include <linux/gpio.h>17#include <linux/mfd/core.h>18#include <linux/mfd/aat2870.h>19#include <linux/regulator/machine.h>20 21static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {22	/* readable, writeable, value */23	{ 0, 1, 0x00 },	/* 0x00 AAT2870_BL_CH_EN */24	{ 0, 1, 0x16 },	/* 0x01 AAT2870_BLM */25	{ 0, 1, 0x16 },	/* 0x02 AAT2870_BLS */26	{ 0, 1, 0x56 },	/* 0x03 AAT2870_BL1 */27	{ 0, 1, 0x56 },	/* 0x04 AAT2870_BL2 */28	{ 0, 1, 0x56 },	/* 0x05 AAT2870_BL3 */29	{ 0, 1, 0x56 },	/* 0x06 AAT2870_BL4 */30	{ 0, 1, 0x56 },	/* 0x07 AAT2870_BL5 */31	{ 0, 1, 0x56 },	/* 0x08 AAT2870_BL6 */32	{ 0, 1, 0x56 },	/* 0x09 AAT2870_BL7 */33	{ 0, 1, 0x56 },	/* 0x0A AAT2870_BL8 */34	{ 0, 1, 0x00 },	/* 0x0B AAT2870_FLR */35	{ 0, 1, 0x03 },	/* 0x0C AAT2870_FM */36	{ 0, 1, 0x03 },	/* 0x0D AAT2870_FS */37	{ 0, 1, 0x10 },	/* 0x0E AAT2870_ALS_CFG0 */38	{ 0, 1, 0x06 },	/* 0x0F AAT2870_ALS_CFG1 */39	{ 0, 1, 0x00 },	/* 0x10 AAT2870_ALS_CFG2 */40	{ 1, 0, 0x00 },	/* 0x11 AAT2870_AMB */41	{ 0, 1, 0x00 },	/* 0x12 AAT2870_ALS0 */42	{ 0, 1, 0x00 },	/* 0x13 AAT2870_ALS1 */43	{ 0, 1, 0x00 },	/* 0x14 AAT2870_ALS2 */44	{ 0, 1, 0x00 },	/* 0x15 AAT2870_ALS3 */45	{ 0, 1, 0x00 },	/* 0x16 AAT2870_ALS4 */46	{ 0, 1, 0x00 },	/* 0x17 AAT2870_ALS5 */47	{ 0, 1, 0x00 },	/* 0x18 AAT2870_ALS6 */48	{ 0, 1, 0x00 },	/* 0x19 AAT2870_ALS7 */49	{ 0, 1, 0x00 },	/* 0x1A AAT2870_ALS8 */50	{ 0, 1, 0x00 },	/* 0x1B AAT2870_ALS9 */51	{ 0, 1, 0x00 },	/* 0x1C AAT2870_ALSA */52	{ 0, 1, 0x00 },	/* 0x1D AAT2870_ALSB */53	{ 0, 1, 0x00 },	/* 0x1E AAT2870_ALSC */54	{ 0, 1, 0x00 },	/* 0x1F AAT2870_ALSD */55	{ 0, 1, 0x00 },	/* 0x20 AAT2870_ALSE */56	{ 0, 1, 0x00 },	/* 0x21 AAT2870_ALSF */57	{ 0, 1, 0x00 },	/* 0x22 AAT2870_SUB_SET */58	{ 0, 1, 0x00 },	/* 0x23 AAT2870_SUB_CTRL */59	{ 0, 1, 0x00 },	/* 0x24 AAT2870_LDO_AB */60	{ 0, 1, 0x00 },	/* 0x25 AAT2870_LDO_CD */61	{ 0, 1, 0x00 },	/* 0x26 AAT2870_LDO_EN */62};63 64static struct mfd_cell aat2870_devs[] = {65	{66		.name = "aat2870-backlight",67		.id = AAT2870_ID_BL,68		.pdata_size = sizeof(struct aat2870_bl_platform_data),69	},70	{71		.name = "aat2870-regulator",72		.id = AAT2870_ID_LDOA,73		.pdata_size = sizeof(struct regulator_init_data),74	},75	{76		.name = "aat2870-regulator",77		.id = AAT2870_ID_LDOB,78		.pdata_size = sizeof(struct regulator_init_data),79	},80	{81		.name = "aat2870-regulator",82		.id = AAT2870_ID_LDOC,83		.pdata_size = sizeof(struct regulator_init_data),84	},85	{86		.name = "aat2870-regulator",87		.id = AAT2870_ID_LDOD,88		.pdata_size = sizeof(struct regulator_init_data),89	},90};91 92static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)93{94	int ret;95 96	if (addr >= AAT2870_REG_NUM) {97		dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);98		return -EINVAL;99	}100 101	if (!aat2870->reg_cache[addr].readable) {102		*val = aat2870->reg_cache[addr].value;103		goto out;104	}105 106	ret = i2c_master_send(aat2870->client, &addr, 1);107	if (ret < 0)108		return ret;109	if (ret != 1)110		return -EIO;111 112	ret = i2c_master_recv(aat2870->client, val, 1);113	if (ret < 0)114		return ret;115	if (ret != 1)116		return -EIO;117 118out:119	dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);120	return 0;121}122 123static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)124{125	u8 msg[2];126	int ret;127 128	if (addr >= AAT2870_REG_NUM) {129		dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);130		return -EINVAL;131	}132 133	if (!aat2870->reg_cache[addr].writeable) {134		dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",135			addr);136		return -EINVAL;137	}138 139	msg[0] = addr;140	msg[1] = val;141	ret = i2c_master_send(aat2870->client, msg, 2);142	if (ret < 0)143		return ret;144	if (ret != 2)145		return -EIO;146 147	aat2870->reg_cache[addr].value = val;148 149	dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);150	return 0;151}152 153static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)154{155	int ret;156 157	mutex_lock(&aat2870->io_lock);158	ret = __aat2870_read(aat2870, addr, val);159	mutex_unlock(&aat2870->io_lock);160 161	return ret;162}163 164static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)165{166	int ret;167 168	mutex_lock(&aat2870->io_lock);169	ret = __aat2870_write(aat2870, addr, val);170	mutex_unlock(&aat2870->io_lock);171 172	return ret;173}174 175static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,176			  u8 val)177{178	int change;179	u8 old_val, new_val;180	int ret;181 182	mutex_lock(&aat2870->io_lock);183 184	ret = __aat2870_read(aat2870, addr, &old_val);185	if (ret)186		goto out_unlock;187 188	new_val = (old_val & ~mask) | (val & mask);189	change = old_val != new_val;190	if (change)191		ret = __aat2870_write(aat2870, addr, new_val);192 193out_unlock:194	mutex_unlock(&aat2870->io_lock);195 196	return ret;197}198 199static inline void aat2870_enable(struct aat2870_data *aat2870)200{201	if (aat2870->en_pin >= 0)202		gpio_set_value(aat2870->en_pin, 1);203 204	aat2870->is_enable = 1;205}206 207static inline void aat2870_disable(struct aat2870_data *aat2870)208{209	if (aat2870->en_pin >= 0)210		gpio_set_value(aat2870->en_pin, 0);211 212	aat2870->is_enable = 0;213}214 215#ifdef CONFIG_DEBUG_FS216static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)217{218	u8 addr, val;219	ssize_t count = 0;220	int ret;221 222	count += sprintf(buf, "aat2870 registers\n");223	for (addr = 0; addr < AAT2870_REG_NUM; addr++) {224		count += snprintf(buf + count, PAGE_SIZE - count, "0x%02x: ", addr);225		if (count >= PAGE_SIZE - 1)226			break;227 228		ret = aat2870->read(aat2870, addr, &val);229		if (ret == 0)230			count += snprintf(buf + count, PAGE_SIZE - count,231					  "0x%02x", val);232		else233			count += snprintf(buf + count, PAGE_SIZE - count,234					  "<read fail: %d>", ret);235 236		if (count >= PAGE_SIZE - 1)237			break;238 239		count += snprintf(buf + count, PAGE_SIZE - count, "\n");240		if (count >= PAGE_SIZE - 1)241			break;242	}243 244	/* Truncate count; min() would cause a warning */245	if (count >= PAGE_SIZE)246		count = PAGE_SIZE - 1;247 248	return count;249}250 251static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,252				     size_t count, loff_t *ppos)253{254	struct aat2870_data *aat2870 = file->private_data;255	char *buf;256	ssize_t ret;257 258	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);259	if (!buf)260		return -ENOMEM;261 262	ret = aat2870_dump_reg(aat2870, buf);263	if (ret >= 0)264		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);265 266	kfree(buf);267 268	return ret;269}270 271static ssize_t aat2870_reg_write_file(struct file *file,272				      const char __user *user_buf, size_t count,273				      loff_t *ppos)274{275	struct aat2870_data *aat2870 = file->private_data;276	char buf[32];277	ssize_t buf_size;278	char *start = buf;279	unsigned long addr, val;280	int ret;281 282	buf_size = min(count, (size_t)(sizeof(buf)-1));283	if (copy_from_user(buf, user_buf, buf_size)) {284		dev_err(aat2870->dev, "Failed to copy from user\n");285		return -EFAULT;286	}287	buf[buf_size] = 0;288 289	while (*start == ' ')290		start++;291 292	ret = kstrtoul(start, 16, &addr);293	if (ret)294		return ret;295 296	if (addr >= AAT2870_REG_NUM) {297		dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);298		return -EINVAL;299	}300 301	while (*start == ' ')302		start++;303 304	ret = kstrtoul(start, 16, &val);305	if (ret)306		return ret;307 308	ret = aat2870->write(aat2870, (u8)addr, (u8)val);309	if (ret)310		return ret;311 312	return buf_size;313}314 315static const struct file_operations aat2870_reg_fops = {316	.open = simple_open,317	.read = aat2870_reg_read_file,318	.write = aat2870_reg_write_file,319};320 321static void aat2870_init_debugfs(struct aat2870_data *aat2870)322{323	aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);324 325	debugfs_create_file("regs", 0644, aat2870->dentry_root, aat2870,326			    &aat2870_reg_fops);327}328 329#else330static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)331{332}333#endif /* CONFIG_DEBUG_FS */334 335static int aat2870_i2c_probe(struct i2c_client *client)336{337	struct aat2870_platform_data *pdata = dev_get_platdata(&client->dev);338	struct aat2870_data *aat2870;339	int i, j;340	int ret = 0;341 342	aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data),343				GFP_KERNEL);344	if (!aat2870)345		return -ENOMEM;346 347	aat2870->dev = &client->dev;348	aat2870->client = client;349	i2c_set_clientdata(client, aat2870);350 351	aat2870->reg_cache = aat2870_regs;352 353	if (pdata->en_pin < 0)354		aat2870->en_pin = -1;355	else356		aat2870->en_pin = pdata->en_pin;357 358	aat2870->init = pdata->init;359	aat2870->uninit = pdata->uninit;360	aat2870->read = aat2870_read;361	aat2870->write = aat2870_write;362	aat2870->update = aat2870_update;363 364	mutex_init(&aat2870->io_lock);365 366	if (aat2870->init)367		aat2870->init(aat2870);368 369	if (aat2870->en_pin >= 0) {370		ret = devm_gpio_request_one(&client->dev, aat2870->en_pin,371					GPIOF_OUT_INIT_HIGH, "aat2870-en");372		if (ret < 0) {373			dev_err(&client->dev,374				"Failed to request GPIO %d\n", aat2870->en_pin);375			return ret;376		}377	}378 379	aat2870_enable(aat2870);380 381	for (i = 0; i < pdata->num_subdevs; i++) {382		for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {383			if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&384					!strcmp(pdata->subdevs[i].name,385						aat2870_devs[j].name)) {386				aat2870_devs[j].platform_data =387					pdata->subdevs[i].platform_data;388				break;389			}390		}391	}392 393	ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,394			      ARRAY_SIZE(aat2870_devs), NULL, 0, NULL);395	if (ret != 0) {396		dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);397		goto out_disable;398	}399 400	aat2870_init_debugfs(aat2870);401 402	return 0;403 404out_disable:405	aat2870_disable(aat2870);406	return ret;407}408 409static int aat2870_i2c_suspend(struct device *dev)410{411	struct i2c_client *client = to_i2c_client(dev);412	struct aat2870_data *aat2870 = i2c_get_clientdata(client);413 414	aat2870_disable(aat2870);415 416	return 0;417}418 419static int aat2870_i2c_resume(struct device *dev)420{421	struct i2c_client *client = to_i2c_client(dev);422	struct aat2870_data *aat2870 = i2c_get_clientdata(client);423	struct aat2870_register *reg = NULL;424	int i;425 426	aat2870_enable(aat2870);427 428	/* restore registers */429	for (i = 0; i < AAT2870_REG_NUM; i++) {430		reg = &aat2870->reg_cache[i];431		if (reg->writeable)432			aat2870->write(aat2870, i, reg->value);433	}434 435	return 0;436}437 438static DEFINE_SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,439				aat2870_i2c_resume);440 441static const struct i2c_device_id aat2870_i2c_id_table[] = {442	{ "aat2870" },443	{ }444};445 446static struct i2c_driver aat2870_i2c_driver = {447	.driver = {448		.name			= "aat2870",449		.pm			= pm_sleep_ptr(&aat2870_pm_ops),450		.suppress_bind_attrs	= true,451	},452	.probe		= aat2870_i2c_probe,453	.id_table	= aat2870_i2c_id_table,454};455 456static int __init aat2870_init(void)457{458	return i2c_add_driver(&aat2870_i2c_driver);459}460subsys_initcall(aat2870_init);461