Blame view

drivers/mfd/rn5t618.c 6.88 KB
3c910ecbd   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
2
3
4
5
  /*
   * MFD core driver for Ricoh RN5T618 PMIC
   *
   * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
6
   * Copyright (C) 2016 Toradex AG
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
7
   */
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
8
  #include <linux/delay.h>
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
9
  #include <linux/i2c.h>
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
10
11
  #include <linux/interrupt.h>
  #include <linux/irq.h>
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
12
13
14
  #include <linux/mfd/core.h>
  #include <linux/mfd/rn5t618.h>
  #include <linux/module.h>
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
15
  #include <linux/of_device.h>
bc6167661   Andreas Kemnade   mfd: rn5t618: Add...
16
  #include <linux/platform_device.h>
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
17
  #include <linux/reboot.h>
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
18
19
20
21
22
23
  #include <linux/regmap.h>
  
  static const struct mfd_cell rn5t618_cells[] = {
  	{ .name = "rn5t618-regulator" },
  	{ .name = "rn5t618-wdt" },
  };
bc6167661   Andreas Kemnade   mfd: rn5t618: Add...
24
25
  static const struct mfd_cell rc5t619_cells[] = {
  	{ .name = "rn5t618-adc" },
d1264a075   Andreas Kemnade   mfd: rn5t618: Add...
26
  	{ .name = "rn5t618-power" },
bc6167661   Andreas Kemnade   mfd: rn5t618: Add...
27
28
29
30
  	{ .name = "rn5t618-regulator" },
  	{ .name = "rc5t619-rtc" },
  	{ .name = "rn5t618-wdt" },
  };
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
31
32
33
34
35
36
  static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case RN5T618_WATCHDOGCNT:
  	case RN5T618_DCIRQ:
  	case RN5T618_ILIMDATAH ... RN5T618_AIN0DATAL:
2f3dc25c0   Andreas Kemnade   mfd: rn5t618: Mar...
37
  	case RN5T618_ADCCNT3:
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
38
39
40
41
42
  	case RN5T618_IR_ADC1 ... RN5T618_IR_ADC3:
  	case RN5T618_IR_GPR:
  	case RN5T618_IR_GPF:
  	case RN5T618_MON_IOIN:
  	case RN5T618_INTMON:
11027ce6f   Andreas Kemnade   mfd: rn5t618: Add...
43
44
  	case RN5T618_RTC_CTRL1 ... RN5T618_RTC_CTRL2:
  	case RN5T618_RTC_SECONDS ... RN5T618_RTC_YEAR:
594f1935b   Andreas Kemnade   mfd: rn5t618: Fix...
45
46
47
  	case RN5T618_CHGSTATE:
  	case RN5T618_CHGCTRL_IRR ... RN5T618_CHGERR_MONI:
  	case RN5T618_CONTROL ... RN5T618_CC_AVEREG0:
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
48
49
50
51
52
53
54
55
56
57
58
59
60
  		return true;
  	default:
  		return false;
  	}
  }
  
  static const struct regmap_config rn5t618_regmap_config = {
  	.reg_bits	= 8,
  	.val_bits	= 8,
  	.volatile_reg	= rn5t618_volatile_reg,
  	.max_register	= RN5T618_MAX_REG,
  	.cache_type	= REGCACHE_RBTREE,
  };
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  static const struct regmap_irq rc5t619_irqs[] = {
  	REGMAP_IRQ_REG(RN5T618_IRQ_SYS, 0, BIT(0)),
  	REGMAP_IRQ_REG(RN5T618_IRQ_DCDC, 0, BIT(1)),
  	REGMAP_IRQ_REG(RN5T618_IRQ_RTC, 0, BIT(2)),
  	REGMAP_IRQ_REG(RN5T618_IRQ_ADC, 0, BIT(3)),
  	REGMAP_IRQ_REG(RN5T618_IRQ_GPIO, 0, BIT(4)),
  	REGMAP_IRQ_REG(RN5T618_IRQ_CHG, 0, BIT(6)),
  };
  
  static const struct regmap_irq_chip rc5t619_irq_chip = {
  	.name = "rc5t619",
  	.irqs = rc5t619_irqs,
  	.num_irqs = ARRAY_SIZE(rc5t619_irqs),
  	.num_regs = 1,
  	.status_base = RN5T618_INTMON,
  	.mask_base = RN5T618_INTEN,
  	.mask_invert = true,
  };
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
79
  static struct i2c_client *rn5t618_pm_power_off;
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
80
  static struct notifier_block rn5t618_restart_handler;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
