xref: /linux/drivers/mfd/tps65910.c (revision bcc1dd4cd77ec168894ea325b4e89b15a8b5b4f6)
127c6750eSGraeme Gregory /*
227c6750eSGraeme Gregory  * tps65910.c  --  TI TPS6591x
327c6750eSGraeme Gregory  *
427c6750eSGraeme Gregory  * Copyright 2010 Texas Instruments Inc.
527c6750eSGraeme Gregory  *
627c6750eSGraeme Gregory  * Author: Graeme Gregory <gg@slimlogic.co.uk>
727c6750eSGraeme Gregory  * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
827c6750eSGraeme Gregory  *
927c6750eSGraeme Gregory  *  This program is free software; you can redistribute it and/or modify it
1027c6750eSGraeme Gregory  *  under  the terms of the GNU General  Public License as published by the
1127c6750eSGraeme Gregory  *  Free Software Foundation;  either version 2 of the License, or (at your
1227c6750eSGraeme Gregory  *  option) any later version.
1327c6750eSGraeme Gregory  *
1427c6750eSGraeme Gregory  */
1527c6750eSGraeme Gregory 
1627c6750eSGraeme Gregory #include <linux/module.h>
1727c6750eSGraeme Gregory #include <linux/moduleparam.h>
1827c6750eSGraeme Gregory #include <linux/init.h>
19dc9913a0SLaxman Dewangan #include <linux/err.h>
2027c6750eSGraeme Gregory #include <linux/slab.h>
2127c6750eSGraeme Gregory #include <linux/i2c.h>
2227c6750eSGraeme Gregory #include <linux/mfd/core.h>
23dc9913a0SLaxman Dewangan #include <linux/regmap.h>
2427c6750eSGraeme Gregory #include <linux/mfd/tps65910.h>
25cd4209ceSRhyland Klein #include <linux/of_device.h>
2627c6750eSGraeme Gregory 
2727c6750eSGraeme Gregory static struct mfd_cell tps65910s[] = {
2827c6750eSGraeme Gregory 	{
2932df986eSLaxman Dewangan 		.name = "tps65910-gpio",
3032df986eSLaxman Dewangan 	},
3132df986eSLaxman Dewangan 	{
3227c6750eSGraeme Gregory 		.name = "tps65910-pmic",
3327c6750eSGraeme Gregory 	},
3427c6750eSGraeme Gregory 	{
3527c6750eSGraeme Gregory 		.name = "tps65910-rtc",
3627c6750eSGraeme Gregory 	},
3727c6750eSGraeme Gregory 	{
3827c6750eSGraeme Gregory 		.name = "tps65910-power",
3927c6750eSGraeme Gregory 	},
4027c6750eSGraeme Gregory };
4127c6750eSGraeme Gregory 
4227c6750eSGraeme Gregory 
43dc9913a0SLaxman Dewangan static bool is_volatile_reg(struct device *dev, unsigned int reg)
44dc9913a0SLaxman Dewangan {
45dc9913a0SLaxman Dewangan 	struct tps65910 *tps65910 = dev_get_drvdata(dev);
46dc9913a0SLaxman Dewangan 
47dc9913a0SLaxman Dewangan 	/*
48dc9913a0SLaxman Dewangan 	 * Caching all regulator registers.
49dc9913a0SLaxman Dewangan 	 * All regualator register address range is same for
50dc9913a0SLaxman Dewangan 	 * TPS65910 and TPS65911
51dc9913a0SLaxman Dewangan 	 */
52dc9913a0SLaxman Dewangan 	if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) {
53dc9913a0SLaxman Dewangan 		/* Check for non-existing register */
54dc9913a0SLaxman Dewangan 		if (tps65910_chip_id(tps65910) == TPS65910)
55dc9913a0SLaxman Dewangan 			if ((reg == TPS65911_VDDCTRL_OP) ||
56dc9913a0SLaxman Dewangan 				(reg == TPS65911_VDDCTRL_SR))
57dc9913a0SLaxman Dewangan 				return true;
58dc9913a0SLaxman Dewangan 		return false;
59dc9913a0SLaxman Dewangan 	}
60dc9913a0SLaxman Dewangan 	return true;
61dc9913a0SLaxman Dewangan }
62dc9913a0SLaxman Dewangan 
6339ecb037SLaxman Dewangan static const struct regmap_config tps65910_regmap_config = {
64dc9913a0SLaxman Dewangan 	.reg_bits = 8,
65dc9913a0SLaxman Dewangan 	.val_bits = 8,
66dc9913a0SLaxman Dewangan 	.volatile_reg = is_volatile_reg,
673bf6bf9bSLaxman Dewangan 	.max_register = TPS65910_MAX_REGISTER - 1,
68dc9913a0SLaxman Dewangan 	.cache_type = REGCACHE_RBTREE,
69dc9913a0SLaxman Dewangan };
70dc9913a0SLaxman Dewangan 
71712db99dSJohan Hovold static int __devinit tps65910_misc_init(struct tps65910 *tps65910,
72712db99dSJohan Hovold 					struct tps65910_board *pmic_pdata)
73712db99dSJohan Hovold {
74712db99dSJohan Hovold 	struct device *dev = tps65910->dev;
75712db99dSJohan Hovold 	int ret;
76712db99dSJohan Hovold 
77712db99dSJohan Hovold 	if (pmic_pdata->en_ck32k_xtal) {
78712db99dSJohan Hovold 		ret = tps65910_reg_clear_bits(tps65910,
79712db99dSJohan Hovold 						TPS65910_DEVCTRL,
80712db99dSJohan Hovold 						DEVCTRL_CK32K_CTRL_MASK);
81712db99dSJohan Hovold 		if (ret < 0) {
82712db99dSJohan Hovold 			dev_err(dev, "clear ck32k_ctrl failed: %d\n", ret);
83712db99dSJohan Hovold 			return ret;
84712db99dSJohan Hovold 		}
85712db99dSJohan Hovold 	}
86712db99dSJohan Hovold 
87712db99dSJohan Hovold 	return 0;
88712db99dSJohan Hovold }
89712db99dSJohan Hovold 
9063745d40SMark Brown static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
91201cf052SLaxman Dewangan 		struct tps65910_board *pmic_pdata)
92201cf052SLaxman Dewangan {
93201cf052SLaxman Dewangan 	struct device *dev = NULL;
94201cf052SLaxman Dewangan 	int ret = 0;
95201cf052SLaxman Dewangan 
96201cf052SLaxman Dewangan 	dev = tps65910->dev;
97201cf052SLaxman Dewangan 
98201cf052SLaxman Dewangan 	if (!pmic_pdata->en_dev_slp)
99201cf052SLaxman Dewangan 		return 0;
100201cf052SLaxman Dewangan 
101201cf052SLaxman Dewangan 	/* enabling SLEEP device state */
1023f7e8275SRhyland Klein 	ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
103201cf052SLaxman Dewangan 				DEVCTRL_DEV_SLP_MASK);
104201cf052SLaxman Dewangan 	if (ret < 0) {
105201cf052SLaxman Dewangan 		dev_err(dev, "set dev_slp failed: %d\n", ret);
106201cf052SLaxman Dewangan 		goto err_sleep_init;
107201cf052SLaxman Dewangan 	}
108201cf052SLaxman Dewangan 
109201cf052SLaxman Dewangan 	/* Return if there is no sleep keepon data. */
110201cf052SLaxman Dewangan 	if (!pmic_pdata->slp_keepon)
111201cf052SLaxman Dewangan 		return 0;
112201cf052SLaxman Dewangan 
113201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->therm_keepon) {
1143f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
1153f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
116201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK);
117201cf052SLaxman Dewangan 		if (ret < 0) {
118201cf052SLaxman Dewangan 			dev_err(dev, "set therm_keepon failed: %d\n", ret);
119201cf052SLaxman Dewangan 			goto disable_dev_slp;
120201cf052SLaxman Dewangan 		}
121201cf052SLaxman Dewangan 	}
122201cf052SLaxman Dewangan 
123201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->clkout32k_keepon) {
1243f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
1253f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
126201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK);
127201cf052SLaxman Dewangan 		if (ret < 0) {
128201cf052SLaxman Dewangan 			dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
129201cf052SLaxman Dewangan 			goto disable_dev_slp;
130201cf052SLaxman Dewangan 		}
131201cf052SLaxman Dewangan 	}
132201cf052SLaxman Dewangan 
133201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->i2chs_keepon) {
1343f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
1353f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
136201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK);
137201cf052SLaxman Dewangan 		if (ret < 0) {
138201cf052SLaxman Dewangan 			dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
139201cf052SLaxman Dewangan 			goto disable_dev_slp;
140201cf052SLaxman Dewangan 		}
141201cf052SLaxman Dewangan 	}
142201cf052SLaxman Dewangan 
143201cf052SLaxman Dewangan 	return 0;
144201cf052SLaxman Dewangan 
145201cf052SLaxman Dewangan disable_dev_slp:
1463f7e8275SRhyland Klein 	tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
1473f7e8275SRhyland Klein 				DEVCTRL_DEV_SLP_MASK);
148201cf052SLaxman Dewangan 
149201cf052SLaxman Dewangan err_sleep_init:
150201cf052SLaxman Dewangan 	return ret;
151201cf052SLaxman Dewangan }
152201cf052SLaxman Dewangan 
153cd4209ceSRhyland Klein #ifdef CONFIG_OF
154cd4209ceSRhyland Klein static struct of_device_id tps65910_of_match[] = {
155cd4209ceSRhyland Klein 	{ .compatible = "ti,tps65910", .data = (void *)TPS65910},
156cd4209ceSRhyland Klein 	{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
157cd4209ceSRhyland Klein 	{ },
158cd4209ceSRhyland Klein };
159cd4209ceSRhyland Klein MODULE_DEVICE_TABLE(of, tps65910_of_match);
160cd4209ceSRhyland Klein 
161cd4209ceSRhyland Klein static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
162cd4209ceSRhyland Klein 						int *chip_id)
163cd4209ceSRhyland Klein {
164cd4209ceSRhyland Klein 	struct device_node *np = client->dev.of_node;
165cd4209ceSRhyland Klein 	struct tps65910_board *board_info;
166cd4209ceSRhyland Klein 	unsigned int prop;
167cd4209ceSRhyland Klein 	const struct of_device_id *match;
168cd4209ceSRhyland Klein 	int ret = 0;
169cd4209ceSRhyland Klein 
170cd4209ceSRhyland Klein 	match = of_match_device(tps65910_of_match, &client->dev);
171cd4209ceSRhyland Klein 	if (!match) {
172cd4209ceSRhyland Klein 		dev_err(&client->dev, "Failed to find matching dt id\n");
173cd4209ceSRhyland Klein 		return NULL;
174cd4209ceSRhyland Klein 	}
175cd4209ceSRhyland Klein 
176cd4209ceSRhyland Klein 	*chip_id  = (int)match->data;
177cd4209ceSRhyland Klein 
178cd4209ceSRhyland Klein 	board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
179cd4209ceSRhyland Klein 			GFP_KERNEL);
180cd4209ceSRhyland Klein 	if (!board_info) {
181cd4209ceSRhyland Klein 		dev_err(&client->dev, "Failed to allocate pdata\n");
182cd4209ceSRhyland Klein 		return NULL;
183cd4209ceSRhyland Klein 	}
184cd4209ceSRhyland Klein 
185cd4209ceSRhyland Klein 	ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
186cd4209ceSRhyland Klein 	if (!ret)
187cd4209ceSRhyland Klein 		board_info->vmbch_threshold = prop;
188cd4209ceSRhyland Klein 	else if (*chip_id == TPS65911)
189cd4209ceSRhyland Klein 		dev_warn(&client->dev, "VMBCH-Threshold not specified");
190cd4209ceSRhyland Klein 
191cd4209ceSRhyland Klein 	ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
192cd4209ceSRhyland Klein 	if (!ret)
193cd4209ceSRhyland Klein 		board_info->vmbch2_threshold = prop;
194cd4209ceSRhyland Klein 	else if (*chip_id == TPS65911)
195cd4209ceSRhyland Klein 		dev_warn(&client->dev, "VMBCH2-Threshold not specified");
196cd4209ceSRhyland Klein 
197*bcc1dd4cSJohan Hovold 	prop = of_property_read_bool(np, "ti,en-ck32k-xtal");
198*bcc1dd4cSJohan Hovold 	board_info->en_ck32k_xtal = prop;
199*bcc1dd4cSJohan Hovold 
200cd4209ceSRhyland Klein 	board_info->irq = client->irq;
201cd4209ceSRhyland Klein 	board_info->irq_base = -1;
202cd4209ceSRhyland Klein 
203cd4209ceSRhyland Klein 	return board_info;
204cd4209ceSRhyland Klein }
205cd4209ceSRhyland Klein #else
2067f65f74cSSamuel Ortiz static inline
2077f65f74cSSamuel Ortiz struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
2087f65f74cSSamuel Ortiz 					 int *chip_id)
209cd4209ceSRhyland Klein {
210cd4209ceSRhyland Klein 	return NULL;
211cd4209ceSRhyland Klein }
212cd4209ceSRhyland Klein #endif
213201cf052SLaxman Dewangan 
21463745d40SMark Brown static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
21527c6750eSGraeme Gregory 					const struct i2c_device_id *id)
21627c6750eSGraeme Gregory {
21727c6750eSGraeme Gregory 	struct tps65910 *tps65910;
2182537df72SGraeme Gregory 	struct tps65910_board *pmic_plat_data;
219cb8d8654SLaxman Dewangan 	struct tps65910_board *of_pmic_plat_data = NULL;
220e3471bdcSGraeme Gregory 	struct tps65910_platform_data *init_data;
22127c6750eSGraeme Gregory 	int ret = 0;
222cd4209ceSRhyland Klein 	int chip_id = id->driver_data;
22327c6750eSGraeme Gregory 
2242537df72SGraeme Gregory 	pmic_plat_data = dev_get_platdata(&i2c->dev);
225cd4209ceSRhyland Klein 
226cb8d8654SLaxman Dewangan 	if (!pmic_plat_data && i2c->dev.of_node) {
227cd4209ceSRhyland Klein 		pmic_plat_data = tps65910_parse_dt(i2c, &chip_id);
228cb8d8654SLaxman Dewangan 		of_pmic_plat_data = pmic_plat_data;
229cb8d8654SLaxman Dewangan 	}
230cd4209ceSRhyland Klein 
2312537df72SGraeme Gregory 	if (!pmic_plat_data)
2322537df72SGraeme Gregory 		return -EINVAL;
2332537df72SGraeme Gregory 
23463fe7deeSLaxman Dewangan 	init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL);
235e3471bdcSGraeme Gregory 	if (init_data == NULL)
236e3471bdcSGraeme Gregory 		return -ENOMEM;
237e3471bdcSGraeme Gregory 
23863fe7deeSLaxman Dewangan 	tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL);
23963fe7deeSLaxman Dewangan 	if (tps65910 == NULL)
24027c6750eSGraeme Gregory 		return -ENOMEM;
24127c6750eSGraeme Gregory 
242cb8d8654SLaxman Dewangan 	tps65910->of_plat_data = of_pmic_plat_data;
24327c6750eSGraeme Gregory 	i2c_set_clientdata(i2c, tps65910);
24427c6750eSGraeme Gregory 	tps65910->dev = &i2c->dev;
24527c6750eSGraeme Gregory 	tps65910->i2c_client = i2c;
246cd4209ceSRhyland Klein 	tps65910->id = chip_id;
24727c6750eSGraeme Gregory 	mutex_init(&tps65910->io_mutex);
24827c6750eSGraeme Gregory 
24963fe7deeSLaxman Dewangan 	tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config);
250dc9913a0SLaxman Dewangan 	if (IS_ERR(tps65910->regmap)) {
251dc9913a0SLaxman Dewangan 		ret = PTR_ERR(tps65910->regmap);
252dc9913a0SLaxman Dewangan 		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
25363fe7deeSLaxman Dewangan 		return ret;
254dc9913a0SLaxman Dewangan 	}
255dc9913a0SLaxman Dewangan 
25627c6750eSGraeme Gregory 	ret = mfd_add_devices(tps65910->dev, -1,
25727c6750eSGraeme Gregory 			      tps65910s, ARRAY_SIZE(tps65910s),
25827c6750eSGraeme Gregory 			      NULL, 0);
25963fe7deeSLaxman Dewangan 	if (ret < 0) {
26063fe7deeSLaxman Dewangan 		dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
26163fe7deeSLaxman Dewangan 		return ret;
26263fe7deeSLaxman Dewangan 	}
26327c6750eSGraeme Gregory 
264b1224cd1SJesper Juhl 	init_data->irq = pmic_plat_data->irq;
2651773140fSLaxman Dewangan 	init_data->irq_base = pmic_plat_data->irq_base;
266b1224cd1SJesper Juhl 
2671e351a95SAfzal Mohammed 	tps65910_irq_init(tps65910, init_data->irq, init_data);
268712db99dSJohan Hovold 	tps65910_misc_init(tps65910, pmic_plat_data);
269201cf052SLaxman Dewangan 	tps65910_sleepinit(tps65910, pmic_plat_data);
270201cf052SLaxman Dewangan 
27127c6750eSGraeme Gregory 	return ret;
27227c6750eSGraeme Gregory }
27327c6750eSGraeme Gregory 
27463745d40SMark Brown static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
27527c6750eSGraeme Gregory {
27627c6750eSGraeme Gregory 	struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
27727c6750eSGraeme Gregory 
278ec2328c3SMark Brown 	tps65910_irq_exit(tps65910);
2791e351a95SAfzal Mohammed 	mfd_remove_devices(tps65910->dev);
28027c6750eSGraeme Gregory 
28127c6750eSGraeme Gregory 	return 0;
28227c6750eSGraeme Gregory }
28327c6750eSGraeme Gregory 
28427c6750eSGraeme Gregory static const struct i2c_device_id tps65910_i2c_id[] = {
28579557056SJorge Eduardo Candelaria        { "tps65910", TPS65910 },
28679557056SJorge Eduardo Candelaria        { "tps65911", TPS65911 },
28727c6750eSGraeme Gregory        { }
28827c6750eSGraeme Gregory };
28927c6750eSGraeme Gregory MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
29027c6750eSGraeme Gregory 
29127c6750eSGraeme Gregory 
29227c6750eSGraeme Gregory static struct i2c_driver tps65910_i2c_driver = {
29327c6750eSGraeme Gregory 	.driver = {
29427c6750eSGraeme Gregory 		   .name = "tps65910",
29527c6750eSGraeme Gregory 		   .owner = THIS_MODULE,
296cd4209ceSRhyland Klein 		   .of_match_table = of_match_ptr(tps65910_of_match),
29727c6750eSGraeme Gregory 	},
29827c6750eSGraeme Gregory 	.probe = tps65910_i2c_probe,
29963745d40SMark Brown 	.remove = __devexit_p(tps65910_i2c_remove),
30027c6750eSGraeme Gregory 	.id_table = tps65910_i2c_id,
30127c6750eSGraeme Gregory };
30227c6750eSGraeme Gregory 
30327c6750eSGraeme Gregory static int __init tps65910_i2c_init(void)
30427c6750eSGraeme Gregory {
30527c6750eSGraeme Gregory 	return i2c_add_driver(&tps65910_i2c_driver);
30627c6750eSGraeme Gregory }
30727c6750eSGraeme Gregory /* init early so consumer devices can complete system boot */
30827c6750eSGraeme Gregory subsys_initcall(tps65910_i2c_init);
30927c6750eSGraeme Gregory 
31027c6750eSGraeme Gregory static void __exit tps65910_i2c_exit(void)
31127c6750eSGraeme Gregory {
31227c6750eSGraeme Gregory 	i2c_del_driver(&tps65910_i2c_driver);
31327c6750eSGraeme Gregory }
31427c6750eSGraeme Gregory module_exit(tps65910_i2c_exit);
31527c6750eSGraeme Gregory 
31627c6750eSGraeme Gregory MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
31727c6750eSGraeme Gregory MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
31827c6750eSGraeme Gregory MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
31927c6750eSGraeme Gregory MODULE_LICENSE("GPL");
320