linux/drivers/mfd/arizona-core.c
<<
>>
Prefs
   1/*
   2 * Arizona core driver
   3 *
   4 * Copyright 2012 Wolfson Microelectronics plc
   5 *
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/err.h>
  15#include <linux/gpio.h>
  16#include <linux/interrupt.h>
  17#include <linux/mfd/core.h>
  18#include <linux/module.h>
  19#include <linux/pm_runtime.h>
  20#include <linux/regmap.h>
  21#include <linux/regulator/consumer.h>
  22#include <linux/slab.h>
  23
  24#include <linux/mfd/arizona/core.h>
  25#include <linux/mfd/arizona/registers.h>
  26
  27#include "arizona.h"
  28
  29static const char *wm5102_core_supplies[] = {
  30        "AVDD",
  31        "DBVDD1",
  32};
  33
  34int arizona_clk32k_enable(struct arizona *arizona)
  35{
  36        int ret = 0;
  37
  38        mutex_lock(&arizona->clk_lock);
  39
  40        arizona->clk32k_ref++;
  41
  42        if (arizona->clk32k_ref == 1)
  43                ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
  44                                         ARIZONA_CLK_32K_ENA,
  45                                         ARIZONA_CLK_32K_ENA);
  46
  47        if (ret != 0)
  48                arizona->clk32k_ref--;
  49
  50        mutex_unlock(&arizona->clk_lock);
  51
  52        return ret;
  53}
  54EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
  55
  56int arizona_clk32k_disable(struct arizona *arizona)
  57{
  58        int ret = 0;
  59
  60        mutex_lock(&arizona->clk_lock);
  61
  62        BUG_ON(arizona->clk32k_ref <= 0);
  63
  64        arizona->clk32k_ref--;
  65
  66        if (arizona->clk32k_ref == 0)
  67                regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
  68                                   ARIZONA_CLK_32K_ENA, 0);
  69
  70        mutex_unlock(&arizona->clk_lock);
  71
  72        return ret;
  73}
  74EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
  75
  76static irqreturn_t arizona_clkgen_err(int irq, void *data)
  77{
  78        struct arizona *arizona = data;
  79
  80        dev_err(arizona->dev, "CLKGEN error\n");
  81
  82        return IRQ_HANDLED;
  83}
  84
  85static irqreturn_t arizona_underclocked(int irq, void *data)
  86{
  87        struct arizona *arizona = data;
  88        unsigned int val;
  89        int ret;
  90
  91        ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
  92                          &val);
  93        if (ret != 0) {
  94                dev_err(arizona->dev, "Failed to read underclock status: %d\n",
  95                        ret);
  96                return IRQ_NONE;
  97        }
  98
  99        if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
 100                dev_err(arizona->dev, "AIF3 underclocked\n");
 101        if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
 102                dev_err(arizona->dev, "AIF2 underclocked\n");
 103        if (val & ARIZONA_AIF1_UNDERCLOCKED_STS)
 104                dev_err(arizona->dev, "AIF1 underclocked\n");
 105        if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
 106                dev_err(arizona->dev, "ISRC2 underclocked\n");
 107        if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
 108                dev_err(arizona->dev, "ISRC1 underclocked\n");
 109        if (val & ARIZONA_FX_UNDERCLOCKED_STS)
 110                dev_err(arizona->dev, "FX underclocked\n");
 111        if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
 112                dev_err(arizona->dev, "ASRC underclocked\n");
 113        if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
 114                dev_err(arizona->dev, "DAC underclocked\n");
 115        if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
 116                dev_err(arizona->dev, "ADC underclocked\n");
 117        if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
 118                dev_err(arizona->dev, "Mixer underclocked\n");
 119
 120        return IRQ_HANDLED;
 121}
 122
 123static irqreturn_t arizona_overclocked(int irq, void *data)
 124{
 125        struct arizona *arizona = data;
 126        unsigned int val[2];
 127        int ret;
 128        
 129        ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
 130                               &val[0], 2);
 131        if (ret != 0) {
 132                dev_err(arizona->dev, "Failed to read overclock status: %d\n",
 133                        ret);
 134                return IRQ_NONE;
 135        }
 136
 137        if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
 138                dev_err(arizona->dev, "PWM overclocked\n");
 139        if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
 140                dev_err(arizona->dev, "FX core overclocked\n");
 141        if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
 142                dev_err(arizona->dev, "DAC SYS overclocked\n");
 143        if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
 144                dev_err(arizona->dev, "DAC WARP overclocked\n");
 145        if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
 146                dev_err(arizona->dev, "ADC overclocked\n");
 147        if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
 148                dev_err(arizona->dev, "Mixer overclocked\n");
 149        if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
 150                dev_err(arizona->dev, "AIF3 overclocked\n");
 151        if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
 152                dev_err(arizona->dev, "AIF2 overclocked\n");
 153        if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
 154                dev_err(arizona->dev, "AIF1 overclocked\n");
 155        if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
 156                dev_err(arizona->dev, "Pad control overclocked\n");
 157
 158        if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
 159                dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
 160        if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
 161                dev_err(arizona->dev, "Slimbus async overclocked\n");
 162        if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
 163                dev_err(arizona->dev, "Slimbus sync overclocked\n");
 164        if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
 165                dev_err(arizona->dev, "ASRC async system overclocked\n");
 166        if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
 167                dev_err(arizona->dev, "ASRC async WARP overclocked\n");
 168        if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
 169                dev_err(arizona->dev, "ASRC sync system overclocked\n");
 170        if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
 171                dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
 172        if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
 173                dev_err(arizona->dev, "DSP1 overclocked\n");
 174        if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
 175                dev_err(arizona->dev, "ISRC2 overclocked\n");
 176        if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
 177                dev_err(arizona->dev, "ISRC1 overclocked\n");
 178
 179        return IRQ_HANDLED;
 180}
 181
 182static int arizona_wait_for_boot(struct arizona *arizona)
 183{
 184        unsigned int reg;
 185        int ret, i;
 186
 187        /*
 188         * We can't use an interrupt as we need to runtime resume to do so,
 189         * we won't race with the interrupt handler as it'll be blocked on
 190         * runtime resume.
 191         */
 192        for (i = 0; i < 5; i++) {
 193                msleep(1);
 194
 195                ret = regmap_read(arizona->regmap,
 196                                  ARIZONA_INTERRUPT_RAW_STATUS_5, &reg);
 197                if (ret != 0) {
 198                        dev_err(arizona->dev, "Failed to read boot state: %d\n",
 199                                ret);
 200                        continue;
 201                }
 202
 203                if (reg & ARIZONA_BOOT_DONE_STS)
 204                        break;
 205        }
 206
 207        if (reg & ARIZONA_BOOT_DONE_STS) {
 208                regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
 209                             ARIZONA_BOOT_DONE_STS);
 210        } else {
 211                dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
 212                return -ETIMEDOUT;
 213        }
 214
 215        pm_runtime_mark_last_busy(arizona->dev);
 216
 217        return 0;
 218}
 219
 220#ifdef CONFIG_PM_RUNTIME
 221static int arizona_runtime_resume(struct device *dev)
 222{
 223        struct arizona *arizona = dev_get_drvdata(dev);
 224        int ret;
 225
 226        dev_dbg(arizona->dev, "Leaving AoD mode\n");
 227
 228        ret = regulator_enable(arizona->dcvdd);
 229        if (ret != 0) {
 230                dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
 231                return ret;
 232        }
 233
 234        regcache_cache_only(arizona->regmap, false);
 235
 236        ret = arizona_wait_for_boot(arizona);
 237        if (ret != 0) {
 238                regulator_disable(arizona->dcvdd);
 239                return ret;
 240        }
 241
 242        ret = regcache_sync(arizona->regmap);
 243        if (ret != 0) {
 244                dev_err(arizona->dev, "Failed to restore register cache\n");
 245                regulator_disable(arizona->dcvdd);
 246                return ret;
 247        }
 248
 249        return 0;
 250}
 251
 252static int arizona_runtime_suspend(struct device *dev)
 253{
 254        struct arizona *arizona = dev_get_drvdata(dev);
 255
 256        dev_dbg(arizona->dev, "Entering AoD mode\n");
 257
 258        regulator_disable(arizona->dcvdd);
 259        regcache_cache_only(arizona->regmap, true);
 260        regcache_mark_dirty(arizona->regmap);
 261
 262        return 0;
 263}
 264#endif
 265
 266const struct dev_pm_ops arizona_pm_ops = {
 267        SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
 268                           arizona_runtime_resume,
 269                           NULL)
 270};
 271EXPORT_SYMBOL_GPL(arizona_pm_ops);
 272
 273static struct mfd_cell early_devs[] = {
 274        { .name = "arizona-ldo1" },
 275};
 276
 277static struct mfd_cell wm5102_devs[] = {
 278        { .name = "arizona-extcon" },
 279        { .name = "arizona-gpio" },
 280        { .name = "arizona-haptics" },
 281        { .name = "arizona-micsupp" },
 282        { .name = "arizona-pwm" },
 283        { .name = "wm5102-codec" },
 284};
 285
 286static struct mfd_cell wm5110_devs[] = {
 287        { .name = "arizona-extcon" },
 288        { .name = "arizona-gpio" },
 289        { .name = "arizona-haptics" },
 290        { .name = "arizona-micsupp" },
 291        { .name = "arizona-pwm" },
 292        { .name = "wm5110-codec" },
 293};
 294
 295int arizona_dev_init(struct arizona *arizona)
 296{
 297        struct device *dev = arizona->dev;
 298        const char *type_name;
 299        unsigned int reg, val;
 300        int (*apply_patch)(struct arizona *) = NULL;
 301        int ret, i;
 302
 303        dev_set_drvdata(arizona->dev, arizona);
 304        mutex_init(&arizona->clk_lock);
 305
 306        if (dev_get_platdata(arizona->dev))
 307                memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
 308                       sizeof(arizona->pdata));
 309
 310        regcache_cache_only(arizona->regmap, true);
 311
 312        switch (arizona->type) {
 313        case WM5102:
 314        case WM5110:
 315                for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
 316                        arizona->core_supplies[i].supply
 317                                = wm5102_core_supplies[i];
 318                arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
 319                break;
 320        default:
 321                dev_err(arizona->dev, "Unknown device type %d\n",
 322                        arizona->type);
 323                return -EINVAL;
 324        }
 325
 326        ret = mfd_add_devices(arizona->dev, -1, early_devs,
 327                              ARRAY_SIZE(early_devs), NULL, 0, NULL);
 328        if (ret != 0) {
 329                dev_err(dev, "Failed to add early children: %d\n", ret);
 330                return ret;
 331        }
 332
 333        ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
 334                                      arizona->core_supplies);
 335        if (ret != 0) {
 336                dev_err(dev, "Failed to request core supplies: %d\n",
 337                        ret);
 338                goto err_early;
 339        }
 340
 341        arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD");
 342        if (IS_ERR(arizona->dcvdd)) {
 343                ret = PTR_ERR(arizona->dcvdd);
 344                dev_err(dev, "Failed to request DCVDD: %d\n", ret);
 345                goto err_early;
 346        }
 347
 348        ret = regulator_bulk_enable(arizona->num_core_supplies,
 349                                    arizona->core_supplies);
 350        if (ret != 0) {
 351                dev_err(dev, "Failed to enable core supplies: %d\n",
 352                        ret);
 353                goto err_early;
 354        }
 355
 356        ret = regulator_enable(arizona->dcvdd);
 357        if (ret != 0) {
 358                dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
 359                goto err_enable;
 360        }
 361
 362        if (arizona->pdata.reset) {
 363                /* Start out with /RESET low to put the chip into reset */
 364                ret = gpio_request_one(arizona->pdata.reset,
 365                                       GPIOF_DIR_OUT | GPIOF_INIT_LOW,
 366                                       "arizona /RESET");
 367                if (ret != 0) {
 368                        dev_err(dev, "Failed to request /RESET: %d\n", ret);
 369                        goto err_dcvdd;
 370                }
 371
 372                gpio_set_value_cansleep(arizona->pdata.reset, 1);
 373        }
 374
 375        regcache_cache_only(arizona->regmap, false);
 376
 377        ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
 378        if (ret != 0) {
 379                dev_err(dev, "Failed to read ID register: %d\n", ret);
 380                goto err_reset;
 381        }
 382
 383        ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
 384                          &arizona->rev);
 385        if (ret != 0) {
 386                dev_err(dev, "Failed to read revision register: %d\n", ret);
 387                goto err_reset;
 388        }
 389        arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
 390
 391        switch (reg) {
 392#ifdef CONFIG_MFD_WM5102
 393        case 0x5102:
 394                type_name = "WM5102";
 395                if (arizona->type != WM5102) {
 396                        dev_err(arizona->dev, "WM5102 registered as %d\n",
 397                                arizona->type);
 398                        arizona->type = WM5102;
 399                }
 400                apply_patch = wm5102_patch;
 401                break;
 402#endif
 403#ifdef CONFIG_MFD_WM5110
 404        case 0x5110:
 405                type_name = "WM5110";
 406                if (arizona->type != WM5110) {
 407                        dev_err(arizona->dev, "WM5110 registered as %d\n",
 408                                arizona->type);
 409                        arizona->type = WM5110;
 410                }
 411                apply_patch = wm5110_patch;
 412                break;
 413#endif
 414        default:
 415                dev_err(arizona->dev, "Unknown device ID %x\n", reg);
 416                goto err_reset;
 417        }
 418
 419        dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
 420
 421        /* If we have a /RESET GPIO we'll already be reset */
 422        if (!arizona->pdata.reset) {
 423                regcache_mark_dirty(arizona->regmap);
 424
 425                ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
 426                if (ret != 0) {
 427                        dev_err(dev, "Failed to reset device: %d\n", ret);
 428                        goto err_reset;
 429                }
 430
 431                ret = regcache_sync(arizona->regmap);
 432                if (ret != 0) {
 433                        dev_err(dev, "Failed to sync device: %d\n", ret);
 434                        goto err_reset;
 435                }
 436        }
 437
 438        ret = arizona_wait_for_boot(arizona);
 439        if (ret != 0) {
 440                dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
 441                goto err_reset;
 442        }
 443
 444        if (apply_patch) {
 445                ret = apply_patch(arizona);
 446                if (ret != 0) {
 447                        dev_err(arizona->dev, "Failed to apply patch: %d\n",
 448                                ret);
 449                        goto err_reset;
 450                }
 451        }
 452
 453        for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
 454                if (!arizona->pdata.gpio_defaults[i])
 455                        continue;
 456
 457                regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
 458                             arizona->pdata.gpio_defaults[i]);
 459        }
 460
 461        pm_runtime_set_autosuspend_delay(arizona->dev, 100);
 462        pm_runtime_use_autosuspend(arizona->dev);
 463        pm_runtime_enable(arizona->dev);
 464
 465        /* Chip default */
 466        if (!arizona->pdata.clk32k_src)
 467                arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
 468
 469        switch (arizona->pdata.clk32k_src) {
 470        case ARIZONA_32KZ_MCLK1:
 471        case ARIZONA_32KZ_MCLK2:
 472                regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
 473                                   ARIZONA_CLK_32K_SRC_MASK,
 474                                   arizona->pdata.clk32k_src - 1);
 475                break;
 476        case ARIZONA_32KZ_NONE:
 477                regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
 478                                   ARIZONA_CLK_32K_SRC_MASK, 2);
 479                break;
 480        default:
 481                dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
 482                        arizona->pdata.clk32k_src);
 483                ret = -EINVAL;
 484                goto err_reset;
 485        }
 486
 487        for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
 488                /* Default for both is 0 so noop with defaults */
 489                val = arizona->pdata.dmic_ref[i]
 490                        << ARIZONA_IN1_DMIC_SUP_SHIFT;
 491                val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
 492
 493                regmap_update_bits(arizona->regmap,
 494                                   ARIZONA_IN1L_CONTROL + (i * 8),
 495                                   ARIZONA_IN1_DMIC_SUP_MASK |
 496                                   ARIZONA_IN1_MODE_MASK, val);
 497        }
 498
 499        for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
 500                /* Default is 0 so noop with defaults */
 501                if (arizona->pdata.out_mono[i])
 502                        val = ARIZONA_OUT1_MONO;
 503                else
 504                        val = 0;
 505
 506                regmap_update_bits(arizona->regmap,
 507                                   ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
 508                                   ARIZONA_OUT1_MONO, val);
 509        }
 510
 511        for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
 512                if (arizona->pdata.spk_mute[i])
 513                        regmap_update_bits(arizona->regmap,
 514                                           ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
 515                                           ARIZONA_SPK1_MUTE_ENDIAN_MASK |
 516                                           ARIZONA_SPK1_MUTE_SEQ1_MASK,
 517                                           arizona->pdata.spk_mute[i]);
 518
 519                if (arizona->pdata.spk_fmt[i])
 520                        regmap_update_bits(arizona->regmap,
 521                                           ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
 522                                           ARIZONA_SPK1_FMT_MASK,
 523                                           arizona->pdata.spk_fmt[i]);
 524        }
 525
 526        /* Set up for interrupts */
 527        ret = arizona_irq_init(arizona);
 528        if (ret != 0)
 529                goto err_reset;
 530
 531        arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
 532                            arizona_clkgen_err, arizona);
 533        arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
 534                            arizona_overclocked, arizona);
 535        arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
 536                            arizona_underclocked, arizona);
 537
 538        switch (arizona->type) {
 539        case WM5102:
 540                ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
 541                                      ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
 542                break;
 543        case WM5110:
 544                ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
 545                                      ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
 546                break;
 547        }
 548
 549        if (ret != 0) {
 550                dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
 551                goto err_irq;
 552        }
 553
 554#ifdef CONFIG_PM_RUNTIME
 555        regulator_disable(arizona->dcvdd);
 556#endif
 557
 558        return 0;
 559
 560err_irq:
 561        arizona_irq_exit(arizona);
 562err_reset:
 563        if (arizona->pdata.reset) {
 564                gpio_set_value_cansleep(arizona->pdata.reset, 1);
 565                gpio_free(arizona->pdata.reset);
 566        }
 567err_dcvdd:
 568        regulator_disable(arizona->dcvdd);
 569err_enable:
 570        regulator_bulk_disable(arizona->num_core_supplies,
 571                               arizona->core_supplies);
 572err_early:
 573        mfd_remove_devices(dev);
 574        return ret;
 575}
 576EXPORT_SYMBOL_GPL(arizona_dev_init);
 577
 578int arizona_dev_exit(struct arizona *arizona)
 579{
 580        mfd_remove_devices(arizona->dev);
 581        arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
 582        arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
 583        arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
 584        pm_runtime_disable(arizona->dev);
 585        arizona_irq_exit(arizona);
 586        return 0;
 587}
 588EXPORT_SYMBOL_GPL(arizona_dev_exit);
 589
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.