Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * AS3711 PMIC MFC driver
  3 *
  4 * Copyright (C) 2012 Renesas Electronics Corporation
  5 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of the version 2 of the GNU General Public License as
  9 * published by the Free Software Foundation
 10 */
 11
 12#include <linux/device.h>
 13#include <linux/err.h>
 14#include <linux/i2c.h>
 15#include <linux/init.h>
 16#include <linux/kernel.h>
 17#include <linux/mfd/as3711.h>
 18#include <linux/mfd/core.h>
 19#include <linux/module.h>
 20#include <linux/of.h>
 21#include <linux/regmap.h>
 22#include <linux/slab.h>
 23
 24enum {
 25	AS3711_REGULATOR,
 26	AS3711_BACKLIGHT,
 27};
 28
 29/*
 30 * Ok to have it static: it is only used during probing and multiple I2C devices
 31 * cannot be probed simultaneously. Just make sure to avoid stale data.
 32 */
 33static struct mfd_cell as3711_subdevs[] = {
 34	[AS3711_REGULATOR] = {.name = "as3711-regulator",},
 35	[AS3711_BACKLIGHT] = {.name = "as3711-backlight",},
 36};
 37
 38static bool as3711_volatile_reg(struct device *dev, unsigned int reg)
 39{
 40	switch (reg) {
 41	case AS3711_GPIO_SIGNAL_IN:
 42	case AS3711_INTERRUPT_STATUS_1:
 43	case AS3711_INTERRUPT_STATUS_2:
 44	case AS3711_INTERRUPT_STATUS_3:
 45	case AS3711_CHARGER_STATUS_1:
 46	case AS3711_CHARGER_STATUS_2:
 47	case AS3711_REG_STATUS:
 48		return true;
 49	}
 50	return false;
 51}
 52
 53static bool as3711_precious_reg(struct device *dev, unsigned int reg)
 54{
 55	switch (reg) {
 56	case AS3711_INTERRUPT_STATUS_1:
 57	case AS3711_INTERRUPT_STATUS_2:
 58	case AS3711_INTERRUPT_STATUS_3:
 59		return true;
 60	}
 61	return false;
 62}
 63
 64static bool as3711_readable_reg(struct device *dev, unsigned int reg)
 65{
 66	switch (reg) {
 67	case AS3711_SD_1_VOLTAGE:
 68	case AS3711_SD_2_VOLTAGE:
 69	case AS3711_SD_3_VOLTAGE:
 70	case AS3711_SD_4_VOLTAGE:
 71	case AS3711_LDO_1_VOLTAGE:
 72	case AS3711_LDO_2_VOLTAGE:
 73	case AS3711_LDO_3_VOLTAGE:
 74	case AS3711_LDO_4_VOLTAGE:
 75	case AS3711_LDO_5_VOLTAGE:
 76	case AS3711_LDO_6_VOLTAGE:
 77	case AS3711_LDO_7_VOLTAGE:
 78	case AS3711_LDO_8_VOLTAGE:
 79	case AS3711_SD_CONTROL:
 80	case AS3711_GPIO_SIGNAL_OUT:
 81	case AS3711_GPIO_SIGNAL_IN:
 82	case AS3711_SD_CONTROL_1:
 83	case AS3711_SD_CONTROL_2:
 84	case AS3711_CURR_CONTROL:
 85	case AS3711_CURR1_VALUE:
 86	case AS3711_CURR2_VALUE:
 87	case AS3711_CURR3_VALUE:
 88	case AS3711_STEPUP_CONTROL_1:
 89	case AS3711_STEPUP_CONTROL_2:
 90	case AS3711_STEPUP_CONTROL_4:
 91	case AS3711_STEPUP_CONTROL_5:
 92	case AS3711_REG_STATUS:
 93	case AS3711_INTERRUPT_STATUS_1:
 94	case AS3711_INTERRUPT_STATUS_2:
 95	case AS3711_INTERRUPT_STATUS_3:
 96	case AS3711_CHARGER_STATUS_1:
 97	case AS3711_CHARGER_STATUS_2:
 98	case AS3711_ASIC_ID_1:
 99	case AS3711_ASIC_ID_2:
100		return true;
101	}
102	return false;
103}
104
105static const struct regmap_config as3711_regmap_config = {
106	.reg_bits = 8,
107	.val_bits = 8,
108	.volatile_reg = as3711_volatile_reg,
109	.readable_reg = as3711_readable_reg,
110	.precious_reg = as3711_precious_reg,
111	.max_register = AS3711_MAX_REG,
112	.num_reg_defaults_raw = AS3711_NUM_REGS,
113	.cache_type = REGCACHE_RBTREE,
114};
115
116#ifdef CONFIG_OF
117static const struct of_device_id as3711_of_match[] = {
118	{.compatible = "ams,as3711",},
119	{}
120};
121MODULE_DEVICE_TABLE(of, as3711_of_match);
122#endif
123
124static int as3711_i2c_probe(struct i2c_client *client,
125			    const struct i2c_device_id *id)
126{
127	struct as3711 *as3711;
128	struct as3711_platform_data *pdata;
129	unsigned int id1, id2;
130	int ret;
131
132	if (!client->dev.of_node) {
133		pdata = dev_get_platdata(&client->dev);
134		if (!pdata)
135			dev_dbg(&client->dev, "Platform data not found\n");
136	} else {
137		pdata = devm_kzalloc(&client->dev,
138				     sizeof(*pdata), GFP_KERNEL);
139		if (!pdata)
140			return -ENOMEM;
141	}
142
143	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
144	if (!as3711)
145		return -ENOMEM;
146
147	as3711->dev = &client->dev;
148	i2c_set_clientdata(client, as3711);
149
150	if (client->irq)
151		dev_notice(&client->dev, "IRQ not supported yet\n");
152
153	as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
154	if (IS_ERR(as3711->regmap)) {
155		ret = PTR_ERR(as3711->regmap);
156		dev_err(&client->dev,
157			"regmap initialization failed: %d\n", ret);
158		return ret;
159	}
160
161	ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1);
162	if (!ret)
163		ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2);
164	if (ret < 0) {
165		dev_err(&client->dev, "regmap_read() failed: %d\n", ret);
166		return ret;
167	}
168	if (id1 != 0x8b)
169		return -ENODEV;
170	dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
171
172	/*
173	 * We can reuse as3711_subdevs[],
174	 * it will be copied in mfd_add_devices()
175	 */
176	if (pdata) {
177		as3711_subdevs[AS3711_REGULATOR].platform_data =
178			&pdata->regulator;
179		as3711_subdevs[AS3711_REGULATOR].pdata_size =
180			sizeof(pdata->regulator);
181		as3711_subdevs[AS3711_BACKLIGHT].platform_data =
182			&pdata->backlight;
183		as3711_subdevs[AS3711_BACKLIGHT].pdata_size =
184			sizeof(pdata->backlight);
185	} else {
186		as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
187		as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
188		as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL;
189		as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0;
190	}
191
192	ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs,
193			      ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL);
194	if (ret < 0)
195		dev_err(&client->dev, "add mfd devices failed: %d\n", ret);
196
197	return ret;
198}
199
200static int as3711_i2c_remove(struct i2c_client *client)
201{
202	struct as3711 *as3711 = i2c_get_clientdata(client);
203
204	mfd_remove_devices(as3711->dev);
205	return 0;
206}
207
208static const struct i2c_device_id as3711_i2c_id[] = {
209	{.name = "as3711", .driver_data = 0},
210	{}
211};
212
213MODULE_DEVICE_TABLE(i2c, as3711_i2c_id);
214
215static struct i2c_driver as3711_i2c_driver = {
216	.driver = {
217		   .name = "as3711",
218		   .of_match_table = of_match_ptr(as3711_of_match),
219	},
220	.probe = as3711_i2c_probe,
221	.remove = as3711_i2c_remove,
222	.id_table = as3711_i2c_id,
223};
224
225static int __init as3711_i2c_init(void)
226{
227	return i2c_add_driver(&as3711_i2c_driver);
228}
229/* Initialise early */
230subsys_initcall(as3711_i2c_init);
231
232static void __exit as3711_i2c_exit(void)
233{
234	i2c_del_driver(&as3711_i2c_driver);
235}
236module_exit(as3711_i2c_exit);
237
238MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
239MODULE_DESCRIPTION("AS3711 PMIC driver");
240MODULE_LICENSE("GPL v2");