sec-core.c 9.6 KB
Newer Older
S
Sangbeom Kim 已提交
1
/*
2
 * sec-core.c
S
Sangbeom Kim 已提交
3
 *
4
 * Copyright (c) 2012 Samsung Electronics Co., Ltd
S
Sangbeom Kim 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *              http://www.samsung.com
 *
 *  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>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
20
#include <linux/of.h>
21
#include <linux/of_irq.h>
S
Sangbeom Kim 已提交
22 23 24 25
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
S
Sangbeom Kim 已提交
26 27 28
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/rtc.h>
29 30 31
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
S
Sangbeom Kim 已提交
32 33
#include <linux/regmap.h>

34
static const struct mfd_cell s5m8751_devs[] = {
35 36 37 38 39 40 41 42 43
	{
		.name = "s5m8751-pmic",
	}, {
		.name = "s5m-charger",
	}, {
		.name = "s5m8751-codec",
	},
};

44
static const struct mfd_cell s5m8763_devs[] = {
45 46 47 48 49 50 51 52 53
	{
		.name = "s5m8763-pmic",
	}, {
		.name = "s5m-rtc",
	}, {
		.name = "s5m-charger",
	},
};

54
static const struct mfd_cell s5m8767_devs[] = {
S
Sangbeom Kim 已提交
55 56 57 58 59 60 61
	{
		.name = "s5m8767-pmic",
	}, {
		.name = "s5m-rtc",
	},
};

62
static const struct mfd_cell s2mps11_devs[] = {
63 64
	{
		.name = "s2mps11-pmic",
65 66 67
	}, {
		.name = "s2mps11-clk",
	}
68 69
};

70 71 72 73 74
#ifdef CONFIG_OF
static struct of_device_id sec_dt_match[] = {
	{	.compatible = "samsung,s5m8767-pmic",
		.data = (void *)S5M8767X,
	},
75 76 77
	{	.compatible = "samsung,s2mps11-pmic",
		.data = (void *)S2MPS11X,
	},
78 79 80 81
	{},
};
#endif

82
int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest)
S
Sangbeom Kim 已提交
83
{
84
	return regmap_read(sec_pmic->regmap_pmic, reg, dest);
S
Sangbeom Kim 已提交
85
}
86
EXPORT_SYMBOL_GPL(sec_reg_read);
S
Sangbeom Kim 已提交
87

88
int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
S
Sangbeom Kim 已提交
89
{
90
	return regmap_bulk_read(sec_pmic->regmap_pmic, reg, buf, count);
S
Sangbeom Kim 已提交
91
}
92
EXPORT_SYMBOL_GPL(sec_bulk_read);
S
Sangbeom Kim 已提交
93

94
int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value)
S
Sangbeom Kim 已提交
95
{
96
	return regmap_write(sec_pmic->regmap_pmic, reg, value);
S
Sangbeom Kim 已提交
97
}
98
EXPORT_SYMBOL_GPL(sec_reg_write);
S
Sangbeom Kim 已提交
99

100
int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
S
Sangbeom Kim 已提交
101
{
102
	return regmap_raw_write(sec_pmic->regmap_pmic, reg, buf, count);
S
Sangbeom Kim 已提交
103
}
104
EXPORT_SYMBOL_GPL(sec_bulk_write);
S
Sangbeom Kim 已提交
105

106
int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask)
S
Sangbeom Kim 已提交
107
{
108
	return regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, val);
S
Sangbeom Kim 已提交
109
}
110
EXPORT_SYMBOL_GPL(sec_reg_update);
S
Sangbeom Kim 已提交
111

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static bool s2mps11_volatile(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case S2MPS11_REG_INT1M:
	case S2MPS11_REG_INT2M:
	case S2MPS11_REG_INT3M:
		return false;
	default:
		return true;
	}
}

static bool s5m8763_volatile(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case S5M8763_REG_IRQM1:
	case S5M8763_REG_IRQM2:
	case S5M8763_REG_IRQM3:
	case S5M8763_REG_IRQM4:
		return false;
	default:
		return true;
	}
}

137
static const struct regmap_config sec_regmap_config = {
S
Sangbeom Kim 已提交
138 139 140 141
	.reg_bits = 8,
	.val_bits = 8,
};

142
static const struct regmap_config s2mps11_regmap_config = {
143 144 145 146
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S2MPS11_REG_L38CTRL,
147 148
	.volatile_reg = s2mps11_volatile,
	.cache_type = REGCACHE_FLAT,
149 150
};

151
static const struct regmap_config s5m8763_regmap_config = {
152 153 154 155
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S5M8763_REG_LBCNFG2,
156 157
	.volatile_reg = s5m8763_volatile,
	.cache_type = REGCACHE_FLAT,
158 159
};

160
static const struct regmap_config s5m8767_regmap_config = {
161 162 163 164
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S5M8767_REG_LDO28CTRL,
165 166
	.volatile_reg = s2mps11_volatile,
	.cache_type = REGCACHE_FLAT,
167
};
168

169 170 171 172 173
static const struct regmap_config sec_rtc_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
};

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
/*
 * Only the common platform data elements for s5m8767 are parsed here from the
 * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and
 * others have to parse their own platform data elements from device tree.
 *
 * The s5m8767 platform data structure is instantiated here and the drivers for
 * the sub-modules need not instantiate another instance while parsing their
 * platform data.
 */
