Blame view
drivers/mfd/pcf50633-core.c
7.83 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 |
int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, int nr_regs, u8 *data) { |
60b5c5a43 mfd: Make pcf5063... |
46 |
return regmap_raw_write(pcf->regmap, reg, data, nr_regs); |
f52046b14 mfd: PCF50633 cor... |
47 48 49 50 51 |
} EXPORT_SYMBOL_GPL(pcf50633_write_block); u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg) { |
6e3ad1180 mfd: Convert pcf5... |
52 53 |
unsigned int val; int ret; |
f52046b14 mfd: PCF50633 cor... |
54 |
|
6e3ad1180 mfd: Convert pcf5... |
55 56 57 |
ret = regmap_read(pcf->regmap, reg, &val); if (ret < 0) return -1; |
f52046b14 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 mfd: Convert pcf5... |
65 |
return regmap_write(pcf->regmap, reg, val); |
f52046b14 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 mfd: Convert pcf5... |
71 |
return regmap_update_bits(pcf->regmap, reg, mask, val); |
f52046b14 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 mfd: Convert pcf5... |
77 |
return regmap_update_bits(pcf->regmap, reg, val, 0); |
f52046b14 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 mfd: PCF50633 cor... |
145 146 147 148 |
static void pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, struct platform_device **pdev) { |
f52046b14 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 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 mfd: Convert pcf5... |
167 168 |
#ifdef CONFIG_PM_SLEEP static int pcf50633_suspend(struct device *dev) |
f52046b14 mfd: PCF50633 cor... |
169 |
{ |
939941d44 mfd: Convert pcf5... |
170 171 |
struct i2c_client *client = to_i2c_client(dev); struct pcf50633 *pcf = i2c_get_clientdata(client); |
f52046b14 mfd: PCF50633 cor... |
172 |
|
380c09f64 mfd: Move pcf5063... |
173 |
return pcf50633_irq_suspend(pcf); |
f52046b14 mfd: PCF50633 cor... |
174 |
} |
939941d44 mfd: Convert pcf5... |
175 |
static int pcf50633_resume(struct device *dev) |
f52046b14 mfd: PCF50633 cor... |
176 |
{ |
939941d44 mfd: Convert pcf5... |
177 178 |
struct i2c_client *client = to_i2c_client(dev); struct pcf50633 *pcf = i2c_get_clientdata(client); |
f52046b14 mfd: PCF50633 cor... |
179 |
|
380c09f64 mfd: Move pcf5063... |
180 |
return pcf50633_irq_resume(pcf); |
f52046b14 mfd: PCF50633 cor... |
181 |
} |
f52046b14 mfd: PCF50633 cor... |
182 |
#endif |
939941d44 mfd: Convert pcf5... |
183 |
static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); |
6e3ad1180 mfd: Convert pcf5... |
184 185 186 187 |
static struct regmap_config pcf50633_regmap_config = { .reg_bits = 8, .val_bits = 8, }; |
f791be492 mfd: remove use o... |
188 |
static int pcf50633_probe(struct i2c_client *client, |
f52046b14 mfd: PCF50633 cor... |
189 190 191 |
const struct i2c_device_id *ids) { struct pcf50633 *pcf; |
334a41ce9 mfd: Use dev_get_... |
192 |
struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev); |
24213ae19 mfd: Cleanup pcf5... |
193 |
int i, ret; |
f52046b14 mfd: PCF50633 cor... |
194 |
int version, variant; |
24213ae19 mfd: Cleanup pcf5... |
195 196 197 198 199 |
if (!client->irq) { dev_err(&client->dev, "Missing IRQ "); return -ENOENT; } |
aa4603a0a mfd: Convert pcf5... |
200 |
pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL); |
f52046b14 mfd: PCF50633 cor... |
201 202 |
if (!pcf) return -ENOMEM; |
b30dd8f2e mfd: pcf50633: In... |
203 204 |
i2c_set_clientdata(client, pcf); pcf->dev = &client->dev; |
f52046b14 mfd: PCF50633 cor... |
205 206 207 |
pcf->pdata = pdata; mutex_init(&pcf->lock); |
aa4603a0a mfd: Convert pcf5... |
208 |
pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config); |
6e3ad1180 mfd: Convert pcf5... |
209 210 |
if (IS_ERR(pcf->regmap)) { ret = PTR_ERR(pcf->regmap); |
aa4603a0a mfd: Convert pcf5... |
211 212 213 |
dev_err(pcf->dev, "Failed to allocate register map: %d ", ret); return ret; |
6e3ad1180 mfd: Convert pcf5... |
214 |
} |
f52046b14 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 mfd: Convert pcf5... |
221 |
return ret; |
f52046b14 mfd: PCF50633 cor... |
222 223 224 225 226 |
} dev_info(pcf->dev, "Probed device version %d variant %d ", version, variant); |
380c09f64 mfd: Move pcf5063... |
227 |
pcf50633_irq_init(pcf, client->irq); |
24213ae19 mfd: Cleanup pcf5... |
228 |
|
f52046b14 mfd: PCF50633 cor... |
229 |
/* Create sub devices */ |
aa4603a0a 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 backlight: pcf506... |
235 |
|
f52046b14 mfd: PCF50633 cor... |
236 237 238 |
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { struct platform_device *pdev; |
561427f5e mfd: pcf50633: Co... |
239 |
pdev = platform_device_alloc("pcf50633-regulator", i); |
f52046b14 mfd: PCF50633 cor... |
240 |
if (!pdev) { |
24213ae19 mfd: Cleanup pcf5... |
241 242 |
dev_err(pcf->dev, "Cannot create regulator %d ", i); |
f52046b14 mfd: PCF50633 cor... |
243 244 245 246 |
continue; } pdev->dev.parent = pcf->dev; |
18273c5b4 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 mfd: PCF50633 cor... |
255 256 257 258 |
pcf->regulator_pdev[i] = pdev; platform_device_add(pdev); } |
f52046b14 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 mfd: PCF50633 cor... |
268 |
} |
4740f73fe mfd: remove use o... |
269 |
static int pcf50633_remove(struct i2c_client *client) |
f52046b14 mfd: PCF50633 cor... |
270 271 272 |
{ struct pcf50633 *pcf = i2c_get_clientdata(client); int i; |
8220fe4cb mfd: Fix resource... |
273 |
sysfs_remove_group(&client->dev.kobj, &pcf_attr_group); |
380c09f64 mfd: Move pcf5063... |
274 |
pcf50633_irq_free(pcf); |
f52046b14 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 mfd: Fix resource... |
280 |
platform_device_unregister(pcf->bl_pdev); |
f52046b14 mfd: PCF50633 cor... |
281 282 283 |
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); |
f52046b14 mfd: PCF50633 cor... |
284 285 |
return 0; } |
1206552b0 mfd: Constify i2c... |
286 |
static const struct i2c_device_id pcf50633_id_table[] = { |
f52046b14 mfd: PCF50633 cor... |
287 |
{"pcf50633", 0x73}, |
8915e5402 mfd: terminate pc... |
288 |
{/* end of list */} |
f52046b14 mfd: PCF50633 cor... |
289 |
}; |
7679089de mfd: Add MODULE_D... |
290 |
MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); |
f52046b14 mfd: PCF50633 cor... |
291 292 293 294 |
static struct i2c_driver pcf50633_driver = { .driver = { .name = "pcf50633", |
939941d44 mfd: Convert pcf5... |
295 |
.pm = &pcf50633_pm, |
f52046b14 mfd: PCF50633 cor... |
296 297 298 |
}, .id_table = pcf50633_id_table, .probe = pcf50633_probe, |
84449216b mfd: remove use o... |
299 |
.remove = pcf50633_remove, |
f52046b14 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 mfd: early init f... |
315 |
subsys_initcall(pcf50633_init); |
f52046b14 mfd: PCF50633 cor... |
316 |
module_exit(pcf50633_exit); |