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