sec-core.c 10.2 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
#include <linux/mfd/samsung/s2mps11.h>
30
#include <linux/mfd/samsung/s2mps14.h>
31 32
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
S
Sangbeom Kim 已提交
33 34
#include <linux/regmap.h>

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

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

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

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

73 74 75 76 77 78 79 80 81 82
static const struct mfd_cell s2mps14_devs[] = {
	{
		.name = "s2mps14-pmic",
	}, {
		.name = "s2mps14-rtc",
	}, {
		.name = "s2mps14-clk",
	}
};

83 84 85 86 87
#ifdef CONFIG_OF
static struct of_device_id sec_dt_match[] = {
	{	.compatible = "samsung,s5m8767-pmic",
		.data = (void *)S5M8767X,
	},
88 89 90
	{	.compatible = "samsung,s2mps11-pmic",
		.data = (void *)S2MPS11X,
	},
91 92 93
	{	.compatible = "samsung,s2mps14-pmic",
		.data = (void *)S2MPS14X,
	},
94 95 96 97
	{},
};
#endif

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
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;
	}
}

123
static const struct regmap_config sec_regmap_config = {
S
Sangbeom Kim 已提交
124 125 126 127
	.reg_bits = 8,
	.val_bits = 8,
};

128
static const struct regmap_config s2mps11_regmap_config = {
129 130 131 132
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S2MPS11_REG_L38CTRL,
133 134
	.volatile_reg = s2mps11_volatile,
	.cache_type = REGCACHE_FLAT,
135 136
};

137 138 139 140 141 142 143 144 145
static const struct regmap_config s2mps14_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S2MPS14_REG_LDODSCH3,
	.volatile_reg = s2mps11_volatile,
	.cache_type = REGCACHE_FLAT,
};

146
static const struct regmap_config s5m8763_regmap_config = {
147 148 149 150
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S5M8763_REG_LBCNFG2,
151 152
	.volatile_reg = s5m8763_volatile,
	.cache_type = REGCACHE_FLAT,
153 154
};

155
static const struct regmap_config s5m8767_regmap_config = {
156 157 158 159
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S5M8767_REG_LDO28CTRL,
160 161
	.volatile_reg = s2mps11_volatile,
	.cache_type = REGCACHE_FLAT,
162
};
163

164
static const struct regmap_config s5m_rtc_regmap_config = {
165 166
	.reg_bits = 8,
	.val_bits = 8,
167 168

	.max_register = SEC_RTC_REG_MAX,
169 170
};

171 172 173 174 175 176 177
static const struct regmap_config s2mps14_rtc_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = S2MPS_RTC_REG_MAX,
};

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 204 205 206 207
#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
208
static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata(
209 210
					struct device *dev)
{
211
	return NULL;
212 213 214
}
#endif

215
static inline unsigned long sec_i2c_get_driver_data(struct i2c_client *i2c,
216 217 218 219 220 221
						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);
222
		return (unsigned long)match->data;
223 224
	}
#endif
225
	return id->driver_data;
226 227
}

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

236
	sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
237
				GFP_KERNEL);
238
	if (sec_pmic == NULL)
S
Sangbeom Kim 已提交
239 240
		return -ENOMEM;

241 242 243 244
	i2c_set_clientdata(i2c, sec_pmic);
	sec_pmic->dev = &i2c->dev;
	sec_pmic->i2c = i2c;
	sec_pmic->irq = i2c->irq;
245 246 247 248 249 250 251 252 253 254
	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 已提交
255
	if (pdata) {
256 257 258 259
		sec_pmic->device_type = pdata->device_type;
		sec_pmic->ono = pdata->ono;
		sec_pmic->irq_base = pdata->irq_base;
		sec_pmic->wakeup = pdata->wakeup;
260
		sec_pmic->pdata = pdata;
S
Sangbeom Kim 已提交
261 262
	}

263 264 265
	switch (sec_pmic->device_type) {
	case S2MPS11X:
		regmap = &s2mps11_regmap_config;
266 267 268 269 270 271
		/*
		 * The rtc-s5m driver does not support S2MPS11 and there
		 * is no mfd_cell for S2MPS11 RTC device.
		 * However we must pass something to devm_regmap_init_i2c()
		 * so use S5M-like regmap config even though it wouldn't work.
		 */
272 273 274 275 276
		regmap_rtc = &s5m_rtc_regmap_config;
		break;
	case S2MPS14X:
		regmap = &s2mps14_regmap_config;
		regmap_rtc = &s2mps14_rtc_regmap_config;
277 278 279
		break;
	case S5M8763X:
		regmap = &s5m8763_regmap_config;
280
		regmap_rtc = &s5m_rtc_regmap_config;
281 282 283
		break;
	case S5M8767X:
		regmap = &s5m8767_regmap_config;
284
		regmap_rtc = &s5m_rtc_regmap_config;
285 286 287
		break;
	default:
		regmap = &sec_regmap_config;
288
		regmap_rtc = &s5m_rtc_regmap_config;
289 290 291
		break;
	}