81

0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  static int rn5t618_irq_init(struct rn5t618 *rn5t618)
  {
  	const struct regmap_irq_chip *irq_chip = NULL;
  	int ret;
  
  	if (!rn5t618->irq)
  		return 0;
  
  	switch (rn5t618->variant) {
  	case RC5T619:
  		irq_chip = &rc5t619_irq_chip;
  		break;
  	default:
  		dev_err(rn5t618->dev, "Currently no IRQ support for variant %d
  ",
  			(int)rn5t618->variant);
  		return -ENOENT;
  	}
  
  	ret = devm_regmap_add_irq_chip(rn5t618->dev, rn5t618->regmap,
  				       rn5t618->irq,
  				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
  				       0, irq_chip, &rn5t618->irq_data);
  	if (ret)
  		dev_err(rn5t618->dev, "Failed to register IRQ chip
  ");
  
  	return ret;
  }
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
111
  static void rn5t618_trigger_poweroff_sequence(bool repower)
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
112
  {
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
113
  	int ret;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
114
  	/* disable automatic repower-on */
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
115
116
117
118
119
120
121
122
123
124
125
126
  	ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_REPCNT);
  	if (ret < 0)
  		goto err;
  
  	ret &= ~RN5T618_REPCNT_REPWRON;
  	if (repower)
  		ret |= RN5T618_REPCNT_REPWRON;
  
  	ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
  					RN5T618_REPCNT, (u8)ret);
  	if (ret < 0)
  		goto err;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
127
  	/* start power-off sequence */
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  	ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_SLPCNT);
  	if (ret < 0)
  		goto err;
  
  	ret |= RN5T618_SLPCNT_SWPWROFF;
  
  	ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
  					RN5T618_SLPCNT, (u8)ret);
  	if (ret < 0)
  		goto err;
  
  	return;
  
  err:
  	dev_alert(&rn5t618_pm_power_off->dev, "Failed to shutdown (err = %d)
  ", ret);
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
144
  }
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  static void rn5t618_power_off(void)
  {
  	rn5t618_trigger_poweroff_sequence(false);
  }
  
  static int rn5t618_restart(struct notifier_block *this,
  			    unsigned long mode, void *cmd)
  {
  	rn5t618_trigger_poweroff_sequence(true);
  
  	/*
  	 * Re-power factor detection on PMIC side is not instant. 1ms
  	 * proved to be enough time until reset takes effect.
  	 */
  	mdelay(1);
  
  	return NOTIFY_DONE;
  }
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
163
164
165
  static const struct of_device_id rn5t618_of_match[] = {
  	{ .compatible = "ricoh,rn5t567", .data = (void *)RN5T567 },
  	{ .compatible = "ricoh,rn5t618", .data = (void *)RN5T618 },
c5e589a17   Pierre-Hugues Husson   mfd: rn5t618: Add...
166
  	{ .compatible = "ricoh,rc5t619", .data = (void *)RC5T619 },
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
167
168
169
  	{ }
  };
  MODULE_DEVICE_TABLE(of, rn5t618_of_match);
