tps65910.c 7.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * tps65910.c  --  TI TPS6591x
 *
 * Copyright 2010 Texas Instruments Inc.
 *
 * Author: Graeme Gregory <gg@slimlogic.co.uk>
 * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under  the terms of the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the License, or (at your
 *  option) any later version.
 *
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
19
#include <linux/err.h>
20 21 22 23
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
24
#include <linux/regmap.h>
25
#include <linux/mfd/tps65910.h>
26
#include <linux/of_device.h>
27 28 29 30 31 32 33 34 35 36 37 38 39 40

static struct mfd_cell tps65910s[] = {
	{
		.name = "tps65910-pmic",
	},
	{
		.name = "tps65910-rtc",
	},
	{
		.name = "tps65910-power",
	},
};


41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
	struct tps65910 *tps65910 = dev_get_drvdata(dev);

	/*
	 * Caching all regulator registers.
	 * All regualator register address range is same for
	 * TPS65910 and TPS65911
	 */
	if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) {
		/* Check for non-existing register */
		if (tps65910_chip_id(tps65910) == TPS65910)
			if ((reg == TPS65911_VDDCTRL_OP) ||
				(reg == TPS65911_VDDCTRL_SR))
				return true;
		return false;
	}
	return true;
}

61
static const struct regmap_config tps65910_regmap_config = {
62 63 64 65 66 67 68 69
	.reg_bits = 8,
	.val_bits = 8,
	.volatile_reg = is_volatile_reg,
	.max_register = TPS65910_MAX_REGISTER,
	.num_reg_defaults_raw = TPS65910_MAX_REGISTER,
	.cache_type = REGCACHE_RBTREE,
};

70
static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
71 72 73 74 75 76 77 78 79 80 81
		struct tps65910_board *pmic_pdata)
{
	struct device *dev = NULL;
	int ret = 0;

	dev = tps65910->dev;

	if (!pmic_pdata->en_dev_slp)
		return 0;

	/* enabling SLEEP device state */
82
	ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
83 84 85 86 87 88 89 90 91 92 93
				DEVCTRL_DEV_SLP_MASK);
	if (ret < 0) {
		dev_err(dev, "set dev_slp failed: %d\n", ret);
		goto err_sleep_init;
	}

	/* Return if there is no sleep keepon data. */
	if (!pmic_pdata->slp_keepon)
		return 0;

	if (pmic_pdata->slp_keepon->therm_keepon) {
94 95
		ret = tps65910_reg_set_bits(tps65910,
				TPS65910_SLEEP_KEEP_RES_ON,
96 97 98 99 100 101 102 103
				SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK);
		if (ret < 0) {
			dev_err(dev, "set therm_keepon failed: %d\n", ret);
			goto disable_dev_slp;
		}
	}

	if (pmic_pdata->slp_keepon->clkout32k_keepon) {
104 105
		ret = tps65910_reg_set_bits(tps65910,
				TPS65910_SLEEP_KEEP_RES_ON,
106 107 108 109 110 111 112 113
				SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK);
		if (ret < 0) {
			dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
			goto disable_dev_slp;
		}
	}

	if (pmic_pdata->slp_keepon->i2chs_keepon) {
114 115
		ret = tps65910_reg_set_bits(tps65910,
				TPS65910_SLEEP_KEEP_RES_ON,
116 117 118 119 120 121 122 123 124 125
				SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK);
		if (ret < 0) {
			dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
			goto disable_dev_slp;
		}
	}

	return 0;

disable_dev_slp:
126 127
	tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
				DEVCTRL_DEV_SLP_MASK);
128 129 130 131 132

err_sleep_init:
	return ret;
}

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
#ifdef CONFIG_OF
static struct of_device_id tps65910_of_match[] = {
	{ .compatible = "ti,tps65910", .data = (void *)TPS65910},
	{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
	{ },
};
MODULE_DEVICE_TABLE(of, tps65910_of_match);

static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
						int *chip_id)
{
	struct device_node *np = client->dev.of_node;
	struct tps65910_board *board_info;
	unsigned int prop;
	const struct of_device_id *match;
	unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
	int ret = 0;
	int idx;

	match = of_match_device(tps65910_of_match, &client->dev);
	if (!match) {
		dev_err(&client->dev, "Failed to find matching dt id\n");
		return NULL;
	}

	*chip_id  = (int)match->data;

	board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
			GFP_KERNEL);
	if (!board_info) {
		dev_err(&client->dev, "Failed to allocate pdata\n");
		return NULL;
	}

	ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
	if (!ret)
		board_info->vmbch_threshold = prop;
	else if (*chip_id == TPS65911)
		dev_warn(&client->dev, "VMBCH-Threshold not specified");

	ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
	if (!ret)
		board_info->vmbch2_threshold = prop;
	else if (*chip_id == TPS65911)
		dev_warn(&client->dev, "VMBCH2-Threshold not specified");

	ret = of_property_read_u32_array(np, "ti,en-gpio-sleep",
				   prop_array, TPS6591X_MAX_NUM_GPIO);
	if (!ret)
		for (idx = 0; idx < ARRAY_SIZE(prop_array); idx++)
			board_info->en_gpio_sleep[idx] = (prop_array[idx] != 0);
	else if (ret != -EINVAL) {
		dev_err(&client->dev,
			"error reading property ti,en-gpio-sleep: %d\n.", ret);
		return NULL;
	}


	board_info->irq = client->irq;
	board_info->irq_base = -1;
	board_info->gpio_base = -1;

	return board_info;
}
#else
static inline struct tps65910_board *tps65910_parse_dt(
					struct i2c_client *client)
{
	return NULL;
}
#endif
204

