Blame view
drivers/mfd/pcf50633-core.c
7.91 KB
f52046b14 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 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 mfd: Convert pcf5... |
24 |
#include <linux/pm.h> |
5a0e3ad6a include cleanup: ... |
25 |
#include <linux/slab.h> |
6e3ad1180 mfd: Convert pcf5... |
26 27 |
#include <linux/regmap.h> #include <linux/err.h> |
f52046b14 mfd: PCF50633 cor... |
28 29 |
#include <linux/mfd/pcf50633/core.h> |
25985edce Fix common misspe... |
30 |
/* Read a block of up to 32 regs */ |
f52046b14 mfd: PCF50633 cor... |
31 32 33 34 |
int pcf50633_read_block(struct pcf50633 *pcf, u8 reg, int nr_regs, u8 *data) { int ret; |
6e3ad1180 mfd: Convert pcf5... |
35 36 37 |
ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs); if (ret != 0) return ret; |
f52046b14 mfd: PCF50633 cor... |
38 |
|
6e3ad1180 mfd: Convert pcf5... |
39 |
return nr_regs; |
f52046b14 mfd: PCF50633 cor... |
40 41 |
} EXPORT_SYMBOL_GPL(pcf50633_read_block); |
25985edce Fix common misspe... |
42 |
/* Write a block of up to 32 regs */ |
f52046b14 mfd: PCF50633 cor... |
43 44 45 46 |
int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, int nr_regs, u8 *data) { int ret; |
6e3ad1180 mfd: Convert pcf5... |
47 48 49 |
ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs); if (ret != 0) return ret; |
f52046b14 mfd: PCF50633 cor... |
50 |
|
6e3ad1180 mfd: Convert pcf5... |
51 |
return nr_regs; |
f52046b14 mfd: PCF50633 cor... |
52 53 54 55 56 |
} EXPORT_SYMBOL_GPL(pcf50633_write_block); u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg) { |
6e3ad1180 mfd: Convert pcf5... |
57 58 |
unsigned int val; int ret; |
f52046b14 mfd: PCF50633 cor... |
59 |
|
6e3ad1180 mfd: Convert pcf5... |
60 61 62 |
ret = regmap_read(pcf->regmap, reg, &val); if (ret < 0) return -1; |
f52046b14 mfd: PCF50633 cor... |
63 64 65 66 67 68 69 |
return val; } EXPORT_SYMBOL_GPL(pcf50633_reg_read); int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val) { |
6e3ad1180 mfd: Convert pcf5... |
70 |
return regmap_write(pcf->regmap, reg, val); |
f52046b14 mfd: PCF50633 cor... |
71 72 73 74 75 |
} EXPORT_SYMBOL_GPL(pcf50633_reg_write); int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val) { |
6e3ad1180 mfd: Convert pcf5... |
76 |
return regmap_update_bits(pcf->regmap, reg, mask, val); |
f52046b14 mfd: PCF50633 cor... |
77 78 79 80 81 |
} EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask); int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val) { |
6e3ad1180 mfd: Convert pcf5... |
82 |
return regmap_update_bits(pcf->regmap, reg, val, 0); |
f52046b14 mfd: PCF50633 cor... |
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 145 146 147 148 149 |
} 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 mfd: PCF50633 cor... |
150 151 152 153 |
static void pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, struct platform_device **pdev) { |
f52046b14 mfd: PCF50633 cor... |
154 155 156 157 158 159 160 161 |
int ret; *pdev = platform_device_alloc(name, -1); if (!*pdev) { dev_err(pcf->dev, "Falied to allocate %s ", name); return; } |
f52046b14 mfd: PCF50633 cor... |
162 163 164 165 166 167 168 169 170 171 |
(*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 mfd: Convert pcf5... |
172 173 |
#ifdef CONFIG_PM_SLEEP static int pcf50633_suspend(struct device *dev) |
f52046b14 mfd: PCF50633 cor... |
174 |
{ |
939941d44 mfd: Convert pcf5... |
175 176 |
struct i2c_client *client = to_i2c_client(dev); struct pcf50633 *pcf = i2c_get_clientdata(client); |
f52046b14 mfd: PCF50633 cor... |
177 |
|
380c09f64 mfd: Move pcf5063... |
178 |
return pcf50633_irq_suspend(pcf); |
f52046b14 mfd: PCF50633 cor... |
179 |
} |
939941d44 mfd: Convert pcf5... |
180 |
static int pcf50633_resume(struct device *dev) |
f52046b14 mfd: PCF50633 cor... |
181 |
{ |
939941d44 mfd: Convert pcf5... |
182 183 |
struct i2c_client *client = to_i2c_client(dev); struct pcf50633 *pcf = i2c_get_clientdata(client); |
f52046b14 mfd: PCF50633 cor... |
184 |
|
380c09f64 mfd: Move pcf5063... |
185 |
return pcf50633_irq_resume(pcf); |
f52046b14 mfd: PCF50633 cor... |
186 |
} |
f52046b14 mfd: PCF50633 cor... |
187 |
#endif |
939941d44 mfd: Convert pcf5... |
188 |
static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); |
6e3ad1180 mfd: Convert pcf5... |
189 190 191 192 |
static struct regmap_config pcf50633_regmap_config = { .reg_bits = 8, .val_bits = 8, }; |
f52046b14 mfd: PCF50633 cor... |
193 194 195 196 197 |
static int __devinit pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { struct pcf50633 *pcf; struct pcf50633_platform_data *pdata = client->dev.platform_data; |
24213ae19 mfd: Cleanup pcf5... |
198 |
int i, ret; |
f52046b14 mfd: PCF50633 cor... |
199 |
int version, variant; |
24213ae19 mfd: Cleanup pcf5... |
200 201 202 203 204 |
if (!client->irq) { dev_err(&client->dev, "Missing IRQ "); return -ENOENT; } |
f52046b14 mfd: PCF50633 cor... |
205 206 207 208 209 210 211 |
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL); if (!pcf) return -ENOMEM; pcf->pdata = pdata; mutex_init(&pcf->lock); |
6e3ad1180 mfd: Convert pcf5... |
212 213 214 215 216 217 218 219 |
pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config); if (IS_ERR(pcf->regmap)) { ret = PTR_ERR(pcf->regmap); dev_err(pcf->dev, "Failed to allocate register map: %d ", ret); goto err_free; } |
f52046b14 mfd: PCF50633 cor... |
220 221 |
i2c_set_clientdata(client, pcf); pcf->dev = &client->dev; |
f52046b14 mfd: PCF50633 cor... |
222 223 224 225 226 227 228 |
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; |
6e3ad1180 mfd: Convert pcf5... |
229 |
goto err_regmap; |
f52046b14 mfd: PCF50633 cor... |
230 231 232 233 234 |
} dev_info(pcf->dev, "Probed device version %d variant %d ", version, variant); |
380c09f64 mfd: Move pcf5063... |
235 |
pcf50633_irq_init(pcf, client->irq); |
24213ae19 mfd: Cleanup pcf5... |
236 |
|
f52046b14 mfd: PCF50633 cor... |
237 238 239 240 241 242 243 244 245 |
/* Create sub devices */ 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); |
f5bf403a9 backlight: pcf506... |
246 247 |
pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev); |
f52046b14 mfd: PCF50633 cor... |
248 249 250 251 252 253 |
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { struct platform_device *pdev; pdev = platform_device_alloc("pcf50633-regltr", i); if (!pdev) { |
24213ae19 mfd: Cleanup pcf5... |
254 255 |
dev_err(pcf->dev, "Cannot create regulator %d ", i); |
f52046b14 mfd: PCF50633 cor... |
256 257 258 259 |
continue; } pdev->dev.parent = pcf->dev; |
bbb2e496f mfd: Use platform... |
260 261 |
platform_device_add_data(pdev, &pdata->reg_init_data[i], sizeof(pdata->reg_init_data[i])); |
f52046b14 mfd: PCF50633 cor... |
262 263 264 265 |
pcf->regulator_pdev[i] = pdev; platform_device_add(pdev); } |
f52046b14 mfd: PCF50633 cor... |
266 267 268 269 270 271 272 273 274 |
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; |
6e3ad1180 mfd: Convert pcf5... |
275 276 |
err_regmap: regmap_exit(pcf->regmap); |
24213ae19 mfd: Cleanup pcf5... |
277 |
err_free: |
f52046b14 mfd: PCF50633 cor... |
278 |
kfree(pcf); |
24213ae19 mfd: Cleanup pcf5... |
279 |
|
f52046b14 mfd: PCF50633 cor... |
280 281 282 283 284 285 286 |
return ret; } static int __devexit pcf50633_remove(struct i2c_client *client) { struct pcf50633 *pcf = i2c_get_clientdata(client); int i; |
8220fe4cb mfd: Fix resource... |
287 |
sysfs_remove_group(&client->dev.kobj, &pcf_attr_group); |
380c09f64 mfd: Move pcf5063... |
288 |
pcf50633_irq_free(pcf); |
f52046b14 mfd: PCF50633 cor... |
289 290 291 292 293 |
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 mfd: Fix resource... |
294 |
platform_device_unregister(pcf->bl_pdev); |
f52046b14 mfd: PCF50633 cor... |
295 296 297 |
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); |
6e3ad1180 mfd: Convert pcf5... |
298 |
regmap_exit(pcf->regmap); |
f52046b14 mfd: PCF50633 cor... |
299 300 301 302 |
kfree(pcf); return 0; } |
1206552b0 mfd: Constify i2c... |
303 |
static const struct i2c_device_id pcf50633_id_table[] = { |
f52046b14 mfd: PCF50633 cor... |
304 |
{"pcf50633", 0x73}, |
8915e5402 mfd: terminate pc... |
305 |
{/* end of list */} |
f52046b14 mfd: PCF50633 cor... |
306 |
}; |
7679089de mfd: Add MODULE_D... |
307 |
MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); |
f52046b14 mfd: PCF50633 cor... |
308 309 310 311 |
static struct i2c_driver pcf50633_driver = { .driver = { .name = "pcf50633", |
939941d44 mfd: Convert pcf5... |
312 |
.pm = &pcf50633_pm, |
f52046b14 mfd: PCF50633 cor... |
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
}, .id_table = pcf50633_id_table, .probe = pcf50633_probe, .remove = __devexit_p(pcf50633_remove), }; 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 mfd: early init f... |
332 |
subsys_initcall(pcf50633_init); |
f52046b14 mfd: PCF50633 cor... |
333 |
module_exit(pcf50633_exit); |