static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
					struct device *dev)
{
	struct sec_platform_data *pd;

	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
	if (!pd) {
		dev_err(dev, "could not allocate memory for pdata\n");
		return ERR_PTR(-ENOMEM);
	}

	/*
	 * ToDo: the 'wakeup' member in the platform data is more of a linux
	 * specfic information. Hence, there is no binding for that yet and
	 * not parsed here.
	 */

	return pd;
}
#else
204
static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
					struct device *dev)
{
	return 0;
}
#endif

static inline int sec_i2c_get_driver_data(struct i2c_client *i2c,
						const struct i2c_device_id *id)
{
#ifdef CONFIG_OF
	if (i2c->dev.of_node) {
		const struct of_device_id *match;
		match = of_match_node(sec_dt_match, i2c->dev.of_node);
		return (int)match->data;
	}
#endif
	return (int)id->driver_data;
}

224
static int sec_pmic_probe(struct i2c_client *i2c,
S
Sangbeom Kim 已提交
225 226
			    const struct i2c_device_id *id)
{
J
Jingoo Han 已提交
227
	struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev);
228
	const struct regmap_config *regmap;
229
	struct sec_pmic_dev *sec_pmic;
230
	int ret;
S
Sangbeom Kim 已提交
231

232
	sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
233
				GFP_KERNEL);
234
	if (sec_pmic == NULL)
S
Sangbeom Kim 已提交
235 236
		return -ENOMEM;

237 238 239 240
	i2c_set_clientdata(i2c, sec_pmic);
	sec_pmic->dev = &i2c->dev;
	sec_pmic->i2c = i2c;
	sec_pmic->irq = i2c->irq;
241 242 243 244 245 246 247 248 249 250
	sec_pmic->type = sec_i2c_get_driver_data(i2c, id);

	if (sec_pmic->dev->of_node) {
		pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev);
		if (IS_ERR(pdata)) {
			ret = PTR_ERR(pdata);
			return ret;
		}
		pdata->device_type = sec_pmic->type;
	}
S
Sangbeom Kim 已提交
251
	if (pdata) {
252 253 254 255
		sec_pmic->device_type = pdata->device_type;
		sec_pmic->ono = pdata->ono;
		sec_pmic->irq_base = pdata->irq_base;
		sec_pmic->wakeup = pdata->wakeup;
256
		sec_pmic->pdata = pdata;
S
Sangbeom Kim 已提交
257 258
	}

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
	switch (sec_pmic->device_type) {
	case S2MPS11X:
		regmap = &s2mps11_regmap_config;
		break;
	case S5M8763X:
		regmap = &s5m8763_regmap_config;
		break;
	case S5M8767X:
		regmap = &s5m8767_regmap_config;
		break;
	default:
		regmap = &sec_regmap_config;
		break;
	}

274 275 276
	sec_pmic->regmap_pmic = devm_regmap_init_i2c(i2c, regmap);
	if (IS_ERR(sec_pmic->regmap_pmic)) {
		ret = PTR_ERR(sec_pmic->regmap_pmic);
S
Sangbeom Kim 已提交
277
		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
278
			ret);
279
		return ret;
S
Sangbeom Kim 已提交
280 281
	}

282 283
	sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
	i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
S
Sangbeom Kim 已提交
284

285 286 287 288 289 290 291 292 293
	sec_pmic->regmap_rtc = devm_regmap_init_i2c(sec_pmic->rtc,
			&sec_rtc_regmap_config);
	if (IS_ERR(sec_pmic->regmap_rtc)) {
		ret = PTR_ERR(sec_pmic->regmap_rtc);
		dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n",
			ret);
		return ret;
	}

