xref: /linux/drivers/mfd/tps65910.c (revision cd4209ced4d3936cfe51b7b8833260457e2d9995)
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/gpio.h>
2327c6750eSGraeme Gregory #include <linux/mfd/core.h>
24dc9913a0SLaxman Dewangan #include <linux/regmap.h>
2527c6750eSGraeme Gregory #include <linux/mfd/tps65910.h>
26*cd4209ceSRhyland Klein #include <linux/of_device.h>
2727c6750eSGraeme Gregory 
2827c6750eSGraeme Gregory static struct mfd_cell tps65910s[] = {
2927c6750eSGraeme Gregory 	{
3027c6750eSGraeme Gregory 		.name = "tps65910-pmic",
3127c6750eSGraeme Gregory 	},
3227c6750eSGraeme Gregory 	{
3327c6750eSGraeme Gregory 		.name = "tps65910-rtc",
3427c6750eSGraeme Gregory 	},
3527c6750eSGraeme Gregory 	{
3627c6750eSGraeme Gregory 		.name = "tps65910-power",
3727c6750eSGraeme Gregory 	},
3827c6750eSGraeme Gregory };
3927c6750eSGraeme Gregory 
4027c6750eSGraeme Gregory 
41dc9913a0SLaxman Dewangan static bool is_volatile_reg(struct device *dev, unsigned int reg)
42dc9913a0SLaxman Dewangan {
43dc9913a0SLaxman Dewangan 	struct tps65910 *tps65910 = dev_get_drvdata(dev);
44dc9913a0SLaxman Dewangan 
45dc9913a0SLaxman Dewangan 	/*
46dc9913a0SLaxman Dewangan 	 * Caching all regulator registers.
47dc9913a0SLaxman Dewangan 	 * All regualator register address range is same for
48dc9913a0SLaxman Dewangan 	 * TPS65910 and TPS65911
49dc9913a0SLaxman Dewangan 	 */
50dc9913a0SLaxman Dewangan 	if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) {
51dc9913a0SLaxman Dewangan 		/* Check for non-existing register */
52dc9913a0SLaxman Dewangan 		if (tps65910_chip_id(tps65910) == TPS65910)
53dc9913a0SLaxman Dewangan 			if ((reg == TPS65911_VDDCTRL_OP) ||
54dc9913a0SLaxman Dewangan 				(reg == TPS65911_VDDCTRL_SR))
55dc9913a0SLaxman Dewangan 				return true;
56dc9913a0SLaxman Dewangan 		return false;
57dc9913a0SLaxman Dewangan 	}
58dc9913a0SLaxman Dewangan 	return true;
59dc9913a0SLaxman Dewangan }
60dc9913a0SLaxman Dewangan 
6139ecb037SLaxman Dewangan static const struct regmap_config tps65910_regmap_config = {
62dc9913a0SLaxman Dewangan 	.reg_bits = 8,
63dc9913a0SLaxman Dewangan 	.val_bits = 8,
64dc9913a0SLaxman Dewangan 	.volatile_reg = is_volatile_reg,
65dc9913a0SLaxman Dewangan 	.max_register = TPS65910_MAX_REGISTER,
66dc9913a0SLaxman Dewangan 	.num_reg_defaults_raw = TPS65910_MAX_REGISTER,
67dc9913a0SLaxman Dewangan 	.cache_type = REGCACHE_RBTREE,
68dc9913a0SLaxman Dewangan };
69dc9913a0SLaxman Dewangan 
7063745d40SMark Brown static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
71201cf052SLaxman Dewangan 		struct tps65910_board *pmic_pdata)
72201cf052SLaxman Dewangan {
73201cf052SLaxman Dewangan 	struct device *dev = NULL;
74201cf052SLaxman Dewangan 	int ret = 0;
75201cf052SLaxman Dewangan 
76201cf052SLaxman Dewangan 	dev = tps65910->dev;
77201cf052SLaxman Dewangan 
78201cf052SLaxman Dewangan 	if (!pmic_pdata->en_dev_slp)
79201cf052SLaxman Dewangan 		return 0;
80201cf052SLaxman Dewangan 
81201cf052SLaxman Dewangan 	/* enabling SLEEP device state */
823f7e8275SRhyland Klein 	ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
83201cf052SLaxman Dewangan 				DEVCTRL_DEV_SLP_MASK);
84201cf052SLaxman Dewangan 	if (ret < 0) {
85201cf052SLaxman Dewangan 		dev_err(dev, "set dev_slp failed: %d\n", ret);
86201cf052SLaxman Dewangan 		goto err_sleep_init;
87201cf052SLaxman Dewangan 	}
88201cf052SLaxman Dewangan 
89201cf052SLaxman Dewangan 	/* Return if there is no sleep keepon data. */
90201cf052SLaxman Dewangan 	if (!pmic_pdata->slp_keepon)
91201cf052SLaxman Dewangan 		return 0;
92201cf052SLaxman Dewangan 
93201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->therm_keepon) {
943f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
953f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
96201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK);
97201cf052SLaxman Dewangan 		if (ret < 0) {
98201cf052SLaxman Dewangan 			dev_err(dev, "set therm_keepon failed: %d\n", ret);
99201cf052SLaxman Dewangan 			goto disable_dev_slp;
100201cf052SLaxman Dewangan 		}
101201cf052SLaxman Dewangan 	}
102201cf052SLaxman Dewangan 
103201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->clkout32k_keepon) {
1043f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
1053f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
106201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK);
107201cf052SLaxman Dewangan 		if (ret < 0) {
108201cf052SLaxman Dewangan 			dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
109201cf052SLaxman Dewangan 			goto disable_dev_slp;
110201cf052SLaxman Dewangan 		}
111201cf052SLaxman Dewangan 	}
112201cf052SLaxman Dewangan 
113201cf052SLaxman Dewangan 	if (pmic_pdata->slp_keepon->i2chs_keepon) {
1143f7e8275SRhyland Klein 		ret = tps65910_reg_set_bits(tps65910,
1153f7e8275SRhyland Klein 				TPS65910_SLEEP_KEEP_RES_ON,
116201cf052SLaxman Dewangan 				SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK);
117201cf052SLaxman Dewangan 		if (ret < 0) {
118201cf052SLaxman Dewangan 			dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
119201cf052SLaxman Dewangan 			goto disable_dev_slp;
120201cf052SLaxman Dewangan 		}
121201cf052SLaxman Dewangan 	}
122201cf052SLaxman Dewangan 
123201cf052SLaxman Dewangan 	return 0;
124201cf052SLaxman Dewangan 
125201cf052SLaxman Dewangan disable_dev_slp:
1263f7e8275SRhyland Klein 	tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
1273f7e8275SRhyland Klein 				DEVCTRL_DEV_SLP_MASK);
128201cf052SLaxman Dewangan 
129201cf052SLaxman Dewangan err_sleep_init:
130201cf052SLaxman Dewangan 	return ret;
131201cf052SLaxman Dewangan }
132201cf052SLaxman Dewangan 
133*cd4209ceSRhyland Klein #ifdef CONFIG_OF
134*cd4209ceSRhyland Klein static struct of_device_id tps65910_of_match[] = {
135*cd4209ceSRhyland Klein 	{ .compatible = "ti,tps65910", .data = (void *)TPS65910},
136*cd4209ceSRhyland Klein 	{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
137*cd4209ceSRhyland Klein 	{ },
138*cd4209ceSRhyland Klein };
139*cd4209ceSRhyland Klein MODULE_DEVICE_TABLE(of, tps65910_of_match);
140*cd4209ceSRhyland Klein 
141*cd4209ceSRhyland Klein static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
142*cd4209ceSRhyland Klein 						int *chip_id)
143*cd4209ceSRhyland Klein {
144*cd4209ceSRhyland Klein 	struct device_node *np = client->dev.of_node;
145*cd4209ceSRhyland Klein 	struct tps65910_board *board_info;
146*cd4209ceSRhyland Klein 	unsigned int prop;
147*cd4209ceSRhyland Klein 	const struct of_device_id *match;
148*cd4209ceSRhyland Klein 	unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
149*cd4209ceSRhyland Klein 	int ret = 0;
150*cd4209ceSRhyland Klein 	int idx;
151*cd4209ceSRhyland Klein 
152*cd4209ceSRhyland Klein 	match = of_match_device(tps65910_of_match, &client->dev);
153*cd4209ceSRhyland Klein 	if (!match) {
154*cd4209ceSRhyland Klein 		dev_err(&client->dev, "Failed to find matching dt id\n");
155*cd4209ceSRhyland Klein 		return NULL;
156*cd4209ceSRhyland Klein 	}
157*cd4209ceSRhyland Klein 
158*cd4209ceSRhyland Klein 	*chip_id  = (int)match->data;
159*cd4209ceSRhyland Klein 
160*cd4209ceSRhyland Klein 	board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
161*cd4209ceSRhyland Klein 			GFP_KERNEL);
162*cd4209ceSRhyland Klein 	if (!board_info) {
163*cd4209ceSRhyland Klein 		dev_err(&client->dev, "Failed to allocate pdata\n");
164*cd4209ceSRhyland Klein 		return NULL;
165*cd4209ceSRhyland Klein 	}
166*cd4209ceSRhyland Klein 
167*cd4209ceSRhyland Klein 	ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
168*cd4209ceSRhyland Klein 	if (!ret)
169*cd4209ceSRhyland Klein 		board_info->vmbch_threshold = prop;
170*cd4209ceSRhyland Klein 	else if (*chip_id == TPS65911)
171*cd4209ceSRhyland Klein 		dev_warn(&client->dev, "VMBCH-Threshold not specified");
172*cd4209ceSRhyland Klein 
173*cd4209ceSRhyland Klein 	ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
174*cd4209ceSRhyland Klein 	if (!ret)
175*cd4209ceSRhyland Klein 		board_info->vmbch2_threshold = prop;
176*cd4209ceSRhyland Klein 	else if (*chip_id == TPS65911)
177*cd4209ceSRhyland Klein 		dev_warn(&client->dev, "VMBCH2-Threshold not specified");
178*cd4209ceSRhyland Klein 
179*cd4209ceSRhyland Klein 	ret = of_property_read_u32_array(np, "ti,en-gpio-sleep",
180*cd4209ceSRhyland Klein 				   prop_array, TPS6591X_MAX_NUM_GPIO);
181*cd4209ceSRhyland Klein 	if (!ret)
182*cd4209ceSRhyland Klein 		for (idx = 0; idx < ARRAY_SIZE(prop_array); idx++)
183*cd4209ceSRhyland Klein 			board_info->en_gpio_sleep[idx] = (prop_array[idx] != 0);
184*cd4209ceSRhyland Klein 	else if (ret != -EINVAL) {
185*cd4209ceSRhyland Klein 		dev_err(&client->dev,
186*cd4209ceSRhyland Klein 			"error reading property ti,en-gpio-sleep: %d\n.", ret);
187*cd4209ceSRhyland Klein 		return NULL;
188*cd4209ceSRhyland Klein 	}
189*cd4209ceSRhyland Klein 
190*cd4209ceSRhyland Klein 
191*cd4209ceSRhyland Klein 	board_info->irq = client->irq;
192*cd4209ceSRhyland Klein 	board_info->irq_base = -1;
193*cd4209ceSRhyland Klein 	board_info->gpio_base = -1;
194*cd4209ceSRhyland Klein 
195*cd4209ceSRhyland Klein 	return board_info;
196*cd4209ceSRhyland Klein }
197*cd4209ceSRhyland Klein #else
198*cd4209ceSRhyland Klein static inline struct tps65910_board *tps65910_parse_dt(
199*cd4209ceSRhyland Klein 					struct i2c_client *client)
200*cd4209ceSRhyland Klein {
201*cd4209ceSRhyland Klein 	return NULL;
202*cd4209ceSRhyland Klein }
203*cd4209ceSRhyland Klein #endif
204201cf052SLaxman Dewangan 
20563745d40SMark Brown static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
20627c6750eSGraeme Gregory 					const struct i2c_device_id *id)
20727c6750eSGraeme Gregory {
20827c6750eSGraeme Gregory 	struct tps65910 *tps65910;
2092537df72SGraeme Gregory 	struct tps65910_board *pmic_plat_data;
210e3471bdcSGraeme Gregory 	struct tps65910_platform_data *init_data;
21127c6750eSGraeme Gregory 	int ret = 0;
212*cd4209ceSRhyland Klein 	int chip_id = id->driver_data;
21327c6750eSGraeme Gregory 
2142537df72SGraeme Gregory 	pmic_plat_data = dev_get_platdata(&i2c->dev);
215*cd4209ceSRhyland Klein 
216*cd4209ceSRhyland Klein 	if (!pmic_plat_data && i2c->dev.of_node)
217*cd4209ceSRhyland Klein 		pmic_plat_data = tps65910_parse_dt(i2c, &chip_id);
218*cd4209ceSRhyland Klein 
2192537df72SGraeme Gregory 	if (!pmic_plat_data)
2202537df72SGraeme Gregory 		return -EINVAL;
2212537df72SGraeme Gregory 
222e3471bdcSGraeme Gregory 	init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
223e3471bdcSGraeme Gregory 	if (init_data == NULL)
224e3471bdcSGraeme Gregory 		return -ENOMEM;
225e3471bdcSGraeme Gregory 
22627c6750eSGraeme Gregory 	tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
227dc7e412dSJesper Juhl 	if (tps65910 == NULL) {
228dc7e412dSJesper Juhl 		kfree(init_data);
22927c6750eSGraeme Gregory 		return -ENOMEM;
230dc7e412dSJesper Juhl 	}
23127c6750eSGraeme Gregory 
23227c6750eSGraeme Gregory 	i2c_set_clientdata(i2c, tps65910);
23327c6750eSGraeme Gregory 	tps65910->dev = &i2c->dev;
23427c6750eSGraeme Gregory 	tps65910->i2c_client = i2c;
235*cd4209ceSRhyland Klein 	tps65910->id = chip_id;
23627c6750eSGraeme Gregory 	mutex_init(&tps65910->io_mutex);
23727c6750eSGraeme Gregory 
23839ecb037SLaxman Dewangan 	tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
239dc9913a0SLaxman Dewangan 	if (IS_ERR(tps65910->regmap)) {
240dc9913a0SLaxman Dewangan 		ret = PTR_ERR(tps65910->regmap);
241dc9913a0SLaxman Dewangan 		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
242dc9913a0SLaxman Dewangan 		goto regmap_err;
243dc9913a0SLaxman Dewangan 	}
244dc9913a0SLaxman Dewangan 
24527c6750eSGraeme Gregory 	ret = mfd_add_devices(tps65910->dev, -1,
24627c6750eSGraeme Gregory 			      tps65910s, ARRAY_SIZE(tps65910s),
24727c6750eSGraeme Gregory 			      NULL, 0);
24827c6750eSGraeme Gregory 	if (ret < 0)
24927c6750eSGraeme Gregory 		goto err;
25027c6750eSGraeme Gregory 
251b1224cd1SJesper Juhl 	init_data->irq = pmic_plat_data->irq;
2521773140fSLaxman Dewangan 	init_data->irq_base = pmic_plat_data->irq_base;
253b1224cd1SJesper Juhl 
2542537df72SGraeme Gregory 	tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
2552537df72SGraeme Gregory 
2561e351a95SAfzal Mohammed 	tps65910_irq_init(tps65910, init_data->irq, init_data);
257e3471bdcSGraeme Gregory 
258201cf052SLaxman Dewangan 	tps65910_sleepinit(tps65910, pmic_plat_data);
259201cf052SLaxman Dewangan 
260dc7e412dSJesper Juhl 	kfree(init_data);
26127c6750eSGraeme Gregory 	return ret;
26227c6750eSGraeme Gregory 
26327c6750eSGraeme Gregory err:
264dc9913a0SLaxman Dewangan 	regmap_exit(tps65910->regmap);
265dc9913a0SLaxman Dewangan regmap_err:
26627c6750eSGraeme Gregory 	kfree(tps65910);
267dc7e412dSJesper Juhl 	kfree(init_data);
26827c6750eSGraeme Gregory 	return ret;
26927c6750eSGraeme Gregory }
27027c6750eSGraeme Gregory 
27163745d40SMark Brown static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
27227c6750eSGraeme Gregory {
27327c6750eSGraeme Gregory 	struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
27427c6750eSGraeme Gregory 
275ec2328c3SMark Brown 	tps65910_irq_exit(tps65910);
2761e351a95SAfzal Mohammed 	mfd_remove_devices(tps65910->dev);
277dc9913a0SLaxman Dewangan 	regmap_exit(tps65910->regmap);
27827c6750eSGraeme Gregory 	kfree(tps65910);
27927c6750eSGraeme Gregory 
28027c6750eSGraeme Gregory 	return 0;
28127c6750eSGraeme Gregory }
28227c6750eSGraeme Gregory 
28327c6750eSGraeme Gregory static const struct i2c_device_id tps65910_i2c_id[] = {
28479557056SJorge Eduardo Candelaria        { "tps65910", TPS65910 },
28579557056SJorge Eduardo Candelaria        { "tps65911", TPS65911 },
28627c6750eSGraeme Gregory        { }
28727c6750eSGraeme Gregory };
28827c6750eSGraeme Gregory MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
28927c6750eSGraeme Gregory 
29027c6750eSGraeme Gregory 
29127c6750eSGraeme Gregory static struct i2c_driver tps65910_i2c_driver = {
29227c6750eSGraeme Gregory 	.driver = {
29327c6750eSGraeme Gregory 		   .name = "tps65910",
29427c6750eSGraeme Gregory 		   .owner = THIS_MODULE,
295*cd4209ceSRhyland Klein 		   .of_match_table = of_match_ptr(tps65910_of_match),
29627c6750eSGraeme Gregory 	},
29727c6750eSGraeme Gregory 	.probe = tps65910_i2c_probe,
29863745d40SMark Brown 	.remove = __devexit_p(tps65910_i2c_remove),
29927c6750eSGraeme Gregory 	.id_table = tps65910_i2c_id,
30027c6750eSGraeme Gregory };
30127c6750eSGraeme Gregory 
30227c6750eSGraeme Gregory static int __init tps65910_i2c_init(void)
30327c6750eSGraeme Gregory {
30427c6750eSGraeme Gregory 	return i2c_add_driver(&tps65910_i2c_driver);
30527c6750eSGraeme Gregory }
30627c6750eSGraeme Gregory /* init early so consumer devices can complete system boot */
30727c6750eSGraeme Gregory subsys_initcall(tps65910_i2c_init);
30827c6750eSGraeme Gregory 
30927c6750eSGraeme Gregory static void __exit tps65910_i2c_exit(void)
31027c6750eSGraeme Gregory {
31127c6750eSGraeme Gregory 	i2c_del_driver(&tps65910_i2c_driver);
31227c6750eSGraeme Gregory }
31327c6750eSGraeme Gregory module_exit(tps65910_i2c_exit);
31427c6750eSGraeme Gregory 
31527c6750eSGraeme Gregory MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
31627c6750eSGraeme Gregory MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
31727c6750eSGraeme Gregory MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
31827c6750eSGraeme Gregory MODULE_LICENSE("GPL");
319