205 206
static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
					const struct i2c_device_id *id)
207 208
{
	struct tps65910 *tps65910;
G
Graeme Gregory 已提交
209
	struct tps65910_board *pmic_plat_data;
210
	struct tps65910_platform_data *init_data;
211
	int ret = 0;
212
	int chip_id = id->driver_data;
213

G
Graeme Gregory 已提交
214
	pmic_plat_data = dev_get_platdata(&i2c->dev);
215 216 217 218

	if (!pmic_plat_data && i2c->dev.of_node)
		pmic_plat_data = tps65910_parse_dt(i2c, &chip_id);

G
Graeme Gregory 已提交
219 220 221
	if (!pmic_plat_data)
		return -EINVAL;

222 223 224 225
	init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
	if (init_data == NULL)
		return -ENOMEM;

226
	tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
227 228
	if (tps65910 == NULL) {
		kfree(init_data);
229
		return -ENOMEM;
230
	}
231 232 233 234

	i2c_set_clientdata(i2c, tps65910);
	tps65910->dev = &i2c->dev;
	tps65910->i2c_client = i2c;
235
	tps65910->id = chip_id;
236 237
	mutex_init(&tps65910->io_mutex);

238
	tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
239 240 241 242 243 244
	if (IS_ERR(tps65910->regmap)) {
		ret = PTR_ERR(tps65910->regmap);
		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
		goto regmap_err;
	}

245 246 247 248 249 250
	ret = mfd_add_devices(tps65910->dev, -1,
			      tps65910s, ARRAY_SIZE(tps65910s),
			      NULL, 0);
	if (ret < 0)
		goto err;

251
	init_data->irq = pmic_plat_data->irq;
252
	init_data->irq_base = pmic_plat_data->irq_base;
253

G
Graeme Gregory 已提交
254 255
	tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);

256
	tps65910_irq_init(tps65910, init_data->irq, init_data);
257

258 259
	tps65910_sleepinit(tps65910, pmic_plat_data);

260
	kfree(init_data);
261 262 263
	return ret;

err:
264 265
	regmap_exit(tps65910->regmap);
regmap_err:
266
	kfree(tps65910);
267
	kfree(init_data);
268 269 270
	return ret;
}

271
static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
272 273 274
{
	struct tps65910 *tps65910 = i2c_get_clientdata(i2c);

M
Mark Brown 已提交
275
	tps65910_irq_exit(tps65910);
276
	mfd_remove_devices(tps65910->dev);
277
	regmap_exit(tps65910->regmap);
278 279 280 281 282 283
	kfree(tps65910);

	return 0;
}

static const struct i2c_device_id tps65910_i2c_id[] = {
284 285
       { "tps65910", TPS65910 },
       { "tps65911", TPS65911 },
286 287 288 289 290 291 292 293 294
       { }
};
MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);


static struct i2c_driver tps65910_i2c_driver = {
	.driver = {
		   .name = "tps65910",
		   .owner = THIS_MODULE,
295
		   .of_match_table = of_match_ptr(tps65910_of_match),
296 297
	},
	.probe = tps65910_i2c_probe,
298
	.remove = __devexit_p(tps65910_i2c_remove),
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	.id_table = tps65910_i2c_id,
};

static int __init tps65910_i2c_init(void)
{
	return i2c_add_driver(&tps65910_i2c_driver);
}
/* init early so consumer devices can complete system boot */
subsys_initcall(tps65910_i2c_init);

static void __exit tps65910_i2c_exit(void)
{
	i2c_del_driver(&tps65910_i2c_driver);
}
module_exit(tps65910_i2c_exit);

MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
MODULE_LICENSE("GPL");