7858658cd   Andreas Kemnade   mfd: rn5t618: Cle...
170
  static int rn5t618_i2c_probe(struct i2c_client *i2c)
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
171
  {
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
172
  	const struct of_device_id *of_id;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
173
174
  	struct rn5t618 *priv;
  	int ret;
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
175
176
177
178
179
180
  	of_id = of_match_device(rn5t618_of_match, &i2c->dev);
  	if (!of_id) {
  		dev_err(&i2c->dev, "Failed to find matching DT ID
  ");
  		return -EINVAL;
  	}
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
181
182
183
184
185
  	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
  	if (!priv)
  		return -ENOMEM;
  
  	i2c_set_clientdata(i2c, priv);
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
186
  	priv->variant = (long)of_id->data;
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
187
188
  	priv->irq = i2c->irq;
  	priv->dev = &i2c->dev;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
189
190
191
192
193
194
195
196
  
  	priv->regmap = devm_regmap_init_i2c(i2c, &rn5t618_regmap_config);
  	if (IS_ERR(priv->regmap)) {
  		ret = PTR_ERR(priv->regmap);
  		dev_err(&i2c->dev, "regmap init failed: %d
  ", ret);
  		return ret;
  	}
bc6167661   Andreas Kemnade   mfd: rn5t618: Add...
197
198
199
200
201
202
203
204
205
206
  	if (priv->variant == RC5T619)
  		ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
  					   rc5t619_cells,
  					   ARRAY_SIZE(rc5t619_cells),
  					   NULL, 0, NULL);
  	else
  		ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
  					   rn5t618_cells,
  					   ARRAY_SIZE(rn5t618_cells),
  					   NULL, 0, NULL);
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
207
208
209
210
211
  	if (ret) {
  		dev_err(&i2c->dev, "failed to add sub-devices: %d
  ", ret);
  		return ret;
  	}
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
212
  	rn5t618_pm_power_off = i2c;
fd65ae4cb   Stefan Agner   mfd: rn5t618: Reg...
213
  	if (of_device_is_system_power_controller(i2c->dev.of_node)) {
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
214
  		if (!pm_power_off)
fd65ae4cb   Stefan Agner   mfd: rn5t618: Reg...
215
  			pm_power_off = rn5t618_power_off;
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
216
  		else
fd65ae4cb   Stefan Agner   mfd: rn5t618: Reg...
217
218
  			dev_warn(&i2c->dev, "Poweroff callback already assigned
  ");
a370f60a5   Stefan Agner   mfd: rn5t618: Reg...
219
220
221
222
223
224
225
226
227
228
  	}
  
  	rn5t618_restart_handler.notifier_call = rn5t618_restart;
  	rn5t618_restart_handler.priority = 192;
  
  	ret = register_restart_handler(&rn5t618_restart_handler);
  	if (ret) {
  		dev_err(&i2c->dev, "cannot register restart handler, %d
  ", ret);
  		return ret;
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
229
  	}
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
230
  	return rn5t618_irq_init(priv);
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
231
232
233
234
  }
  
  static int rn5t618_i2c_remove(struct i2c_client *i2c)
  {
cf84dc0bb   Andreas Kemnade   mfd: rn5t618: Mak...
235
  	if (i2c == rn5t618_pm_power_off) {
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
236
237
238
  		rn5t618_pm_power_off = NULL;
  		pm_power_off = NULL;
  	}
ecca790a1   Stefan Agner   mfd: rn5t618: Unr...
239
  	unregister_restart_handler(&rn5t618_restart_handler);
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
240
241
  	return 0;
  }
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  static int __maybe_unused rn5t618_i2c_suspend(struct device *dev)
  {
  	struct rn5t618 *priv = dev_get_drvdata(dev);
  
  	if (priv->irq)
  		disable_irq(priv->irq);
  
  	return 0;
  }
  
  static int __maybe_unused rn5t618_i2c_resume(struct device *dev)
  {
  	struct rn5t618 *priv = dev_get_drvdata(dev);
  
  	if (priv->irq)
  		enable_irq(priv->irq);
  
  	return 0;
  }
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
261
262
263
  static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops,
  			rn5t618_i2c_suspend,
  			rn5t618_i2c_resume);
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
264
265
266
267
  static struct i2c_driver rn5t618_i2c_driver = {
  	.driver = {
  		.name = "rn5t618",
  		.of_match_table = of_match_ptr(rn5t618_of_match),
0c8160451   Andreas Kemnade   mfd: rn5t618: Add...
268
  		.pm = &rn5t618_i2c_dev_pm_ops,
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
269
  	},
7858658cd   Andreas Kemnade   mfd: rn5t618: Cle...
270
  	.probe_new = rn5t618_i2c_probe,
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
271
  	.remove = rn5t618_i2c_remove,
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
272
273
274
275
276
  };
  
  module_i2c_driver(rn5t618_i2c_driver);
  
  MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
a99ab50db   Stefan Agner   mfd: rn5t618: Add...
277
  MODULE_DESCRIPTION("Ricoh RN5T567/618 MFD driver");
9bb9e29c7   Beniamino Galvani   mfd: Add Ricoh RN...
278
  MODULE_LICENSE("GPL v2");