Blame view

drivers/mfd/pcf50633-core.c 7.77 KB
f52046b14   Balaji Rao   mfd: PCF50633 cor...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /* NXP PCF50633 Power Management Unit (PMU) driver
   *
   * (C) 2006-2008 by Openmoko, Inc.
   * Author: Harald Welte <laforge@openmoko.org>
   * 	   Balaji Rao <balajirrao@openmoko.org>
   * All rights reserved.
   *
   *  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/kernel.h>
  #include <linux/device.h>
  #include <linux/sysfs.h>
f52046b14   Balaji Rao   mfd: PCF50633 cor...
18
19
20
21
22
23
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/interrupt.h>
  #include <linux/workqueue.h>
  #include <linux/platform_device.h>
  #include <linux/i2c.h>
939941d44   Mark Brown   mfd: Convert pcf5...
24
  #include <linux/pm.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
6e3ad1180   Mark Brown   mfd: Convert pcf5...
26
27
  #include <linux/regmap.h>
  #include <linux/err.h>
f52046b14   Balaji Rao   mfd: PCF50633 cor...
28
29
  
  #include <linux/mfd/pcf50633/core.h>
25985edce   Lucas De Marchi   Fix common misspe...
30
  /* Read a block of up to 32 regs  */
f52046b14   Balaji Rao   mfd: PCF50633 cor...
31
32
33
34
  int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
  					int nr_regs, u8 *data)
  {
  	int ret;
6e3ad1180   Mark Brown   mfd: Convert pcf5...
35
36
37
  	ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
  	if (ret != 0)
  		return ret;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
38

6e3ad1180   Mark Brown   mfd: Convert pcf5...
39
  	return nr_regs;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
40
41
  }
  EXPORT_SYMBOL_GPL(pcf50633_read_block);
25985edce   Lucas De Marchi   Fix common misspe...
42
  /* Write a block of up to 32 regs  */
