Blame view
drivers/mfd/wm8350-core.c
11.3 KB
89b4012be mfd: Core support... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * wm8350-core.c -- Device access for Wolfson WM8350 * * Copyright 2007, 2008 Wolfson Microelectronics PLC. * * Author: Liam Girdwood, Mark Brown * * 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/module.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
ebccec0fa mfd: Add WM8350 i... |
18 |
#include <linux/bug.h> |
89b4012be mfd: Core support... |
19 20 21 |
#include <linux/device.h> #include <linux/delay.h> #include <linux/interrupt.h> |
b7b142d9f mfd: Convert wm83... |
22 |
#include <linux/regmap.h> |
ebccec0fa mfd: Add WM8350 i... |
23 |
#include <linux/workqueue.h> |
89b4012be mfd: Core support... |
24 25 26 |
#include <linux/mfd/wm8350/core.h> #include <linux/mfd/wm8350/audio.h> |
ebccec0fa mfd: Add WM8350 i... |
27 |
#include <linux/mfd/wm8350/comparator.h> |
89b4012be mfd: Core support... |
28 29 |
#include <linux/mfd/wm8350/gpio.h> #include <linux/mfd/wm8350/pmic.h> |
ebccec0fa mfd: Add WM8350 i... |
30 |
#include <linux/mfd/wm8350/rtc.h> |
89b4012be mfd: Core support... |
31 |
#include <linux/mfd/wm8350/supply.h> |
ebccec0fa mfd: Add WM8350 i... |
32 |
#include <linux/mfd/wm8350/wdt.h> |
89b4012be mfd: Core support... |
33 |
|
89b4012be mfd: Core support... |
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#define WM8350_CLOCK_CONTROL_1 0x28 #define WM8350_AIF_TEST 0x74 /* debug */ #define WM8350_BUS_DEBUG 0 #if WM8350_BUS_DEBUG #define dump(regs, src) do { \ int i_; \ u16 *src_ = src; \ printk(KERN_DEBUG); \ for (i_ = 0; i_ < regs; i_++) \ printk(" 0x%4.4x", *src_++); \ printk(" "); \ } while (0); #else #define dump(bytes, src) #endif #define WM8350_LOCK_DEBUG 0 #if WM8350_LOCK_DEBUG #define ldbg(format, arg...) printk(format, ## arg) #else #define ldbg(format, arg...) #endif /* * WM8350 Device IO */ |
89b4012be mfd: Core support... |
63 |
static DEFINE_MUTEX(reg_lock_mutex); |
89b4012be mfd: Core support... |
64 |
|
89b4012be mfd: Core support... |
65 66 67 68 69 |
/* * Safe read, modify, write methods */ int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { |
19d57ed5a mfd: Remove custo... |
70 |
return regmap_update_bits(wm8350->regmap, reg, mask, 0); |
89b4012be mfd: Core support... |
71 72 73 74 75 |
} EXPORT_SYMBOL_GPL(wm8350_clear_bits); int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { |
19d57ed5a mfd: Remove custo... |
76 |
return regmap_update_bits(wm8350->regmap, reg, mask, mask); |
89b4012be mfd: Core support... |
77 78 79 80 81 |
} EXPORT_SYMBOL_GPL(wm8350_set_bits); u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) { |
19d57ed5a mfd: Remove custo... |
82 |
unsigned int data; |
89b4012be mfd: Core support... |
83 |
int err; |
19d57ed5a mfd: Remove custo... |
84 |
err = regmap_read(wm8350->regmap, reg, &data); |
89b4012be mfd: Core support... |
85 86 87 |
if (err) dev_err(wm8350->dev, "read from reg R%d failed ", reg); |
89b4012be mfd: Core support... |
88 89 90 91 92 93 94 |
return data; } EXPORT_SYMBOL_GPL(wm8350_reg_read); int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) { int ret; |
89b4012be mfd: Core support... |
95 |
|
19d57ed5a mfd: Remove custo... |
96 |
ret = regmap_write(wm8350->regmap, reg, val); |
89b4012be mfd: Core support... |
97 98 99 |
if (ret) dev_err(wm8350->dev, "write to reg R%d failed ", reg); |
89b4012be mfd: Core support... |
100 101 102 103 104 105 106 107 |
return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_write); int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, u16 *dest) { int err = 0; |
19d57ed5a mfd: Remove custo... |
108 |
err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs); |
89b4012be mfd: Core support... |
109 110 111 112 |
if (err) dev_err(wm8350->dev, "block read starting from R%d failed ", start_reg); |
19d57ed5a mfd: Remove custo... |
113 |
|
89b4012be mfd: Core support... |
114 115 116 117 118 119 120 121 |
return err; } EXPORT_SYMBOL_GPL(wm8350_block_read); int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, u16 *src) { int ret = 0; |
19d57ed5a mfd: Remove custo... |
122 |
ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs); |
89b4012be mfd: Core support... |
123 124 125 126 |
if (ret) dev_err(wm8350->dev, "block write starting at R%d failed ", start_reg); |
19d57ed5a mfd: Remove custo... |
127 |
|
89b4012be mfd: Core support... |
128 129 130 |
return ret; } EXPORT_SYMBOL_GPL(wm8350_block_write); |
858e67446 mfd: Add some doc... |
131 132 133 134 135 136 137 |
/** * wm8350_reg_lock() * * The WM8350 has a hardware lock which can be used to prevent writes to * some registers (generally those which can cause particularly serious * problems if misused). This function enables that lock. */ |
89b4012be mfd: Core support... |
138 139 |
int wm8350_reg_lock(struct wm8350 *wm8350) { |
89b4012be mfd: Core support... |
140 |
int ret; |
52b461b86 mfd: Add regmap c... |
141 |
mutex_lock(®_lock_mutex); |
89b4012be mfd: Core support... |
142 |
ldbg(__func__); |
52b461b86 mfd: Add regmap c... |
143 144 |
ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY); |
89b4012be mfd: Core support... |
145 146 147 |
if (ret) dev_err(wm8350->dev, "lock failed "); |
52b461b86 mfd: Add regmap c... |
148 149 150 151 |
wm8350->unlocked = false; mutex_unlock(®_lock_mutex); |
89b4012be mfd: Core support... |
152 153 154 |
return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_lock); |
858e67446 mfd: Add some doc... |
155 156 157 158 159 160 161 162 163 |
/** * wm8350_reg_unlock() * * The WM8350 has a hardware lock which can be used to prevent writes to * some registers (generally those which can cause particularly serious * problems if misused). This function disables that lock so updates * can be performed. For maximum safety this should be done only when * required. */ |
89b4012be mfd: Core support... |
164 165 |
int wm8350_reg_unlock(struct wm8350 *wm8350) { |
89b4012be mfd: Core support... |
166 |
int ret; |
52b461b86 mfd: Add regmap c... |
167 |
mutex_lock(®_lock_mutex); |
89b4012be mfd: Core support... |
168 |
ldbg(__func__); |
52b461b86 mfd: Add regmap c... |
169 170 |
ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY); |
89b4012be mfd: Core support... |
171 172 173 |
if (ret) dev_err(wm8350->dev, "unlock failed "); |
52b461b86 mfd: Add regmap c... |
174 175 176 177 |
wm8350->unlocked = true; mutex_unlock(®_lock_mutex); |
89b4012be mfd: Core support... |
178 179 180 |
return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_unlock); |
674885263 mfd: Add AUXADC s... |
181 182 183 |
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref) { u16 reg, result = 0; |
674885263 mfd: Add AUXADC s... |
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP) return -EINVAL; if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP && (scale != 0 || vref != 0)) return -EINVAL; mutex_lock(&wm8350->auxadc_mutex); /* Turn on the ADC */ reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA); if (scale || vref) { reg = scale << 13; reg |= vref << 12; wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg); } reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); reg |= 1 << channel | WM8350_AUXADC_POLL; wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg); |
5051d411e mfd: Clean up aft... |
206 207 208 |
/* If a late IRQ left the completion signalled then consume * the completion. */ try_wait_for_completion(&wm8350->auxadc_done); |
d19663ac6 mfd: Use completi... |
209 210 211 212 |
/* We ignore the result of the completion and just check for a * conversion result, allowing us to soldier on if the IRQ * infrastructure is not set up for the chip. */ wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5)); |
674885263 mfd: Add AUXADC s... |
213 |
|
d19663ac6 mfd: Use completi... |
214 215 |
reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); if (reg & WM8350_AUXADC_POLL) |
674885263 mfd: Add AUXADC s... |
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
dev_err(wm8350->dev, "adc chn %d read timeout ", channel); else result = wm8350_reg_read(wm8350, WM8350_AUX1_READBACK + channel); /* Turn off the ADC */ reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg & ~WM8350_AUXADC_ENA); mutex_unlock(&wm8350->auxadc_mutex); return result & WM8350_AUXADC_DATA1_MASK; } EXPORT_SYMBOL_GPL(wm8350_read_auxadc); |
d19663ac6 mfd: Use completi... |
232 233 234 235 236 237 238 239 |
static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data) { struct wm8350 *wm8350 = irq_data; complete(&wm8350->auxadc_done); return IRQ_HANDLED; } |
89b4012be mfd: Core support... |
240 |
/* |
9201d38b9 mfd: Add WM8350 s... |
241 242 243 244 245 246 247 248 249 250 |
* Register a client device. This is non-fatal since there is no need to * fail the entire device init due to a single platform device failing. */ static void wm8350_client_dev_register(struct wm8350 *wm8350, const char *name, struct platform_device **pdev) { int ret; *pdev = platform_device_alloc(name, -1); |
cb9b22450 mfd: Fix incorrec... |
251 |
if (*pdev == NULL) { |
9201d38b9 mfd: Add WM8350 s... |
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
dev_err(wm8350->dev, "Failed to allocate %s ", name); return; } (*pdev)->dev.parent = wm8350->dev; platform_set_drvdata(*pdev, wm8350); ret = platform_device_add(*pdev); if (ret != 0) { dev_err(wm8350->dev, "Failed to register %s: %d ", name, ret); platform_device_put(*pdev); *pdev = NULL; } } |
ebccec0fa mfd: Add WM8350 i... |
267 |
int wm8350_device_init(struct wm8350 *wm8350, int irq, |
bcdd4efc1 mfd: Add initiali... |
268 |
struct wm8350_platform_data *pdata) |
89b4012be mfd: Core support... |
269 |
{ |
85c93ea7d mfd: Improve diag... |
270 |
int ret; |
b7b142d9f mfd: Convert wm83... |
271 272 |
unsigned int id1, id2, mask_rev; unsigned int cust_id, mode, chip_rev; |
89b4012be mfd: Core support... |
273 |
|
18bf50a37 mfd: Store wm8350... |
274 |
dev_set_drvdata(wm8350->dev, wm8350); |
89b4012be mfd: Core support... |
275 |
/* get WM8350 revision and config mode */ |
b7b142d9f mfd: Convert wm83... |
276 |
ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1); |
85c93ea7d mfd: Improve diag... |
277 278 279 280 281 |
if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d ", ret); goto err; } |
b7b142d9f mfd: Convert wm83... |
282 |
ret = regmap_read(wm8350->regmap, WM8350_ID, &id2); |
85c93ea7d mfd: Improve diag... |
283 284 285 286 287 |
if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d ", ret); goto err; } |
b7b142d9f mfd: Convert wm83... |
288 |
ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev); |
85c93ea7d mfd: Improve diag... |
289 290 291 292 293 |
if (ret != 0) { dev_err(wm8350->dev, "Failed to read revision: %d ", ret); goto err; } |
89b4012be mfd: Core support... |
294 |
|
b797a5551 mfd: Refactor WM8... |
295 296 297 298 299 300 301 |
if (id1 != 0x6143) { dev_err(wm8350->dev, "Device with ID %x is not a WM8350 ", id1); ret = -ENODEV; goto err; } |
1753b40f5 mfd: wm8350-core:... |
302 |
mode = (id2 & WM8350_CONF_STS_MASK) >> 10; |
b797a5551 mfd: Refactor WM8... |
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
cust_id = id2 & WM8350_CUST_ID_MASK; chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; dev_info(wm8350->dev, "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d ", mode, cust_id, mask_rev, chip_rev); if (cust_id != 0) { dev_err(wm8350->dev, "Unsupported CUST_ID "); ret = -ENODEV; goto err; } switch (mask_rev) { case 0: |
645524a9c mfd: Support conf... |
319 320 |
wm8350->pmic.max_dcdc = WM8350_DCDC_6; wm8350->pmic.max_isink = WM8350_ISINK_B; |
b797a5551 mfd: Refactor WM8... |
321 |
switch (chip_rev) { |
89b4012be mfd: Core support... |
322 |
case WM8350_REV_E: |
b797a5551 mfd: Refactor WM8... |
323 324 |
dev_info(wm8350->dev, "WM8350 Rev E "); |
89b4012be mfd: Core support... |
325 326 |
break; case WM8350_REV_F: |
b797a5551 mfd: Refactor WM8... |
327 328 |
dev_info(wm8350->dev, "WM8350 Rev F "); |
89b4012be mfd: Core support... |
329 330 |
break; case WM8350_REV_G: |
b797a5551 mfd: Refactor WM8... |
331 332 |
dev_info(wm8350->dev, "WM8350 Rev G "); |
d756f4a44 mfd: Switch WM835... |
333 |
wm8350->power.rev_g_coeff = 1; |
89b4012be mfd: Core support... |
334 |
break; |
0c8a60167 mfd: Add WM8350 r... |
335 |
case WM8350_REV_H: |
b797a5551 mfd: Refactor WM8... |
336 337 |
dev_info(wm8350->dev, "WM8350 Rev H "); |
d756f4a44 mfd: Switch WM835... |
338 |
wm8350->power.rev_g_coeff = 1; |
0c8a60167 mfd: Add WM8350 r... |
339 |
break; |
89b4012be mfd: Core support... |
340 341 |
default: /* For safety we refuse to run on unknown hardware */ |
b797a5551 mfd: Refactor WM8... |
342 343 |
dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV "); |
89b4012be mfd: Core support... |
344 345 346 |
ret = -ENODEV; goto err; } |
b797a5551 mfd: Refactor WM8... |
347 |
break; |
ca23f8c1b mfd: Add WM8351 s... |
348 349 350 351 352 353 354 355 356 357 |
case 1: wm8350->pmic.max_dcdc = WM8350_DCDC_4; wm8350->pmic.max_isink = WM8350_ISINK_A; switch (chip_rev) { case 0: dev_info(wm8350->dev, "WM8351 Rev A "); wm8350->power.rev_g_coeff = 1; break; |
02d46e07e mfd: add support ... |
358 359 360 361 362 |
case 1: dev_info(wm8350->dev, "WM8351 Rev B "); wm8350->power.rev_g_coeff = 1; break; |
ca23f8c1b mfd: Add WM8351 s... |
363 364 365 366 367 368 369 |
default: dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV "); ret = -ENODEV; goto err; } break; |
969206306 mfd: Add WM8352 s... |
370 |
case 2: |
645524a9c mfd: Support conf... |
371 372 |
wm8350->pmic.max_dcdc = WM8350_DCDC_6; wm8350->pmic.max_isink = WM8350_ISINK_B; |
969206306 mfd: Add WM8352 s... |
373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
switch (chip_rev) { case 0: dev_info(wm8350->dev, "WM8352 Rev A "); wm8350->power.rev_g_coeff = 1; break; default: dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV "); ret = -ENODEV; goto err; } break; |
b797a5551 mfd: Refactor WM8... |
387 388 389 |
default: dev_err(wm8350->dev, "Unknown MASK_REV "); |
89b4012be mfd: Core support... |
390 391 392 |
ret = -ENODEV; goto err; } |
674885263 mfd: Add AUXADC s... |
393 |
mutex_init(&wm8350->auxadc_mutex); |
d19663ac6 mfd: Use completi... |
394 |
init_completion(&wm8350->auxadc_done); |
320645035 mfd: Support acti... |
395 |
|
e0a3389ab mfd: Split wm8350... |
396 397 |
ret = wm8350_irq_init(wm8350, irq, pdata); if (ret < 0) |
19d57ed5a mfd: Remove custo... |
398 |
goto err; |
ebccec0fa mfd: Add WM8350 i... |
399 |
|
d19663ac6 mfd: Use completi... |
400 401 402 |
if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, |
b3c8c82b4 mfd: wm8350-core:... |
403 404 |
NULL, wm8350_auxadc_irq, IRQF_ONESHOT, |
d19663ac6 mfd: Use completi... |
405 406 407 408 409 410 |
"auxadc", wm8350); if (ret < 0) dev_warn(wm8350->dev, "Failed to request AUXADC IRQ: %d ", ret); } |
62571c29a mfd: Initialise W... |
411 412 413 414 415 416 |
if (pdata && pdata->init) { ret = pdata->init(wm8350); if (ret != 0) { dev_err(wm8350->dev, "Platform init() failed: %d ", ret); |
e0a3389ab mfd: Split wm8350... |
417 |
goto err_irq; |
62571c29a mfd: Initialise W... |
418 419 |
} } |
ebccec0fa mfd: Add WM8350 i... |
420 |
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); |
add41cb46 mfd: Add placehol... |
421 422 423 424 |
wm8350_client_dev_register(wm8350, "wm8350-codec", &(wm8350->codec.pdev)); wm8350_client_dev_register(wm8350, "wm8350-gpio", &(wm8350->gpio.pdev)); |
fb6c023a2 hwmon: Add WM835x... |
425 426 |
wm8350_client_dev_register(wm8350, "wm8350-hwmon", &(wm8350->hwmon.pdev)); |
add41cb46 mfd: Add placehol... |
427 428 429 430 |
wm8350_client_dev_register(wm8350, "wm8350-power", &(wm8350->power.pdev)); wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev)); wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev)); |
89b4012be mfd: Core support... |
431 |
return 0; |
e0a3389ab mfd: Split wm8350... |
432 433 |
err_irq: wm8350_irq_exit(wm8350); |
8c46cf30f mfd: Fix wrong wm... |
434 |
err: |
89b4012be mfd: Core support... |
435 436 437 438 439 440 |
return ret; } EXPORT_SYMBOL_GPL(wm8350_device_init); void wm8350_device_exit(struct wm8350 *wm8350) { |
da09155ac regulator: Add WM... |
441 |
int i; |
0081e8020 leds: Add WM8350 ... |
442 443 |
for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++) platform_device_unregister(wm8350->pmic.led[i].pdev); |
da09155ac regulator: Add WM... |
444 |
for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) |
add41cb46 mfd: Add placehol... |
445 446 447 448 449 |
platform_device_unregister(wm8350->pmic.pdev[i]); platform_device_unregister(wm8350->wdt.pdev); platform_device_unregister(wm8350->rtc.pdev); platform_device_unregister(wm8350->power.pdev); |
fb6c023a2 hwmon: Add WM835x... |
450 |
platform_device_unregister(wm8350->hwmon.pdev); |
add41cb46 mfd: Add placehol... |
451 452 |
platform_device_unregister(wm8350->gpio.pdev); platform_device_unregister(wm8350->codec.pdev); |
da09155ac regulator: Add WM... |
453 |
|
d19663ac6 mfd: Use completi... |
454 455 |
if (wm8350->irq_base) free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); |
e0a3389ab mfd: Split wm8350... |
456 |
wm8350_irq_exit(wm8350); |
89b4012be mfd: Core support... |
457 458 |
} EXPORT_SYMBOL_GPL(wm8350_device_exit); |
ebccec0fa mfd: Add WM8350 i... |
459 |
MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); |
89b4012be mfd: Core support... |
460 |
MODULE_LICENSE("GPL"); |