Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcf50633-core.c
Go to the documentation of this file.
1 /* NXP PCF50633 Power Management Unit (PMU) driver
2  *
3  * (C) 2006-2008 by Openmoko, Inc.
4  * Author: Harald Welte <[email protected]>
5  * Balaji Rao <[email protected]>
6  * All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  *
13  */
14 
15 #include <linux/kernel.h>
16 #include <linux/device.h>
17 #include <linux/sysfs.h>
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <linux/interrupt.h>
21 #include <linux/workqueue.h>
22 #include <linux/platform_device.h>
23 #include <linux/i2c.h>
24 #include <linux/pm.h>
25 #include <linux/slab.h>
26 #include <linux/regmap.h>
27 #include <linux/err.h>
28 
30 
31 /* Read a block of up to 32 regs */
33  int nr_regs, u8 *data)
34 {
35  int ret;
36 
37  ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
38  if (ret != 0)
39  return ret;
40 
41  return nr_regs;
42 }
44 
45 /* Write a block of up to 32 regs */
46 int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
47  int nr_regs, u8 *data)
48 {
49  return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
50 }
52 
54 {
55  unsigned int val;
56  int ret;
57 
58  ret = regmap_read(pcf->regmap, reg, &val);
59  if (ret < 0)
60  return -1;
61 
62  return val;
63 }
65 
66 int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
67 {
68  return regmap_write(pcf->regmap, reg, val);
69 }
71 
73 {
74  return regmap_update_bits(pcf->regmap, reg, mask, val);
75 }
77 
79 {
80  return regmap_update_bits(pcf->regmap, reg, val, 0);
81 }
83 
84 /* sysfs attributes */
85 static ssize_t show_dump_regs(struct device *dev, struct device_attribute *attr,
86  char *buf)
87 {
88  struct pcf50633 *pcf = dev_get_drvdata(dev);
89  u8 dump[16];
90  int n, n1, idx = 0;
91  char *buf1 = buf;
92  static u8 address_no_read[] = { /* must be ascending */
98  0 /* terminator */
99  };
100 
101  for (n = 0; n < 256; n += sizeof(dump)) {
102  for (n1 = 0; n1 < sizeof(dump); n1++)
103  if (n == address_no_read[idx]) {
104  idx++;
105  dump[n1] = 0x00;
106  } else
107  dump[n1] = pcf50633_reg_read(pcf, n + n1);
108 
109  hex_dump_to_buffer(dump, sizeof(dump), 16, 1, buf1, 128, 0);
110  buf1 += strlen(buf1);
111  *buf1++ = '\n';
112  *buf1 = '\0';
113  }
114 
115  return buf1 - buf;
116 }
117 static DEVICE_ATTR(dump_regs, 0400, show_dump_regs, NULL);
118 
119 static ssize_t show_resume_reason(struct device *dev,
120  struct device_attribute *attr, char *buf)
121 {
122  struct pcf50633 *pcf = dev_get_drvdata(dev);
123  int n;
124 
125  n = sprintf(buf, "%02x%02x%02x%02x%02x\n",
126  pcf->resume_reason[0],
127  pcf->resume_reason[1],
128  pcf->resume_reason[2],
129  pcf->resume_reason[3],
130  pcf->resume_reason[4]);
131 
132  return n;
133 }
134 static DEVICE_ATTR(resume_reason, 0400, show_resume_reason, NULL);
135 
136 static struct attribute *pcf_sysfs_entries[] = {
137  &dev_attr_dump_regs.attr,
138  &dev_attr_resume_reason.attr,
139  NULL,
140 };
141 
142 static struct attribute_group pcf_attr_group = {
143  .name = NULL, /* put in device directory */
144  .attrs = pcf_sysfs_entries,
145 };
146 
147 static void
148 pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
149  struct platform_device **pdev)
150 {
151  int ret;
152 
153  *pdev = platform_device_alloc(name, -1);
154  if (!*pdev) {
155  dev_err(pcf->dev, "Falied to allocate %s\n", name);
156  return;
157  }
158 
159  (*pdev)->dev.parent = pcf->dev;
160 
161  ret = platform_device_add(*pdev);
162  if (ret) {
163  dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret);
164  platform_device_put(*pdev);
165  *pdev = NULL;
166  }
167 }
168 
169 #ifdef CONFIG_PM_SLEEP
170 static int pcf50633_suspend(struct device *dev)
171 {
172  struct i2c_client *client = to_i2c_client(dev);
173  struct pcf50633 *pcf = i2c_get_clientdata(client);
174 
175  return pcf50633_irq_suspend(pcf);
176 }
177 
178 static int pcf50633_resume(struct device *dev)
179 {
180  struct i2c_client *client = to_i2c_client(dev);
181  struct pcf50633 *pcf = i2c_get_clientdata(client);
182 
183  return pcf50633_irq_resume(pcf);
184 }
185 #endif
186 
187 static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
188 
189 static struct regmap_config pcf50633_regmap_config = {
190  .reg_bits = 8,
191  .val_bits = 8,
192 };
193 
194 static int __devinit pcf50633_probe(struct i2c_client *client,
195  const struct i2c_device_id *ids)
196 {
197  struct pcf50633 *pcf;
198  struct pcf50633_platform_data *pdata = client->dev.platform_data;
199  int i, ret;
200  int version, variant;
201 
202  if (!client->irq) {
203  dev_err(&client->dev, "Missing IRQ\n");
204  return -ENOENT;
205  }
206 
207  pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
208  if (!pcf)
209  return -ENOMEM;
210 
211  pcf->pdata = pdata;
212 
213  mutex_init(&pcf->lock);
214 
215  pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
216  if (IS_ERR(pcf->regmap)) {
217  ret = PTR_ERR(pcf->regmap);
218  dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret);
219  return ret;
220  }
221 
222  i2c_set_clientdata(client, pcf);
223  pcf->dev = &client->dev;
224 
225  version = pcf50633_reg_read(pcf, 0);
226  variant = pcf50633_reg_read(pcf, 1);
227  if (version < 0 || variant < 0) {
228  dev_err(pcf->dev, "Unable to probe pcf50633\n");
229  ret = -ENODEV;
230  return ret;
231  }
232 
233  dev_info(pcf->dev, "Probed device version %d variant %d\n",
234  version, variant);
235 
236  pcf50633_irq_init(pcf, client->irq);
237 
238  /* Create sub devices */
239  pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
240  pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
241  pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
242  pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
243  pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
244 
245 
246  for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
247  struct platform_device *pdev;
248 
249  pdev = platform_device_alloc("pcf50633-regltr", i);
250  if (!pdev) {
251  dev_err(pcf->dev, "Cannot create regulator %d\n", i);
252  continue;
253  }
254 
255  pdev->dev.parent = pcf->dev;
256  if (platform_device_add_data(pdev, &pdata->reg_init_data[i],
257  sizeof(pdata->reg_init_data[i])) < 0) {
258  platform_device_put(pdev);
259  dev_err(pcf->dev, "Out of memory for regulator parameters %d\n",
260  i);
261  continue;
262  }
263  pcf->regulator_pdev[i] = pdev;
264 
265  platform_device_add(pdev);
266  }
267 
268  ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
269  if (ret)
270  dev_err(pcf->dev, "error creating sysfs entries\n");
271 
272  if (pdata->probe_done)
273  pdata->probe_done(pcf);
274 
275  return 0;
276 }
277 
278 static int __devexit pcf50633_remove(struct i2c_client *client)
279 {
280  struct pcf50633 *pcf = i2c_get_clientdata(client);
281  int i;
282 
283  sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
284  pcf50633_irq_free(pcf);
285 
291 
292  for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
294 
295  return 0;
296 }
297 
298 static const struct i2c_device_id pcf50633_id_table[] = {
299  {"pcf50633", 0x73},
300  {/* end of list */}
301 };
302 MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
303 
304 static struct i2c_driver pcf50633_driver = {
305  .driver = {
306  .name = "pcf50633",
307  .pm = &pcf50633_pm,
308  },
309  .id_table = pcf50633_id_table,
310  .probe = pcf50633_probe,
311  .remove = __devexit_p(pcf50633_remove),
312 };
313 
314 static int __init pcf50633_init(void)
315 {
316  return i2c_add_driver(&pcf50633_driver);
317 }
318 
319 static void __exit pcf50633_exit(void)
320 {
321  i2c_del_driver(&pcf50633_driver);
322 }
323 
324 MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
325 MODULE_AUTHOR("Harald Welte <[email protected]>");
326 MODULE_LICENSE("GPL");
327 
328 subsys_initcall(pcf50633_init);
329 module_exit(pcf50633_exit);