f52046b14   Balaji Rao   mfd: PCF50633 cor...
43
44
45
  int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
  					int nr_regs, u8 *data)
  {
60b5c5a43   Axel Lin   mfd: Make pcf5063...
46
  	return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
47
48
49
50
51
  }
  EXPORT_SYMBOL_GPL(pcf50633_write_block);
  
  u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
  {
6e3ad1180   Mark Brown   mfd: Convert pcf5...
52
53
  	unsigned int val;
  	int ret;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
54

6e3ad1180   Mark Brown   mfd: Convert pcf5...
55
56
57
  	ret = regmap_read(pcf->regmap, reg, &val);
  	if (ret < 0)
  		return -1;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
58
59
60
61
62
63
64
  
  	return val;
  }
  EXPORT_SYMBOL_GPL(pcf50633_reg_read);
  
  int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
  {
6e3ad1180   Mark Brown   mfd: Convert pcf5...
65
  	return regmap_write(pcf->regmap, reg, val);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
66
67
68
69
70
  }
  EXPORT_SYMBOL_GPL(pcf50633_reg_write);
  
  int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
  {
6e3ad1180   Mark Brown   mfd: Convert pcf5...
71
  	return regmap_update_bits(pcf->regmap, reg, mask, val);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
72
73
74
75
76
  }
  EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
  
  int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
  {
6e3ad1180   Mark Brown   mfd: Convert pcf5...
77
  	return regmap_update_bits(pcf->regmap, reg, val, 0);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  }
  EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
  
  /* sysfs attributes */
  static ssize_t show_dump_regs(struct device *dev, struct device_attribute *attr,
  			    char *buf)
  {
  	struct pcf50633 *pcf = dev_get_drvdata(dev);
  	u8 dump[16];
  	int n, n1, idx = 0;
  	char *buf1 = buf;
  	static u8 address_no_read[] = { /* must be ascending */
  		PCF50633_REG_INT1,
  		PCF50633_REG_INT2,
  		PCF50633_REG_INT3,
  		PCF50633_REG_INT4,
  		PCF50633_REG_INT5,
  		0 /* terminator */
  	};
  
  	for (n = 0; n < 256; n += sizeof(dump)) {
  		for (n1 = 0; n1 < sizeof(dump); n1++)
  			if (n == address_no_read[idx]) {
  				idx++;
  				dump[n1] = 0x00;
  			} else
  				dump[n1] = pcf50633_reg_read(pcf, n + n1);
970d9fbca   Andy Shevchenko   mfd: pcf50633: Us...
105
106
  		buf1 += sprintf(buf1, "%*ph
  ", (int)sizeof(dump), dump);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
107
108
109
110
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
137
138
139
140
  	}
  
  	return buf1 - buf;
  }
  static DEVICE_ATTR(dump_regs, 0400, show_dump_regs, NULL);
  
  static ssize_t show_resume_reason(struct device *dev,
  				struct device_attribute *attr, char *buf)
  {
  	struct pcf50633 *pcf = dev_get_drvdata(dev);
  	int n;
  
  	n = sprintf(buf, "%02x%02x%02x%02x%02x
  ",
  				pcf->resume_reason[0],
  				pcf->resume_reason[1],
  				pcf->resume_reason[2],
  				pcf->resume_reason[3],
  				pcf->resume_reason[4]);
  
  	return n;
  }
  static DEVICE_ATTR(resume_reason, 0400, show_resume_reason, NULL);
  
  static struct attribute *pcf_sysfs_entries[] = {
  	&dev_attr_dump_regs.attr,
  	&dev_attr_resume_reason.attr,
  	NULL,
  };
  
  static struct attribute_group pcf_attr_group = {
  	.name	= NULL,			/* put in device directory */
  	.attrs	= pcf_sysfs_entries,
  };
f52046b14   Balaji Rao   mfd: PCF50633 cor...
141
142
143
144
  static void
  pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
  						struct platform_device **pdev)
  {
f52046b14   Balaji Rao   mfd: PCF50633 cor...
145
146
147
148
149
150
151
152
  	int ret;
  
  	*pdev = platform_device_alloc(name, -1);
  	if (!*pdev) {
  		dev_err(pcf->dev, "Falied to allocate %s
  ", name);
  		return;
  	}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
153
154
155
156
157
158
159
160
161
162
  	(*pdev)->dev.parent = pcf->dev;
  
  	ret = platform_device_add(*pdev);
  	if (ret) {
  		dev_err(pcf->dev, "Failed to register %s: %d
  ", name, ret);
  		platform_device_put(*pdev);
  		*pdev = NULL;
  	}
  }
939941d44   Mark Brown   mfd: Convert pcf5...
163
164
  #ifdef CONFIG_PM_SLEEP
  static int pcf50633_suspend(struct device *dev)
f52046b14   Balaji Rao   mfd: PCF50633 cor...
165
  {
939941d44   Mark Brown   mfd: Convert pcf5...
166
167
  	struct i2c_client *client = to_i2c_client(dev);
  	struct pcf50633 *pcf = i2c_get_clientdata(client);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
168

380c09f64   Lars-Peter Clausen   mfd: Move pcf5063...
169
  	return pcf50633_irq_suspend(pcf);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
170
  }
939941d44   Mark Brown   mfd: Convert pcf5...
171
  static int pcf50633_resume(struct device *dev)
f52046b14   Balaji Rao   mfd: PCF50633 cor...
172
  {
939941d44   Mark Brown   mfd: Convert pcf5...
173
174
  	struct i2c_client *client = to_i2c_client(dev);
  	struct pcf50633 *pcf = i2c_get_clientdata(client);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
175

380c09f64   Lars-Peter Clausen   mfd: Move pcf5063...
176
  	return pcf50633_irq_resume(pcf);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
177
  }
f52046b14   Balaji Rao   mfd: PCF50633 cor...
178
  #endif
939941d44   Mark Brown   mfd: Convert pcf5...
179
  static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
1590d4a17   Krzysztof Kozlowski   mfd: pcf50633: Co...
180
  static const struct regmap_config pcf50633_regmap_config = {
6e3ad1180   Mark Brown   mfd: Convert pcf5...
181
182
183
  	.reg_bits = 8,
  	.val_bits = 8,
  };
f791be492   Bill Pemberton   mfd: remove use o...
184
  static int pcf50633_probe(struct i2c_client *client,
f52046b14   Balaji Rao   mfd: PCF50633 cor...
185
186
187
  				const struct i2c_device_id *ids)
  {
  	struct pcf50633 *pcf;
cddc11412   Lee Jones   mfd: pcf50633: Ch...
188
  	struct platform_device *pdev;
334a41ce9   Jingoo Han   mfd: Use dev_get_...
189
  	struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev);
cddc11412   Lee Jones   mfd: pcf50633: Ch...
190
  	int i, j, ret;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
191
  	int version, variant;
24213ae19   Lars-Peter Clausen   mfd: Cleanup pcf5...
192
193
194
195
196
  	if (!client->irq) {
  		dev_err(&client->dev, "Missing IRQ
  ");
  		return -ENOENT;
  	}
aa4603a0a   Axel Lin   mfd: Convert pcf5...
197
  	pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
198
199
  	if (!pcf)
  		return -ENOMEM;
b30dd8f2e   Axel Lin   mfd: pcf50633: In...
200
201
  	i2c_set_clientdata(client, pcf);
  	pcf->dev = &client->dev;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
202
203
204
  	pcf->pdata = pdata;
  
  	mutex_init(&pcf->lock);
aa4603a0a   Axel Lin   mfd: Convert pcf5...
205
  	pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
6e3ad1180   Mark Brown   mfd: Convert pcf5...
206
207
  	if (IS_ERR(pcf->regmap)) {
  		ret = PTR_ERR(pcf->regmap);
aa4603a0a   Axel Lin   mfd: Convert pcf5...
208
209
210
  		dev_err(pcf->dev, "Failed to allocate register map: %d
  ", ret);
  		return ret;
6e3ad1180   Mark Brown   mfd: Convert pcf5...
211
  	}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
212
213
214
215
216
217
  	version = pcf50633_reg_read(pcf, 0);
  	variant = pcf50633_reg_read(pcf, 1);
  	if (version < 0 || variant < 0) {
  		dev_err(pcf->dev, "Unable to probe pcf50633
  ");
  		ret = -ENODEV;
aa4603a0a   Axel Lin   mfd: Convert pcf5...
218
  		return ret;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
219
220
221
222
223
  	}
  
  	dev_info(pcf->dev, "Probed device version %d variant %d
  ",
  							version, variant);
380c09f64   Lars-Peter Clausen   mfd: Move pcf5063...
224
  	pcf50633_irq_init(pcf, client->irq);
24213ae19   Lars-Peter Clausen   mfd: Cleanup pcf5...
225

f52046b14   Balaji Rao   mfd: PCF50633 cor...
226
  	/* Create sub devices */
aa4603a0a   Axel Lin   mfd: Convert pcf5...
227
228
229
230
231
  	pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
  	pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
  	pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
  	pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
  	pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
f5bf403a9   Lars-Peter Clausen   backlight: pcf506...
232

f52046b14   Balaji Rao   mfd: PCF50633 cor...
233
234
  
  	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
561427f5e   Axel Lin   mfd: pcf50633: Co...
235
  		pdev = platform_device_alloc("pcf50633-regulator", i);
c981015e5   Lee Jones   mfd: pcf50633: Re...
236
237
  		if (!pdev)
  			return -ENOMEM;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
238
239
  
  		pdev->dev.parent = pcf->dev;
c981015e5   Lee Jones   mfd: pcf50633: Re...
240
241
  		ret = platform_device_add_data(pdev, &pdata->reg_init_data[i],
  					       sizeof(pdata->reg_init_data[i]));
cddc11412   Lee Jones   mfd: pcf50633: Ch...
242
243
244
245
246
247
  		if (ret)
  			goto err;
  
  		ret = platform_device_add(pdev);
  		if (ret)
  			goto err;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
248

cddc11412   Lee Jones   mfd: pcf50633: Ch...
249
  		pcf->regulator_pdev[i] = pdev;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
250
  	}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
251
252
  	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
  	if (ret)
cddc11412   Lee Jones   mfd: pcf50633: Ch...
253
254
  		dev_warn(pcf->dev, "error creating sysfs entries
  ");
f52046b14   Balaji Rao   mfd: PCF50633 cor...
255
256
257
258
259
  
  	if (pdata->probe_done)
  		pdata->probe_done(pcf);
  
  	return 0;
cddc11412   Lee Jones   mfd: pcf50633: Ch...
260
261
262
263
264
265
266
  
  err:
  	platform_device_put(pdev);
  	for (j = 0; j < i; j++)
  		platform_device_put(pcf->regulator_pdev[j]);
  
  	return ret;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
267
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
268
  static int pcf50633_remove(struct i2c_client *client)
f52046b14   Balaji Rao   mfd: PCF50633 cor...
269
270
271
  {
  	struct pcf50633 *pcf = i2c_get_clientdata(client);
  	int i;
8220fe4cb   Axel Lin   mfd: Fix resource...
272
  	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
380c09f64   Lars-Peter Clausen   mfd: Move pcf5063...
273
  	pcf50633_irq_free(pcf);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
274
275
276
277
278
  
  	platform_device_unregister(pcf->input_pdev);
  	platform_device_unregister(pcf->rtc_pdev);
  	platform_device_unregister(pcf->mbc_pdev);
  	platform_device_unregister(pcf->adc_pdev);
8220fe4cb   Axel Lin   mfd: Fix resource...
279
  	platform_device_unregister(pcf->bl_pdev);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
280
281
282
  
  	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
  		platform_device_unregister(pcf->regulator_pdev[i]);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
283
284
  	return 0;
  }
1206552b0   Axel Lin   mfd: Constify i2c...
285
  static const struct i2c_device_id pcf50633_id_table[] = {
f52046b14   Balaji Rao   mfd: PCF50633 cor...
286
  	{"pcf50633", 0x73},
8915e5402   Jean Delvare   mfd: terminate pc...
287
  	{/* end of list */}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
288
  };
7679089de   Axel Lin   mfd: Add MODULE_D...
289
  MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
290
291
292
293
  
  static struct i2c_driver pcf50633_driver = {
  	.driver = {
  		.name	= "pcf50633",
939941d44   Mark Brown   mfd: Convert pcf5...
294
  		.pm	= &pcf50633_pm,
f52046b14   Balaji Rao   mfd: PCF50633 cor...
295
296
297
  	},
  	.id_table = pcf50633_id_table,
  	.probe = pcf50633_probe,
84449216b   Bill Pemberton   mfd: remove use o...
298
  	.remove = pcf50633_remove,
f52046b14   Balaji Rao   mfd: PCF50633 cor...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  };
  
  static int __init pcf50633_init(void)
  {
  	return i2c_add_driver(&pcf50633_driver);
  }
  
  static void __exit pcf50633_exit(void)
  {
  	i2c_del_driver(&pcf50633_driver);
  }
  
  MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
  MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
  MODULE_LICENSE("GPL");
2021de874   Samuel Ortiz   mfd: early init f...
314
  subsys_initcall(pcf50633_init);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
315
  module_exit(pcf50633_exit);