Blame view

drivers/mfd/pcf50633-core.c 7.83 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
105
106
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
141
142
143
144
  }
  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);
  
  		hex_dump_to_buffer(dump, sizeof(dump), 16, 1, buf1, 128, 0);
  		buf1 += strlen(buf1);
  		*buf1++ = '
  ';
  		*buf1 = '\0';
  	}
  
  	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...
145
146
147
148
  static void
  pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
  						struct platform_device **pdev)
  {
f52046b14   Balaji Rao   mfd: PCF50633 cor...
149
150
151
152
153
154
155
156
  	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...
157
158
159
160
161
162
163
164
165
166
  	(*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...
167
168
  #ifdef CONFIG_PM_SLEEP
  static int pcf50633_suspend(struct device *dev)
f52046b14   Balaji Rao   mfd: PCF50633 cor...
169
  {
939941d44   Mark Brown   mfd: Convert pcf5...
170
171
  	struct i2c_client *client = to_i2c_client(dev);
  	struct pcf50633 *pcf = i2c_get_clientdata(client);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
172

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

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

f52046b14   Balaji Rao   mfd: PCF50633 cor...
229
  	/* Create sub devices */
aa4603a0a   Axel Lin   mfd: Convert pcf5...
230
231
232
233
234
  	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...
235

f52046b14   Balaji Rao   mfd: PCF50633 cor...
236
237
238
  
  	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
  		struct platform_device *pdev;
561427f5e   Axel Lin   mfd: pcf50633: Co...
239
  		pdev = platform_device_alloc("pcf50633-regulator", i);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
240
  		if (!pdev) {
24213ae19   Lars-Peter Clausen   mfd: Cleanup pcf5...
241
242
  			dev_err(pcf->dev, "Cannot create regulator %d
  ", i);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
243
244
245
246
  			continue;
  		}
  
  		pdev->dev.parent = pcf->dev;
18273c5b4   Alan Cox   mfd: Add missing ...
247
248
249
250
251
252
253
254
  		if (platform_device_add_data(pdev, &pdata->reg_init_data[i],
  					sizeof(pdata->reg_init_data[i])) < 0) {
  			platform_device_put(pdev);
  			dev_err(pcf->dev, "Out of memory for regulator parameters %d
  ",
  									i);
  			continue;
  		}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
255
256
257
258
  		pcf->regulator_pdev[i] = pdev;
  
  		platform_device_add(pdev);
  	}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
259
260
261
262
263
264
265
266
267
  	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
  	if (ret)
  		dev_err(pcf->dev, "error creating sysfs entries
  ");
  
  	if (pdata->probe_done)
  		pdata->probe_done(pcf);
  
  	return 0;
f52046b14   Balaji Rao   mfd: PCF50633 cor...
268
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
269
  static int pcf50633_remove(struct i2c_client *client)
f52046b14   Balaji Rao   mfd: PCF50633 cor...
270
271
272
  {
  	struct pcf50633 *pcf = i2c_get_clientdata(client);
  	int i;
8220fe4cb   Axel Lin   mfd: Fix resource...
273
  	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
380c09f64   Lars-Peter Clausen   mfd: Move pcf5063...
274
  	pcf50633_irq_free(pcf);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
275
276
277
278
279
  
  	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...
280
  	platform_device_unregister(pcf->bl_pdev);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
281
282
283
  
  	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
  		platform_device_unregister(pcf->regulator_pdev[i]);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
284
285
  	return 0;
  }
1206552b0   Axel Lin   mfd: Constify i2c...
286
  static const struct i2c_device_id pcf50633_id_table[] = {
f52046b14   Balaji Rao   mfd: PCF50633 cor...
287
  	{"pcf50633", 0x73},
8915e5402   Jean Delvare   mfd: terminate pc...
288
  	{/* end of list */}
f52046b14   Balaji Rao   mfd: PCF50633 cor...
289
  };
7679089de   Axel Lin   mfd: Add MODULE_D...
290
  MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
291
292
293
294
  
  static struct i2c_driver pcf50633_driver = {
  	.driver = {
  		.name	= "pcf50633",
939941d44   Mark Brown   mfd: Convert pcf5...
295
  		.pm	= &pcf50633_pm,
f52046b14   Balaji Rao   mfd: PCF50633 cor...
296
297
298
  	},
  	.id_table = pcf50633_id_table,
  	.probe = pcf50633_probe,
84449216b   Bill Pemberton   mfd: remove use o...
299
  	.remove = pcf50633_remove,
f52046b14   Balaji Rao   mfd: PCF50633 cor...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  };
  
  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...
315
  subsys_initcall(pcf50633_init);
f52046b14   Balaji Rao   mfd: PCF50633 cor...
316
  module_exit(pcf50633_exit);