189b4012bSMark Brown /* 289b4012bSMark Brown * wm8350-core.c -- Device access for Wolfson WM8350 389b4012bSMark Brown * 489b4012bSMark Brown * Copyright 2007, 2008 Wolfson Microelectronics PLC. 589b4012bSMark Brown * 689b4012bSMark Brown * Author: Liam Girdwood, Mark Brown 789b4012bSMark Brown * 889b4012bSMark Brown * This program is free software; you can redistribute it and/or modify it 989b4012bSMark Brown * under the terms of the GNU General Public License as published by the 1089b4012bSMark Brown * Free Software Foundation; either version 2 of the License, or (at your 1189b4012bSMark Brown * option) any later version. 1289b4012bSMark Brown * 1389b4012bSMark Brown */ 1489b4012bSMark Brown 1589b4012bSMark Brown #include <linux/kernel.h> 1689b4012bSMark Brown #include <linux/module.h> 1789b4012bSMark Brown #include <linux/init.h> 185a0e3ad6STejun Heo #include <linux/slab.h> 19ebccec0fSMark Brown #include <linux/bug.h> 2089b4012bSMark Brown #include <linux/device.h> 2189b4012bSMark Brown #include <linux/delay.h> 2289b4012bSMark Brown #include <linux/interrupt.h> 23b7b142d9SMark Brown #include <linux/regmap.h> 24ebccec0fSMark Brown #include <linux/workqueue.h> 2589b4012bSMark Brown 2689b4012bSMark Brown #include <linux/mfd/wm8350/core.h> 2789b4012bSMark Brown #include <linux/mfd/wm8350/audio.h> 28ebccec0fSMark Brown #include <linux/mfd/wm8350/comparator.h> 2989b4012bSMark Brown #include <linux/mfd/wm8350/gpio.h> 3089b4012bSMark Brown #include <linux/mfd/wm8350/pmic.h> 31ebccec0fSMark Brown #include <linux/mfd/wm8350/rtc.h> 3289b4012bSMark Brown #include <linux/mfd/wm8350/supply.h> 33ebccec0fSMark Brown #include <linux/mfd/wm8350/wdt.h> 3489b4012bSMark Brown 3589b4012bSMark Brown #define WM8350_CLOCK_CONTROL_1 0x28 3689b4012bSMark Brown #define WM8350_AIF_TEST 0x74 3789b4012bSMark Brown 3889b4012bSMark Brown /* debug */ 3989b4012bSMark Brown #define WM8350_BUS_DEBUG 0 4089b4012bSMark Brown #if WM8350_BUS_DEBUG 4189b4012bSMark Brown #define dump(regs, src) do { \ 4289b4012bSMark Brown int i_; \ 4389b4012bSMark Brown u16 *src_ = src; \ 4489b4012bSMark Brown printk(KERN_DEBUG); \ 4589b4012bSMark Brown for (i_ = 0; i_ < regs; i_++) \ 4689b4012bSMark Brown printk(" 0x%4.4x", *src_++); \ 4789b4012bSMark Brown printk("\n"); \ 4889b4012bSMark Brown } while (0); 4989b4012bSMark Brown #else 5089b4012bSMark Brown #define dump(bytes, src) 5189b4012bSMark Brown #endif 5289b4012bSMark Brown 5389b4012bSMark Brown #define WM8350_LOCK_DEBUG 0 5489b4012bSMark Brown #if WM8350_LOCK_DEBUG 5589b4012bSMark Brown #define ldbg(format, arg...) printk(format, ## arg) 5689b4012bSMark Brown #else 5789b4012bSMark Brown #define ldbg(format, arg...) 5889b4012bSMark Brown #endif 5989b4012bSMark Brown 6089b4012bSMark Brown /* 6189b4012bSMark Brown * WM8350 Device IO 6289b4012bSMark Brown */ 6389b4012bSMark Brown static DEFINE_MUTEX(reg_lock_mutex); 6489b4012bSMark Brown 6589b4012bSMark Brown /* 6689b4012bSMark Brown * Safe read, modify, write methods 6789b4012bSMark Brown */ 6889b4012bSMark Brown int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) 6989b4012bSMark Brown { 7019d57ed5SMark Brown return regmap_update_bits(wm8350->regmap, reg, mask, 0); 7189b4012bSMark Brown } 7289b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_clear_bits); 7389b4012bSMark Brown 7489b4012bSMark Brown int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) 7589b4012bSMark Brown { 7619d57ed5SMark Brown return regmap_update_bits(wm8350->regmap, reg, mask, mask); 7789b4012bSMark Brown } 7889b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_set_bits); 7989b4012bSMark Brown 8089b4012bSMark Brown u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) 8189b4012bSMark Brown { 8219d57ed5SMark Brown unsigned int data; 8389b4012bSMark Brown int err; 8489b4012bSMark Brown 8519d57ed5SMark Brown err = regmap_read(wm8350->regmap, reg, &data); 8689b4012bSMark Brown if (err) 8789b4012bSMark Brown dev_err(wm8350->dev, "read from reg R%d failed\n", reg); 8889b4012bSMark Brown 8989b4012bSMark Brown return data; 9089b4012bSMark Brown } 9189b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_reg_read); 9289b4012bSMark Brown 9389b4012bSMark Brown int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) 9489b4012bSMark Brown { 9589b4012bSMark Brown int ret; 9689b4012bSMark Brown 9719d57ed5SMark Brown ret = regmap_write(wm8350->regmap, reg, val); 9819d57ed5SMark Brown 9989b4012bSMark Brown if (ret) 10089b4012bSMark Brown dev_err(wm8350->dev, "write to reg R%d failed\n", reg); 10189b4012bSMark Brown return ret; 10289b4012bSMark Brown } 10389b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_reg_write); 10489b4012bSMark Brown 10589b4012bSMark Brown int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, 10689b4012bSMark Brown u16 *dest) 10789b4012bSMark Brown { 10889b4012bSMark Brown int err = 0; 10989b4012bSMark Brown 11019d57ed5SMark Brown err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs); 11189b4012bSMark Brown if (err) 11289b4012bSMark Brown dev_err(wm8350->dev, "block read starting from R%d failed\n", 11389b4012bSMark Brown start_reg); 11419d57ed5SMark Brown 11589b4012bSMark Brown return err; 11689b4012bSMark Brown } 11789b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_block_read); 11889b4012bSMark Brown 11989b4012bSMark Brown int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, 12089b4012bSMark Brown u16 *src) 12189b4012bSMark Brown { 12289b4012bSMark Brown int ret = 0; 12389b4012bSMark Brown 12419d57ed5SMark Brown ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs); 12589b4012bSMark Brown if (ret) 12689b4012bSMark Brown dev_err(wm8350->dev, "block write starting at R%d failed\n", 12789b4012bSMark Brown start_reg); 12819d57ed5SMark Brown 12989b4012bSMark Brown return ret; 13089b4012bSMark Brown } 13189b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_block_write); 13289b4012bSMark Brown 133858e6744SMark Brown /** 134858e6744SMark Brown * wm8350_reg_lock() 135858e6744SMark Brown * 136858e6744SMark Brown * The WM8350 has a hardware lock which can be used to prevent writes to 137858e6744SMark Brown * some registers (generally those which can cause particularly serious 138858e6744SMark Brown * problems if misused). This function enables that lock. 139858e6744SMark Brown */ 14089b4012bSMark Brown int wm8350_reg_lock(struct wm8350 *wm8350) 14189b4012bSMark Brown { 14289b4012bSMark Brown int ret; 14389b4012bSMark Brown 14452b461b8SMark Brown mutex_lock(®_lock_mutex); 14552b461b8SMark Brown 14689b4012bSMark Brown ldbg(__func__); 14752b461b8SMark Brown 14852b461b8SMark Brown ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY); 14989b4012bSMark Brown if (ret) 15089b4012bSMark Brown dev_err(wm8350->dev, "lock failed\n"); 15152b461b8SMark Brown 15252b461b8SMark Brown wm8350->unlocked = false; 15352b461b8SMark Brown 15452b461b8SMark Brown mutex_unlock(®_lock_mutex); 15552b461b8SMark Brown 15689b4012bSMark Brown return ret; 15789b4012bSMark Brown } 15889b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_reg_lock); 15989b4012bSMark Brown 160858e6744SMark Brown /** 161858e6744SMark Brown * wm8350_reg_unlock() 162858e6744SMark Brown * 163858e6744SMark Brown * The WM8350 has a hardware lock which can be used to prevent writes to 164858e6744SMark Brown * some registers (generally those which can cause particularly serious 165858e6744SMark Brown * problems if misused). This function disables that lock so updates 166858e6744SMark Brown * can be performed. For maximum safety this should be done only when 167858e6744SMark Brown * required. 168858e6744SMark Brown */ 16989b4012bSMark Brown int wm8350_reg_unlock(struct wm8350 *wm8350) 17089b4012bSMark Brown { 17189b4012bSMark Brown int ret; 17289b4012bSMark Brown 17352b461b8SMark Brown mutex_lock(®_lock_mutex); 17452b461b8SMark Brown 17589b4012bSMark Brown ldbg(__func__); 17652b461b8SMark Brown 17752b461b8SMark Brown ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY); 17889b4012bSMark Brown if (ret) 17989b4012bSMark Brown dev_err(wm8350->dev, "unlock failed\n"); 18052b461b8SMark Brown 18152b461b8SMark Brown wm8350->unlocked = true; 18252b461b8SMark Brown 18352b461b8SMark Brown mutex_unlock(®_lock_mutex); 18452b461b8SMark Brown 18589b4012bSMark Brown return ret; 18689b4012bSMark Brown } 18789b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_reg_unlock); 18889b4012bSMark Brown 18967488526SMark Brown int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref) 19067488526SMark Brown { 19167488526SMark Brown u16 reg, result = 0; 19267488526SMark Brown 19367488526SMark Brown if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP) 19467488526SMark Brown return -EINVAL; 19567488526SMark Brown if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP 19667488526SMark Brown && (scale != 0 || vref != 0)) 19767488526SMark Brown return -EINVAL; 19867488526SMark Brown 19967488526SMark Brown mutex_lock(&wm8350->auxadc_mutex); 20067488526SMark Brown 20167488526SMark Brown /* Turn on the ADC */ 20267488526SMark Brown reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); 20367488526SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA); 20467488526SMark Brown 20567488526SMark Brown if (scale || vref) { 20667488526SMark Brown reg = scale << 13; 20767488526SMark Brown reg |= vref << 12; 20867488526SMark Brown wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg); 20967488526SMark Brown } 21067488526SMark Brown 21167488526SMark Brown reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); 21267488526SMark Brown reg |= 1 << channel | WM8350_AUXADC_POLL; 21367488526SMark Brown wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg); 21467488526SMark Brown 2155051d411SMark Brown /* If a late IRQ left the completion signalled then consume 2165051d411SMark Brown * the completion. */ 2175051d411SMark Brown try_wait_for_completion(&wm8350->auxadc_done); 2185051d411SMark Brown 219d19663acSMark Brown /* We ignore the result of the completion and just check for a 220d19663acSMark Brown * conversion result, allowing us to soldier on if the IRQ 221d19663acSMark Brown * infrastructure is not set up for the chip. */ 222d19663acSMark Brown wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5)); 22367488526SMark Brown 224d19663acSMark Brown reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); 225d19663acSMark Brown if (reg & WM8350_AUXADC_POLL) 22667488526SMark Brown dev_err(wm8350->dev, "adc chn %d read timeout\n", channel); 22767488526SMark Brown else 22867488526SMark Brown result = wm8350_reg_read(wm8350, 22967488526SMark Brown WM8350_AUX1_READBACK + channel); 23067488526SMark Brown 23167488526SMark Brown /* Turn off the ADC */ 23267488526SMark Brown reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); 23367488526SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, 23467488526SMark Brown reg & ~WM8350_AUXADC_ENA); 23567488526SMark Brown 23667488526SMark Brown mutex_unlock(&wm8350->auxadc_mutex); 23767488526SMark Brown 23867488526SMark Brown return result & WM8350_AUXADC_DATA1_MASK; 23967488526SMark Brown } 24067488526SMark Brown EXPORT_SYMBOL_GPL(wm8350_read_auxadc); 24167488526SMark Brown 242d19663acSMark Brown static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data) 243d19663acSMark Brown { 244d19663acSMark Brown struct wm8350 *wm8350 = irq_data; 245d19663acSMark Brown 246d19663acSMark Brown complete(&wm8350->auxadc_done); 247d19663acSMark Brown 248d19663acSMark Brown return IRQ_HANDLED; 249d19663acSMark Brown } 250d19663acSMark Brown 25189b4012bSMark Brown /* 2529201d38bSMark Brown * Register a client device. This is non-fatal since there is no need to 2539201d38bSMark Brown * fail the entire device init due to a single platform device failing. 2549201d38bSMark Brown */ 2559201d38bSMark Brown static void wm8350_client_dev_register(struct wm8350 *wm8350, 2569201d38bSMark Brown const char *name, 2579201d38bSMark Brown struct platform_device **pdev) 2589201d38bSMark Brown { 2599201d38bSMark Brown int ret; 2609201d38bSMark Brown 2619201d38bSMark Brown *pdev = platform_device_alloc(name, -1); 262cb9b2245SDan Carpenter if (*pdev == NULL) { 2639201d38bSMark Brown dev_err(wm8350->dev, "Failed to allocate %s\n", name); 2649201d38bSMark Brown return; 2659201d38bSMark Brown } 2669201d38bSMark Brown 2679201d38bSMark Brown (*pdev)->dev.parent = wm8350->dev; 2689201d38bSMark Brown platform_set_drvdata(*pdev, wm8350); 2699201d38bSMark Brown ret = platform_device_add(*pdev); 2709201d38bSMark Brown if (ret != 0) { 2719201d38bSMark Brown dev_err(wm8350->dev, "Failed to register %s: %d\n", name, ret); 2729201d38bSMark Brown platform_device_put(*pdev); 2739201d38bSMark Brown *pdev = NULL; 2749201d38bSMark Brown } 2759201d38bSMark Brown } 2769201d38bSMark Brown 277ebccec0fSMark Brown int wm8350_device_init(struct wm8350 *wm8350, int irq, 278bcdd4efcSMark Brown struct wm8350_platform_data *pdata) 27989b4012bSMark Brown { 28085c93ea7SMark Brown int ret; 281b7b142d9SMark Brown unsigned int id1, id2, mask_rev; 282b7b142d9SMark Brown unsigned int cust_id, mode, chip_rev; 28389b4012bSMark Brown 28418bf50a3SMark Brown dev_set_drvdata(wm8350->dev, wm8350); 28518bf50a3SMark Brown 28689b4012bSMark Brown /* get WM8350 revision and config mode */ 287b7b142d9SMark Brown ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1); 28885c93ea7SMark Brown if (ret != 0) { 28985c93ea7SMark Brown dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); 29085c93ea7SMark Brown goto err; 29185c93ea7SMark Brown } 29285c93ea7SMark Brown 293b7b142d9SMark Brown ret = regmap_read(wm8350->regmap, WM8350_ID, &id2); 29485c93ea7SMark Brown if (ret != 0) { 29585c93ea7SMark Brown dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); 29685c93ea7SMark Brown goto err; 29785c93ea7SMark Brown } 29885c93ea7SMark Brown 299b7b142d9SMark Brown ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev); 30085c93ea7SMark Brown if (ret != 0) { 30185c93ea7SMark Brown dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); 30285c93ea7SMark Brown goto err; 30385c93ea7SMark Brown } 30489b4012bSMark Brown 305b797a555SMark Brown if (id1 != 0x6143) { 306b797a555SMark Brown dev_err(wm8350->dev, 307b797a555SMark Brown "Device with ID %x is not a WM8350\n", id1); 30889b4012bSMark Brown ret = -ENODEV; 30989b4012bSMark Brown goto err; 31089b4012bSMark Brown } 31189b4012bSMark Brown 31289b4012bSMark Brown mode = id2 & WM8350_CONF_STS_MASK >> 10; 313b797a555SMark Brown cust_id = id2 & WM8350_CUST_ID_MASK; 314b797a555SMark Brown chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; 315b797a555SMark Brown dev_info(wm8350->dev, 316b797a555SMark Brown "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n", 317b797a555SMark Brown mode, cust_id, mask_rev, chip_rev); 318b797a555SMark Brown 319b797a555SMark Brown if (cust_id != 0) { 320b797a555SMark Brown dev_err(wm8350->dev, "Unsupported CUST_ID\n"); 321b797a555SMark Brown ret = -ENODEV; 322b797a555SMark Brown goto err; 323b797a555SMark Brown } 324b797a555SMark Brown 325b797a555SMark Brown switch (mask_rev) { 326b797a555SMark Brown case 0: 327645524a9SMark Brown wm8350->pmic.max_dcdc = WM8350_DCDC_6; 328645524a9SMark Brown wm8350->pmic.max_isink = WM8350_ISINK_B; 329645524a9SMark Brown 330b797a555SMark Brown switch (chip_rev) { 331b797a555SMark Brown case WM8350_REV_E: 332b797a555SMark Brown dev_info(wm8350->dev, "WM8350 Rev E\n"); 333b797a555SMark Brown break; 334b797a555SMark Brown case WM8350_REV_F: 335b797a555SMark Brown dev_info(wm8350->dev, "WM8350 Rev F\n"); 336b797a555SMark Brown break; 337b797a555SMark Brown case WM8350_REV_G: 338b797a555SMark Brown dev_info(wm8350->dev, "WM8350 Rev G\n"); 339b797a555SMark Brown wm8350->power.rev_g_coeff = 1; 340b797a555SMark Brown break; 341b797a555SMark Brown case WM8350_REV_H: 342b797a555SMark Brown dev_info(wm8350->dev, "WM8350 Rev H\n"); 343b797a555SMark Brown wm8350->power.rev_g_coeff = 1; 344b797a555SMark Brown break; 345b797a555SMark Brown default: 346b797a555SMark Brown /* For safety we refuse to run on unknown hardware */ 347b797a555SMark Brown dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n"); 348b797a555SMark Brown ret = -ENODEV; 349b797a555SMark Brown goto err; 350b797a555SMark Brown } 351b797a555SMark Brown break; 352b797a555SMark Brown 353ca23f8c1SMark Brown case 1: 354ca23f8c1SMark Brown wm8350->pmic.max_dcdc = WM8350_DCDC_4; 355ca23f8c1SMark Brown wm8350->pmic.max_isink = WM8350_ISINK_A; 356ca23f8c1SMark Brown 357ca23f8c1SMark Brown switch (chip_rev) { 358ca23f8c1SMark Brown case 0: 359ca23f8c1SMark Brown dev_info(wm8350->dev, "WM8351 Rev A\n"); 360ca23f8c1SMark Brown wm8350->power.rev_g_coeff = 1; 361ca23f8c1SMark Brown break; 362ca23f8c1SMark Brown 36302d46e07SMark Brown case 1: 36402d46e07SMark Brown dev_info(wm8350->dev, "WM8351 Rev B\n"); 36502d46e07SMark Brown wm8350->power.rev_g_coeff = 1; 36602d46e07SMark Brown break; 36702d46e07SMark Brown 368ca23f8c1SMark Brown default: 369ca23f8c1SMark Brown dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n"); 370ca23f8c1SMark Brown ret = -ENODEV; 371ca23f8c1SMark Brown goto err; 372ca23f8c1SMark Brown } 373ca23f8c1SMark Brown break; 374ca23f8c1SMark Brown 37596920630SMark Brown case 2: 376645524a9SMark Brown wm8350->pmic.max_dcdc = WM8350_DCDC_6; 377645524a9SMark Brown wm8350->pmic.max_isink = WM8350_ISINK_B; 378645524a9SMark Brown 37996920630SMark Brown switch (chip_rev) { 38096920630SMark Brown case 0: 38196920630SMark Brown dev_info(wm8350->dev, "WM8352 Rev A\n"); 38296920630SMark Brown wm8350->power.rev_g_coeff = 1; 38396920630SMark Brown break; 38496920630SMark Brown 38596920630SMark Brown default: 38696920630SMark Brown dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n"); 38796920630SMark Brown ret = -ENODEV; 38896920630SMark Brown goto err; 38996920630SMark Brown } 39096920630SMark Brown break; 39196920630SMark Brown 392b797a555SMark Brown default: 393b797a555SMark Brown dev_err(wm8350->dev, "Unknown MASK_REV\n"); 394b797a555SMark Brown ret = -ENODEV; 395b797a555SMark Brown goto err; 396b797a555SMark Brown } 39789b4012bSMark Brown 39867488526SMark Brown mutex_init(&wm8350->auxadc_mutex); 399d19663acSMark Brown init_completion(&wm8350->auxadc_done); 40032064503SMark Brown 401e0a3389aSMark Brown ret = wm8350_irq_init(wm8350, irq, pdata); 402e0a3389aSMark Brown if (ret < 0) 40319d57ed5SMark Brown goto err; 404ebccec0fSMark Brown 405d19663acSMark Brown if (wm8350->irq_base) { 406d19663acSMark Brown ret = request_threaded_irq(wm8350->irq_base + 407d19663acSMark Brown WM8350_IRQ_AUXADC_DATARDY, 408d19663acSMark Brown NULL, wm8350_auxadc_irq, 0, 409d19663acSMark Brown "auxadc", wm8350); 410d19663acSMark Brown if (ret < 0) 411d19663acSMark Brown dev_warn(wm8350->dev, 412d19663acSMark Brown "Failed to request AUXADC IRQ: %d\n", ret); 413d19663acSMark Brown } 414d19663acSMark Brown 41562571c29SMark Brown if (pdata && pdata->init) { 41662571c29SMark Brown ret = pdata->init(wm8350); 41762571c29SMark Brown if (ret != 0) { 41862571c29SMark Brown dev_err(wm8350->dev, "Platform init() failed: %d\n", 41962571c29SMark Brown ret); 420e0a3389aSMark Brown goto err_irq; 42162571c29SMark Brown } 42262571c29SMark Brown } 42362571c29SMark Brown 424ebccec0fSMark Brown wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); 425ebccec0fSMark Brown 426add41cb4SMark Brown wm8350_client_dev_register(wm8350, "wm8350-codec", 427add41cb4SMark Brown &(wm8350->codec.pdev)); 428add41cb4SMark Brown wm8350_client_dev_register(wm8350, "wm8350-gpio", 429add41cb4SMark Brown &(wm8350->gpio.pdev)); 430fb6c023aSMark Brown wm8350_client_dev_register(wm8350, "wm8350-hwmon", 431fb6c023aSMark Brown &(wm8350->hwmon.pdev)); 432add41cb4SMark Brown wm8350_client_dev_register(wm8350, "wm8350-power", 433add41cb4SMark Brown &(wm8350->power.pdev)); 434add41cb4SMark Brown wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev)); 435add41cb4SMark Brown wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev)); 436add41cb4SMark Brown 43789b4012bSMark Brown return 0; 43889b4012bSMark Brown 439e0a3389aSMark Brown err_irq: 440e0a3389aSMark Brown wm8350_irq_exit(wm8350); 4418c46cf30SAxel Lin err: 44289b4012bSMark Brown return ret; 44389b4012bSMark Brown } 44489b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_device_init); 44589b4012bSMark Brown 44689b4012bSMark Brown void wm8350_device_exit(struct wm8350 *wm8350) 44789b4012bSMark Brown { 448da09155aSMark Brown int i; 449da09155aSMark Brown 4500081e802SMark Brown for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++) 4510081e802SMark Brown platform_device_unregister(wm8350->pmic.led[i].pdev); 4520081e802SMark Brown 453da09155aSMark Brown for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) 454da09155aSMark Brown platform_device_unregister(wm8350->pmic.pdev[i]); 455da09155aSMark Brown 456add41cb4SMark Brown platform_device_unregister(wm8350->wdt.pdev); 457add41cb4SMark Brown platform_device_unregister(wm8350->rtc.pdev); 458add41cb4SMark Brown platform_device_unregister(wm8350->power.pdev); 459fb6c023aSMark Brown platform_device_unregister(wm8350->hwmon.pdev); 460add41cb4SMark Brown platform_device_unregister(wm8350->gpio.pdev); 461add41cb4SMark Brown platform_device_unregister(wm8350->codec.pdev); 462add41cb4SMark Brown 463d19663acSMark Brown if (wm8350->irq_base) 464d19663acSMark Brown free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); 465d19663acSMark Brown 466e0a3389aSMark Brown wm8350_irq_exit(wm8350); 46789b4012bSMark Brown } 46889b4012bSMark Brown EXPORT_SYMBOL_GPL(wm8350_device_exit); 46989b4012bSMark Brown 470ebccec0fSMark Brown MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); 47189b4012bSMark Brown MODULE_LICENSE("GPL"); 472