294
	if (pdata && pdata->cfg_pmic_irq)
S
Sangbeom Kim 已提交
295 296
		pdata->cfg_pmic_irq();

297
	sec_irq_init(sec_pmic);
S
Sangbeom Kim 已提交
298

299
	pm_runtime_set_active(sec_pmic->dev);
S
Sangbeom Kim 已提交
300

301
	switch (sec_pmic->device_type) {
302
	case S5M8751X:
303
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
304
				      ARRAY_SIZE(s5m8751_devs), NULL, 0, NULL);
305 306
		break;
	case S5M8763X:
307
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
308
				      ARRAY_SIZE(s5m8763_devs), NULL, 0, NULL);
309 310
		break;
	case S5M8767X:
311
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
312
				      ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);
313
		break;
314 315
	case S2MPS11X:
		ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
316
				      ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);
317
		break;
318 319 320 321
	default:
		/* If this happens the probe function is problem */
		BUG();
	}
S
Sangbeom Kim 已提交
322

323
	if (ret)
S
Sangbeom Kim 已提交
324 325
		goto err;

326 327
	device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);

S
Sangbeom Kim 已提交
328 329 330
	return ret;

err:
331 332
	sec_irq_exit(sec_pmic);
	i2c_unregister_device(sec_pmic->rtc);
S
Sangbeom Kim 已提交
333 334 335
	return ret;
}

336
static int sec_pmic_remove(struct i2c_client *i2c)
S
Sangbeom Kim 已提交
337
{
338
	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
S
Sangbeom Kim 已提交
339

340 341 342
	mfd_remove_devices(sec_pmic->dev);
	sec_irq_exit(sec_pmic);
	i2c_unregister_device(sec_pmic->rtc);
S
Sangbeom Kim 已提交
343 344 345
	return 0;
}

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
static int sec_pmic_suspend(struct device *dev)
{
	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);

	if (device_may_wakeup(dev)) {
		enable_irq_wake(sec_pmic->irq);
		/*
		 * PMIC IRQ must be disabled during suspend for RTC alarm
		 * to work properly.
		 * When device is woken up from suspend by RTC Alarm, an
		 * interrupt occurs before resuming I2C bus controller.
		 * The interrupt is handled by regmap_irq_thread which tries
		 * to read RTC registers. This read fails (I2C is still
		 * suspended) and RTC Alarm interrupt is disabled.
		 */
		disable_irq(sec_pmic->irq);
	}

	return 0;
}

static int sec_pmic_resume(struct device *dev)
{
	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);

	if (device_may_wakeup(dev)) {
		disable_irq_wake(sec_pmic->irq);
		enable_irq(sec_pmic->irq);
	}

	return 0;
}

static SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume);

383 384
static const struct i2c_device_id sec_pmic_id[] = {
	{ "sec_pmic", 0 },
S
Sangbeom Kim 已提交
385 386
	{ }
};
387
MODULE_DEVICE_TABLE(i2c, sec_pmic_id);
S
Sangbeom Kim 已提交
388

389
static struct i2c_driver sec_pmic_driver = {
S
Sangbeom Kim 已提交
390
	.driver = {
391
		   .name = "sec_pmic",
S
Sangbeom Kim 已提交
392
		   .owner = THIS_MODULE,
393
		   .pm = &sec_pmic_pm_ops,
394
		   .of_match_table = of_match_ptr(sec_dt_match),
S
Sangbeom Kim 已提交
395
	},
396 397 398
	.probe = sec_pmic_probe,
	.remove = sec_pmic_remove,
	.id_table = sec_pmic_id,
S
Sangbeom Kim 已提交
399 400
};

401
static int __init sec_pmic_init(void)
S
Sangbeom Kim 已提交
402
{
403
	return i2c_add_driver(&sec_pmic_driver);
S
Sangbeom Kim 已提交
404 405
}

406
subsys_initcall(sec_pmic_init);
S
Sangbeom Kim 已提交
407

408
static void __exit sec_pmic_exit(void)
S
Sangbeom Kim 已提交
409
{
410
	i2c_del_driver(&sec_pmic_driver);
S
Sangbeom Kim 已提交
411
}
412
module_exit(sec_pmic_exit);
S
Sangbeom Kim 已提交
413 414 415 416

MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
MODULE_DESCRIPTION("Core support for the S5M MFD");
MODULE_LICENSE("GPL");