292 293 294
	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 已提交
295
		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
296
			ret);
297
		return ret;
S
Sangbeom Kim 已提交
298 299
	}

300
	sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
301 302 303 304
	if (!sec_pmic->rtc) {
		dev_err(&i2c->dev, "Failed to allocate I2C for RTC\n");
		return -ENODEV;
	}
305
	i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
S
Sangbeom Kim 已提交
306

307
	sec_pmic->regmap_rtc = devm_regmap_init_i2c(sec_pmic->rtc, regmap_rtc);
308 309 310 311 312 313 314
	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;
	}

315
	if (pdata && pdata->cfg_pmic_irq)
S
Sangbeom Kim 已提交
316 317
		pdata->cfg_pmic_irq();

318
	sec_irq_init(sec_pmic);
S
Sangbeom Kim 已提交
319

320
	pm_runtime_set_active(sec_pmic->dev);
S
Sangbeom Kim 已提交
321

322
	switch (sec_pmic->device_type) {
323
	case S5M8751X:
324
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
325
				      ARRAY_SIZE(s5m8751_devs), NULL, 0, NULL);
326 327
		break;
	case S5M8763X:
328
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
329
				      ARRAY_SIZE(s5m8763_devs), NULL, 0, NULL);
330 331
		break;
	case S5M8767X:
332
		ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
333
				      ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);
334
		break;
335 336
	case S2MPS11X:
		ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
337
				      ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);
338
		break;
339 340 341 342
	case S2MPS14X:
		ret = mfd_add_devices(sec_pmic->dev, -1, s2mps14_devs,
				      ARRAY_SIZE(s2mps14_devs), NULL, 0, NULL);
		break;
343 344 345 346
	default:
		/* If this happens the probe function is problem */
		BUG();
	}
S
Sangbeom Kim 已提交
347

348
	if (ret)
S
Sangbeom Kim 已提交
349 350
		goto err;

351 352
	device_init_wakeup(sec_pmic->dev, sec_pmic->wakeup);

S
Sangbeom Kim 已提交
353 354 355
	return ret;

err:
356 357
	sec_irq_exit(sec_pmic);
	i2c_unregister_device(sec_pmic->rtc);
S
Sangbeom Kim 已提交
358 359 360
	return ret;
}

361
static int sec_pmic_remove(struct i2c_client *i2c)
S
Sangbeom Kim 已提交
362
{
363
	struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
S
Sangbeom Kim 已提交
364

365 366 367
	mfd_remove_devices(sec_pmic->dev);
	sec_irq_exit(sec_pmic);
	i2c_unregister_device(sec_pmic->rtc);
S
Sangbeom Kim 已提交
368 369 370
	return 0;
}

371
#ifdef CONFIG_PM_SLEEP
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
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;
}
406
#endif /* CONFIG_PM_SLEEP */
407 408 409

static SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume);

410 411
static const struct i2c_device_id sec_pmic_id[] = {
	{ "sec_pmic", 0 },
S
Sangbeom Kim 已提交
412 413
	{ }
};
414
MODULE_DEVICE_TABLE(i2c, sec_pmic_id);
S
Sangbeom Kim 已提交
415

416
static struct i2c_driver sec_pmic_driver = {
S
Sangbeom Kim 已提交
417
	.driver = {
418
		   .name = "sec_pmic",
S
Sangbeom Kim 已提交
419
		   .owner = THIS_MODULE,
420
		   .pm = &sec_pmic_pm_ops,
421
		   .of_match_table = of_match_ptr(sec_dt_match),
S
Sangbeom Kim 已提交
422
	},
423 424 425
	.probe = sec_pmic_probe,
	.remove = sec_pmic_remove,
	.id_table = sec_pmic_id,
S
Sangbeom Kim 已提交
426 427
};

428
static int __init sec_pmic_init(void)
S
Sangbeom Kim 已提交
429
{
430
	return i2c_add_driver(&sec_pmic_driver);
S
Sangbeom Kim 已提交
431 432
}

433
subsys_initcall(sec_pmic_init);
S
Sangbeom Kim 已提交
434

435
static void __exit sec_pmic_exit(void)
S
Sangbeom Kim 已提交
436
{
437
	i2c_del_driver(&sec_pmic_driver);
S
Sangbeom Kim 已提交
438
}
439
module_exit(sec_pmic_exit);
S
Sangbeom Kim 已提交
440 441 442 443

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