linux/drivers/mfd/88pm860x-core.c
<<
>>
Prefs
   1/*
   2 * Base driver for Marvell 88PM8607
   3 *
   4 * Copyright (C) 2009 Marvell International Ltd.
   5 *      Haojian Zhuang <haojian.zhuang@marvell.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include <linux/i2c.h>
  15#include <linux/irq.h>
  16#include <linux/interrupt.h>
  17#include <linux/platform_device.h>
  18#include <linux/mfd/core.h>
  19#include <linux/mfd/88pm860x.h>
  20#include <linux/regulator/machine.h>
  21
  22#define INT_STATUS_NUM                  3
  23
  24static struct resource bk_resources[] __devinitdata = {
  25        {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
  26        {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
  27        {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
  28};
  29
  30static struct resource led_resources[] __devinitdata = {
  31        {PM8606_LED1_RED,   PM8606_LED1_RED,   "led0-red",   IORESOURCE_IO,},
  32        {PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,},
  33        {PM8606_LED1_BLUE,  PM8606_LED1_BLUE,  "led0-blue",  IORESOURCE_IO,},
  34        {PM8606_LED2_RED,   PM8606_LED2_RED,   "led1-red",   IORESOURCE_IO,},
  35        {PM8606_LED2_GREEN, PM8606_LED2_GREEN, "led1-green", IORESOURCE_IO,},
  36        {PM8606_LED2_BLUE,  PM8606_LED2_BLUE,  "led1-blue",  IORESOURCE_IO,},
  37};
  38
  39static struct resource regulator_resources[] __devinitdata = {
  40        {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,},
  41        {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,},
  42        {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,},
  43        {PM8607_ID_LDO1,  PM8607_ID_LDO1,  "ldo-01", IORESOURCE_IO,},
  44        {PM8607_ID_LDO2,  PM8607_ID_LDO2,  "ldo-02", IORESOURCE_IO,},
  45        {PM8607_ID_LDO3,  PM8607_ID_LDO3,  "ldo-03", IORESOURCE_IO,},
  46        {PM8607_ID_LDO4,  PM8607_ID_LDO4,  "ldo-04", IORESOURCE_IO,},
  47        {PM8607_ID_LDO5,  PM8607_ID_LDO5,  "ldo-05", IORESOURCE_IO,},
  48        {PM8607_ID_LDO6,  PM8607_ID_LDO6,  "ldo-06", IORESOURCE_IO,},
  49        {PM8607_ID_LDO7,  PM8607_ID_LDO7,  "ldo-07", IORESOURCE_IO,},
  50        {PM8607_ID_LDO8,  PM8607_ID_LDO8,  "ldo-08", IORESOURCE_IO,},
  51        {PM8607_ID_LDO9,  PM8607_ID_LDO9,  "ldo-09", IORESOURCE_IO,},
  52        {PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_IO,},
  53        {PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_IO,},
  54        {PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_IO,},
  55        {PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_IO,},
  56        {PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_IO,},
  57        {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,},
  58};
  59
  60static struct resource touch_resources[] __devinitdata = {
  61        {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
  62};
  63
  64static struct resource onkey_resources[] __devinitdata = {
  65        {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
  66};
  67
  68static struct resource codec_resources[] __devinitdata = {
  69        /* Headset microphone insertion or removal */
  70        {PM8607_IRQ_MICIN,   PM8607_IRQ_MICIN,   "micin",   IORESOURCE_IRQ,},
  71        /* Hook-switch press or release */
  72        {PM8607_IRQ_HOOK,    PM8607_IRQ_HOOK,    "hook",    IORESOURCE_IRQ,},
  73        /* Headset insertion or removal */
  74        {PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,},
  75        /* Audio short */
  76        {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
  77};
  78
  79static struct resource battery_resources[] __devinitdata = {
  80        {PM8607_IRQ_CC,  PM8607_IRQ_CC,  "columb counter", IORESOURCE_IRQ,},
  81        {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery",        IORESOURCE_IRQ,},
  82};
  83
  84static struct resource charger_resources[] __devinitdata = {
  85        {PM8607_IRQ_CHG,  PM8607_IRQ_CHG,  "charger detect",  IORESOURCE_IRQ,},
  86        {PM8607_IRQ_CHG_DONE,  PM8607_IRQ_CHG_DONE,  "charging done",       IORESOURCE_IRQ,},
  87        {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout",    IORESOURCE_IRQ,},
  88        {PM8607_IRQ_GPADC1,    PM8607_IRQ_GPADC1,    "battery temperature", IORESOURCE_IRQ,},
  89        {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,},
  90        {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage",    IORESOURCE_IRQ,},
  91};
  92
  93static struct resource preg_resources[] __devinitdata = {
  94        {PM8606_ID_PREG,  PM8606_ID_PREG,  "preg",   IORESOURCE_IO,},
  95};
  96
  97static struct resource rtc_resources[] __devinitdata = {
  98        {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
  99};
 100
 101static struct mfd_cell bk_devs[] = {
 102        {"88pm860x-backlight", 0,},
 103        {"88pm860x-backlight", 1,},
 104        {"88pm860x-backlight", 2,},
 105};
 106
 107static struct mfd_cell led_devs[] = {
 108        {"88pm860x-led", 0,},
 109        {"88pm860x-led", 1,},
 110        {"88pm860x-led", 2,},
 111        {"88pm860x-led", 3,},
 112        {"88pm860x-led", 4,},
 113        {"88pm860x-led", 5,},
 114};
 115
 116static struct mfd_cell regulator_devs[] = {
 117        {"88pm860x-regulator", 0,},
 118        {"88pm860x-regulator", 1,},
 119        {"88pm860x-regulator", 2,},
 120        {"88pm860x-regulator", 3,},
 121        {"88pm860x-regulator", 4,},
 122        {"88pm860x-regulator", 5,},
 123        {"88pm860x-regulator", 6,},
 124        {"88pm860x-regulator", 7,},
 125        {"88pm860x-regulator", 8,},
 126        {"88pm860x-regulator", 9,},
 127        {"88pm860x-regulator", 10,},
 128        {"88pm860x-regulator", 11,},
 129        {"88pm860x-regulator", 12,},
 130        {"88pm860x-regulator", 13,},
 131        {"88pm860x-regulator", 14,},
 132        {"88pm860x-regulator", 15,},
 133        {"88pm860x-regulator", 16,},
 134        {"88pm860x-regulator", 17,},
 135};
 136
 137static struct mfd_cell touch_devs[] = {
 138        {"88pm860x-touch", -1,},
 139};
 140
 141static struct mfd_cell onkey_devs[] = {
 142        {"88pm860x-onkey", -1,},
 143};
 144
 145static struct mfd_cell codec_devs[] = {
 146        {"88pm860x-codec", -1,},
 147};
 148
 149static struct regulator_consumer_supply preg_supply[] = {
 150        REGULATOR_SUPPLY("preg", "charger-manager"),
 151};
 152
 153static struct regulator_init_data preg_init_data = {
 154        .num_consumer_supplies  = ARRAY_SIZE(preg_supply),
 155        .consumer_supplies      = &preg_supply[0],
 156};
 157
 158static struct mfd_cell power_devs[] = {
 159        {"88pm860x-battery", -1,},
 160        {"88pm860x-charger", -1,},
 161        {"88pm860x-preg",    -1,},
 162};
 163
 164static struct mfd_cell rtc_devs[] = {
 165        {"88pm860x-rtc", -1,},
 166};
 167
 168
 169struct pm860x_irq_data {
 170        int     reg;
 171        int     mask_reg;
 172        int     enable;         /* enable or not */
 173        int     offs;           /* bit offset in mask register */
 174};
 175
 176static struct pm860x_irq_data pm860x_irqs[] = {
 177        [PM8607_IRQ_ONKEY] = {
 178                .reg            = PM8607_INT_STATUS1,
 179                .mask_reg       = PM8607_INT_MASK_1,
 180                .offs           = 1 << 0,
 181        },
 182        [PM8607_IRQ_EXTON] = {
 183                .reg            = PM8607_INT_STATUS1,
 184                .mask_reg       = PM8607_INT_MASK_1,
 185                .offs           = 1 << 1,
 186        },
 187        [PM8607_IRQ_CHG] = {
 188                .reg            = PM8607_INT_STATUS1,
 189                .mask_reg       = PM8607_INT_MASK_1,
 190                .offs           = 1 << 2,
 191        },
 192        [PM8607_IRQ_BAT] = {
 193                .reg            = PM8607_INT_STATUS1,
 194                .mask_reg       = PM8607_INT_MASK_1,
 195                .offs           = 1 << 3,
 196        },
 197        [PM8607_IRQ_RTC] = {
 198                .reg            = PM8607_INT_STATUS1,
 199                .mask_reg       = PM8607_INT_MASK_1,
 200                .offs           = 1 << 4,
 201        },
 202        [PM8607_IRQ_CC] = {
 203                .reg            = PM8607_INT_STATUS1,
 204                .mask_reg       = PM8607_INT_MASK_1,
 205                .offs           = 1 << 5,
 206        },
 207        [PM8607_IRQ_VBAT] = {
 208                .reg            = PM8607_INT_STATUS2,
 209                .mask_reg       = PM8607_INT_MASK_2,
 210                .offs           = 1 << 0,
 211        },
 212        [PM8607_IRQ_VCHG] = {
 213                .reg            = PM8607_INT_STATUS2,
 214                .mask_reg       = PM8607_INT_MASK_2,
 215                .offs           = 1 << 1,
 216        },
 217        [PM8607_IRQ_VSYS] = {
 218                .reg            = PM8607_INT_STATUS2,
 219                .mask_reg       = PM8607_INT_MASK_2,
 220                .offs           = 1 << 2,
 221        },
 222        [PM8607_IRQ_TINT] = {
 223                .reg            = PM8607_INT_STATUS2,
 224                .mask_reg       = PM8607_INT_MASK_2,
 225                .offs           = 1 << 3,
 226        },
 227        [PM8607_IRQ_GPADC0] = {
 228                .reg            = PM8607_INT_STATUS2,
 229                .mask_reg       = PM8607_INT_MASK_2,
 230                .offs           = 1 << 4,
 231        },
 232        [PM8607_IRQ_GPADC1] = {
 233                .reg            = PM8607_INT_STATUS2,
 234                .mask_reg       = PM8607_INT_MASK_2,
 235                .offs           = 1 << 5,
 236        },
 237        [PM8607_IRQ_GPADC2] = {
 238                .reg            = PM8607_INT_STATUS2,
 239                .mask_reg       = PM8607_INT_MASK_2,
 240                .offs           = 1 << 6,
 241        },
 242        [PM8607_IRQ_GPADC3] = {
 243                .reg            = PM8607_INT_STATUS2,
 244                .mask_reg       = PM8607_INT_MASK_2,
 245                .offs           = 1 << 7,
 246        },
 247        [PM8607_IRQ_AUDIO_SHORT] = {
 248                .reg            = PM8607_INT_STATUS3,
 249                .mask_reg       = PM8607_INT_MASK_3,
 250                .offs           = 1 << 0,
 251        },
 252        [PM8607_IRQ_PEN] = {
 253                .reg            = PM8607_INT_STATUS3,
 254                .mask_reg       = PM8607_INT_MASK_3,
 255                .offs           = 1 << 1,
 256        },
 257        [PM8607_IRQ_HEADSET] = {
 258                .reg            = PM8607_INT_STATUS3,
 259                .mask_reg       = PM8607_INT_MASK_3,
 260                .offs           = 1 << 2,
 261        },
 262        [PM8607_IRQ_HOOK] = {
 263                .reg            = PM8607_INT_STATUS3,
 264                .mask_reg       = PM8607_INT_MASK_3,
 265                .offs           = 1 << 3,
 266        },
 267        [PM8607_IRQ_MICIN] = {
 268                .reg            = PM8607_INT_STATUS3,
 269                .mask_reg       = PM8607_INT_MASK_3,
 270                .offs           = 1 << 4,
 271        },
 272        [PM8607_IRQ_CHG_FAIL] = {
 273                .reg            = PM8607_INT_STATUS3,
 274                .mask_reg       = PM8607_INT_MASK_3,
 275                .offs           = 1 << 5,
 276        },
 277        [PM8607_IRQ_CHG_DONE] = {
 278                .reg            = PM8607_INT_STATUS3,
 279                .mask_reg       = PM8607_INT_MASK_3,
 280                .offs           = 1 << 6,
 281        },
 282        [PM8607_IRQ_CHG_FAULT] = {
 283                .reg            = PM8607_INT_STATUS3,
 284                .mask_reg       = PM8607_INT_MASK_3,
 285                .offs           = 1 << 7,
 286        },
 287};
 288
 289static irqreturn_t pm860x_irq(int irq, void *data)
 290{
 291        struct pm860x_chip *chip = data;
 292        struct pm860x_irq_data *irq_data;
 293        struct i2c_client *i2c;
 294        int read_reg = -1, value = 0;
 295        int i;
 296
 297        i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
 298        for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
 299                irq_data = &pm860x_irqs[i];
 300                if (read_reg != irq_data->reg) {
 301                        read_reg = irq_data->reg;
 302                        value = pm860x_reg_read(i2c, irq_data->reg);
 303                }
 304                if (value & irq_data->enable)
 305                        handle_nested_irq(chip->irq_base + i);
 306        }
 307        return IRQ_HANDLED;
 308}
 309
 310static void pm860x_irq_lock(struct irq_data *data)
 311{
 312        struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
 313
 314        mutex_lock(&chip->irq_lock);
 315}
 316
 317static void pm860x_irq_sync_unlock(struct irq_data *data)
 318{
 319        struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
 320        struct pm860x_irq_data *irq_data;
 321        struct i2c_client *i2c;
 322        static unsigned char cached[3] = {0x0, 0x0, 0x0};
 323        unsigned char mask[3];
 324        int i;
 325
 326        i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
 327        /* Load cached value. In initial, all IRQs are masked */
 328        for (i = 0; i < 3; i++)
 329                mask[i] = cached[i];
 330        for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
 331                irq_data = &pm860x_irqs[i];
 332                switch (irq_data->mask_reg) {
 333                case PM8607_INT_MASK_1:
 334                        mask[0] &= ~irq_data->offs;
 335                        mask[0] |= irq_data->enable;
 336                        break;
 337                case PM8607_INT_MASK_2:
 338                        mask[1] &= ~irq_data->offs;
 339                        mask[1] |= irq_data->enable;
 340                        break;
 341                case PM8607_INT_MASK_3:
 342                        mask[2] &= ~irq_data->offs;
 343                        mask[2] |= irq_data->enable;
 344                        break;
 345                default:
 346                        dev_err(chip->dev, "wrong IRQ\n");
 347                        break;
 348                }
 349        }
 350        /* update mask into registers */
 351        for (i = 0; i < 3; i++) {
 352                if (mask[i] != cached[i]) {
 353                        cached[i] = mask[i];
 354                        pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
 355                }
 356        }
 357
 358        mutex_unlock(&chip->irq_lock);
 359}
 360
 361static void pm860x_irq_enable(struct irq_data *data)
 362{
 363        struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
 364        pm860x_irqs[data->irq - chip->irq_base].enable
 365                = pm860x_irqs[data->irq - chip->irq_base].offs;
 366}
 367
 368static void pm860x_irq_disable(struct irq_data *data)
 369{
 370        struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
 371        pm860x_irqs[data->irq - chip->irq_base].enable = 0;
 372}
 373
 374static struct irq_chip pm860x_irq_chip = {
 375        .name           = "88pm860x",
 376        .irq_bus_lock   = pm860x_irq_lock,
 377        .irq_bus_sync_unlock = pm860x_irq_sync_unlock,
 378        .irq_enable     = pm860x_irq_enable,
 379        .irq_disable    = pm860x_irq_disable,
 380};
 381
 382static int __devinit device_gpadc_init(struct pm860x_chip *chip,
 383                                       struct pm860x_platform_data *pdata)
 384{
 385        struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
 386                                : chip->companion;
 387        int data;
 388        int ret;
 389
 390        /* initialize GPADC without activating it */
 391
 392        if (!pdata || !pdata->touch)
 393                return -EINVAL;
 394
 395        /* set GPADC MISC1 register */
 396        data = 0;
 397        data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
 398        data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
 399        data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
 400        data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
 401        if (data) {
 402                ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
 403                if (ret < 0)
 404                        goto out;
 405        }
 406        /* set tsi prebias time */
 407        if (pdata->touch->tsi_prebias) {
 408                data = pdata->touch->tsi_prebias;
 409                ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
 410                if (ret < 0)
 411                        goto out;
 412        }
 413        /* set prebias & prechg time of pen detect */
 414        data = 0;
 415        data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
 416        data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
 417        if (data) {
 418                ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
 419                if (ret < 0)
 420                        goto out;
 421        }
 422
 423        ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
 424                              PM8607_GPADC_EN, PM8607_GPADC_EN);
 425out:
 426        return ret;
 427}
 428
 429static int __devinit device_irq_init(struct pm860x_chip *chip,
 430                                     struct pm860x_platform_data *pdata)
 431{
 432        struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
 433                                : chip->companion;
 434        unsigned char status_buf[INT_STATUS_NUM];
 435        unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
 436        int i, data, mask, ret = -EINVAL;
 437        int __irq;
 438
 439        if (!pdata || !pdata->irq_base) {
 440                dev_warn(chip->dev, "No interrupt support on IRQ base\n");
 441                return -EINVAL;
 442        }
 443
 444        mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
 445                | PM8607_B0_MISC1_INT_MASK;
 446        data = 0;
 447        chip->irq_mode = 0;
 448        if (pdata && pdata->irq_mode) {
 449                /*
 450                 * irq_mode defines the way of clearing interrupt. If it's 1,
 451                 * clear IRQ by write. Otherwise, clear it by read.
 452                 * This control bit is valid from 88PM8607 B0 steping.
 453                 */
 454                data |= PM8607_B0_MISC1_INT_CLEAR;
 455                chip->irq_mode = 1;
 456        }
 457        ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
 458        if (ret < 0)
 459                goto out;
 460
 461        /* mask all IRQs */
 462        memset(status_buf, 0, INT_STATUS_NUM);
 463        ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
 464                                INT_STATUS_NUM, status_buf);
 465        if (ret < 0)
 466                goto out;
 467
 468        if (chip->irq_mode) {
 469                /* clear interrupt status by write */
 470                memset(status_buf, 0xFF, INT_STATUS_NUM);
 471                ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
 472                                        INT_STATUS_NUM, status_buf);
 473        } else {
 474                /* clear interrupt status by read */
 475                ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
 476                                        INT_STATUS_NUM, status_buf);
 477        }
 478        if (ret < 0)
 479                goto out;
 480
 481        mutex_init(&chip->irq_lock);
 482        chip->irq_base = pdata->irq_base;
 483        chip->core_irq = i2c->irq;
 484        if (!chip->core_irq)
 485                goto out;
 486
 487        /* register IRQ by genirq */
 488        for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
 489                __irq = i + chip->irq_base;
 490                irq_set_chip_data(__irq, chip);
 491                irq_set_chip_and_handler(__irq, &pm860x_irq_chip,
 492                                         handle_edge_irq);
 493                irq_set_nested_thread(__irq, 1);
 494#ifdef CONFIG_ARM
 495                set_irq_flags(__irq, IRQF_VALID);
 496#else
 497                irq_set_noprobe(__irq);
 498#endif
 499        }
 500
 501        ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
 502                                   "88pm860x", chip);
 503        if (ret) {
 504                dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
 505                chip->core_irq = 0;
 506        }
 507
 508        return 0;
 509out:
 510        chip->core_irq = 0;
 511        return ret;
 512}
 513
 514static void device_irq_exit(struct pm860x_chip *chip)
 515{
 516        if (chip->core_irq)
 517                free_irq(chip->core_irq, chip);
 518}
 519
 520int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
 521{
 522        int ret = -EIO;
 523        struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
 524                chip->client : chip->companion;
 525
 526        dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
 527        dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
 528                        __func__, chip->osc_vote,
 529                        chip->osc_status);
 530
 531        mutex_lock(&chip->osc_lock);
 532        /* Update voting status */
 533        chip->osc_vote |= client;
 534        /* If reference group is off - turn on*/
 535        if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
 536                chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
 537                /* Enable Reference group Vsys */
 538                if (pm860x_set_bits(i2c, PM8606_VSYS,
 539                                PM8606_VSYS_EN, PM8606_VSYS_EN))
 540                        goto out;
 541
 542                /*Enable Internal Oscillator */
 543                if (pm860x_set_bits(i2c, PM8606_MISC,
 544                                PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
 545                        goto out;
 546                /* Update status (only if writes succeed) */
 547                chip->osc_status = PM8606_REF_GP_OSC_ON;
 548        }
 549        mutex_unlock(&chip->osc_lock);
 550
 551        dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
 552                        __func__, chip->osc_vote,
 553                        chip->osc_status, ret);
 554        return 0;
 555out:
 556        mutex_unlock(&chip->osc_lock);
 557        return ret;
 558}
 559EXPORT_SYMBOL(pm8606_osc_enable);
 560
 561int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
 562{
 563        int ret = -EIO;
 564        struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
 565                chip->client : chip->companion;
 566
 567        dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
 568        dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
 569                        __func__, chip->osc_vote,
 570                        chip->osc_status);
 571
 572        mutex_lock(&chip->osc_lock);
 573        /*Update voting status */
 574        chip->osc_vote &= ~(client);
 575        /* If reference group is off and this is the last client to release
 576         * - turn off */
 577        if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
 578                        (chip->osc_vote == REF_GP_NO_CLIENTS)) {
 579                chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
 580                /* Disable Reference group Vsys */
 581                if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
 582                        goto out;
 583                /* Disable Internal Oscillator */
 584                if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
 585                        goto out;
 586                chip->osc_status = PM8606_REF_GP_OSC_OFF;
 587        }
 588        mutex_unlock(&chip->osc_lock);
 589
 590        dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
 591                        __func__, chip->osc_vote,
 592                        chip->osc_status, ret);
 593        return 0;
 594out:
 595        mutex_unlock(&chip->osc_lock);
 596        return ret;
 597}
 598EXPORT_SYMBOL(pm8606_osc_disable);
 599
 600static void __devinit device_osc_init(struct i2c_client *i2c)
 601{
 602        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 603
 604        mutex_init(&chip->osc_lock);
 605        /* init portofino reference group voting and status */
 606        /* Disable Reference group Vsys */
 607        pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
 608        /* Disable Internal Oscillator */
 609        pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
 610
 611        chip->osc_vote = REF_GP_NO_CLIENTS;
 612        chip->osc_status = PM8606_REF_GP_OSC_OFF;
 613}
 614
 615static void __devinit device_bk_init(struct pm860x_chip *chip,
 616                                     struct pm860x_platform_data *pdata)
 617{
 618        int ret;
 619        int i, j, id;
 620
 621        if ((pdata == NULL) || (pdata->backlight == NULL))
 622                return;
 623
 624        if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
 625                pdata->num_backlights = ARRAY_SIZE(bk_devs);
 626
 627        for (i = 0; i < pdata->num_backlights; i++) {
 628                bk_devs[i].platform_data = &pdata->backlight[i];
 629                bk_devs[i].pdata_size = sizeof(struct pm860x_backlight_pdata);
 630
 631                for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
 632                        id = bk_resources[j].start;
 633                        if (pdata->backlight[i].flags != id)
 634                                continue;
 635
 636                        bk_devs[i].num_resources = 1;
 637                        bk_devs[i].resources = &bk_resources[j];
 638                        ret = mfd_add_devices(chip->dev, 0,
 639                                              &bk_devs[i], 1,
 640                                              &bk_resources[j], 0, NULL);
 641                        if (ret < 0) {
 642                                dev_err(chip->dev, "Failed to add "
 643                                        "backlight subdev\n");
 644                                return;
 645                        }
 646                }
 647        }
 648}
 649
 650static void __devinit device_led_init(struct pm860x_chip *chip,
 651                                      struct pm860x_platform_data *pdata)
 652{
 653        int ret;
 654        int i, j, id;
 655
 656        if ((pdata == NULL) || (pdata->led == NULL))
 657                return;
 658
 659        if (pdata->num_leds > ARRAY_SIZE(led_devs))
 660                pdata->num_leds = ARRAY_SIZE(led_devs);
 661
 662        for (i = 0; i < pdata->num_leds; i++) {
 663                led_devs[i].platform_data = &pdata->led[i];
 664                led_devs[i].pdata_size = sizeof(struct pm860x_led_pdata);
 665
 666                for (j = 0; j < ARRAY_SIZE(led_devs); j++) {
 667                        id = led_resources[j].start;
 668                        if (pdata->led[i].flags != id)
 669                                continue;
 670
 671                        led_devs[i].num_resources = 1;
 672                        led_devs[i].resources = &led_resources[j],
 673                        ret = mfd_add_devices(chip->dev, 0,
 674                                              &led_devs[i], 1,
 675                                              &led_resources[j], 0, NULL);
 676                        if (ret < 0) {
 677                                dev_err(chip->dev, "Failed to add "
 678                                        "led subdev\n");
 679                                return;
 680                        }
 681                }
 682        }
 683}
 684
 685static void __devinit device_regulator_init(struct pm860x_chip *chip,
 686                                            struct pm860x_platform_data *pdata)
 687{
 688        struct regulator_init_data *initdata;
 689        int ret;
 690        int i, seq;
 691
 692        if ((pdata == NULL) || (pdata->regulator == NULL))
 693                return;
 694
 695        if (pdata->num_regulators > ARRAY_SIZE(regulator_devs))
 696                pdata->num_regulators = ARRAY_SIZE(regulator_devs);
 697
 698        for (i = 0, seq = -1; i < pdata->num_regulators; i++) {
 699                initdata = &pdata->regulator[i];
 700                seq = *(unsigned int *)initdata->driver_data;
 701                if ((seq < 0) || (seq > PM8607_ID_RG_MAX)) {
 702                        dev_err(chip->dev, "Wrong ID(%d) on regulator(%s)\n",
 703                                seq, initdata->constraints.name);
 704                        goto out;
 705                }
 706                regulator_devs[i].platform_data = &pdata->regulator[i];
 707                regulator_devs[i].pdata_size = sizeof(struct regulator_init_data);
 708                regulator_devs[i].num_resources = 1;
 709                regulator_devs[i].resources = &regulator_resources[seq];
 710
 711                ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
 712                                      &regulator_resources[seq], 0, NULL);
 713                if (ret < 0) {
 714                        dev_err(chip->dev, "Failed to add regulator subdev\n");
 715                        goto out;
 716                }
 717        }
 718out:
 719        return;
 720}
 721
 722static void __devinit device_rtc_init(struct pm860x_chip *chip,
 723                                      struct pm860x_platform_data *pdata)
 724{
 725        int ret;
 726
 727        if ((pdata == NULL))
 728                return;
 729
 730        rtc_devs[0].platform_data = pdata->rtc;
 731        rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
 732        rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
 733        rtc_devs[0].resources = &rtc_resources[0];
 734        ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
 735                              ARRAY_SIZE(rtc_devs), &rtc_resources[0],
 736                              chip->irq_base, NULL);
 737        if (ret < 0)
 738                dev_err(chip->dev, "Failed to add rtc subdev\n");
 739}
 740
 741static void __devinit device_touch_init(struct pm860x_chip *chip,
 742                                        struct pm860x_platform_data *pdata)
 743{
 744        int ret;
 745
 746        if (pdata == NULL)
 747                return;
 748
 749        touch_devs[0].platform_data = pdata->touch;
 750        touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
 751        touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
 752        touch_devs[0].resources = &touch_resources[0];
 753        ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
 754                              ARRAY_SIZE(touch_devs), &touch_resources[0],
 755                              chip->irq_base, NULL);
 756        if (ret < 0)
 757                dev_err(chip->dev, "Failed to add touch subdev\n");
 758}
 759
 760static void __devinit device_power_init(struct pm860x_chip *chip,
 761                                        struct pm860x_platform_data *pdata)
 762{
 763        int ret;
 764
 765        if (pdata == NULL)
 766                return;
 767
 768        power_devs[0].platform_data = pdata->power;
 769        power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
 770        power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
 771        power_devs[0].resources = &battery_resources[0],
 772        ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
 773                              &battery_resources[0], chip->irq_base, NULL);
 774        if (ret < 0)
 775                dev_err(chip->dev, "Failed to add battery subdev\n");
 776
 777        power_devs[1].platform_data = pdata->power;
 778        power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
 779        power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
 780        power_devs[1].resources = &charger_resources[0],
 781        ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
 782                              &charger_resources[0], chip->irq_base, NULL);
 783        if (ret < 0)
 784                dev_err(chip->dev, "Failed to add charger subdev\n");
 785
 786        power_devs[2].platform_data = &preg_init_data;
 787        power_devs[2].pdata_size = sizeof(struct regulator_init_data);
 788        power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
 789        power_devs[2].resources = &preg_resources[0],
 790        ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
 791                              &preg_resources[0], chip->irq_base, NULL);
 792        if (ret < 0)
 793                dev_err(chip->dev, "Failed to add preg subdev\n");
 794}
 795
 796static void __devinit device_onkey_init(struct pm860x_chip *chip,
 797                                        struct pm860x_platform_data *pdata)
 798{
 799        int ret;
 800
 801        onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
 802        onkey_devs[0].resources = &onkey_resources[0],
 803        ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
 804                              ARRAY_SIZE(onkey_devs), &onkey_resources[0],
 805                              chip->irq_base, NULL);
 806        if (ret < 0)
 807                dev_err(chip->dev, "Failed to add onkey subdev\n");
 808}
 809
 810static void __devinit device_codec_init(struct pm860x_chip *chip,
 811                                        struct pm860x_platform_data *pdata)
 812{
 813        int ret;
 814
 815        codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
 816        codec_devs[0].resources = &codec_resources[0],
 817        ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
 818                              ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
 819                              NULL);
 820        if (ret < 0)
 821                dev_err(chip->dev, "Failed to add codec subdev\n");
 822}
 823
 824static void __devinit device_8607_init(struct pm860x_chip *chip,
 825                                       struct i2c_client *i2c,
 826                                       struct pm860x_platform_data *pdata)
 827{
 828        int data, ret;
 829
 830        ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
 831        if (ret < 0) {
 832                dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
 833                goto out;
 834        }
 835        switch (ret & PM8607_VERSION_MASK) {
 836        case 0x40:
 837        case 0x50:
 838                dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
 839                         ret);
 840                break;
 841        default:
 842                dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
 843                        "Chip ID: %02x\n", ret);
 844                goto out;
 845        }
 846
 847        ret = pm860x_reg_read(i2c, PM8607_BUCK3);
 848        if (ret < 0) {
 849                dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
 850                goto out;
 851        }
 852        if (ret & PM8607_BUCK3_DOUBLE)
 853                chip->buck3_double = 1;
 854
 855        ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
 856        if (ret < 0) {
 857                dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
 858                goto out;
 859        }
 860
 861        if (pdata && (pdata->i2c_port == PI2C_PORT))
 862                data = PM8607_B0_MISC1_PI2C;
 863        else
 864                data = 0;
 865        ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
 866        if (ret < 0) {
 867                dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
 868                goto out;
 869        }
 870
 871        ret = device_gpadc_init(chip, pdata);
 872        if (ret < 0)
 873                goto out;
 874
 875        ret = device_irq_init(chip, pdata);
 876        if (ret < 0)
 877                goto out;
 878
 879        device_regulator_init(chip, pdata);
 880        device_rtc_init(chip, pdata);
 881        device_onkey_init(chip, pdata);
 882        device_touch_init(chip, pdata);
 883        device_power_init(chip, pdata);
 884        device_codec_init(chip, pdata);
 885out:
 886        return;
 887}
 888
 889static void __devinit device_8606_init(struct pm860x_chip *chip,
 890                                       struct i2c_client *i2c,
 891                                       struct pm860x_platform_data *pdata)
 892{
 893        device_osc_init(i2c);
 894        device_bk_init(chip, pdata);
 895        device_led_init(chip, pdata);
 896}
 897
 898int __devinit pm860x_device_init(struct pm860x_chip *chip,
 899                       struct pm860x_platform_data *pdata)
 900{
 901        chip->core_irq = 0;
 902
 903        switch (chip->id) {
 904        case CHIP_PM8606:
 905                device_8606_init(chip, chip->client, pdata);
 906                break;
 907        case CHIP_PM8607:
 908                device_8607_init(chip, chip->client, pdata);
 909                break;
 910        }
 911
 912        if (chip->companion) {
 913                switch (chip->id) {
 914                case CHIP_PM8607:
 915                        device_8606_init(chip, chip->companion, pdata);
 916                        break;
 917                case CHIP_PM8606:
 918                        device_8607_init(chip, chip->companion, pdata);
 919                        break;
 920                }
 921        }
 922
 923        return 0;
 924}
 925
 926void __devexit pm860x_device_exit(struct pm860x_chip *chip)
 927{
 928        device_irq_exit(chip);
 929        mfd_remove_devices(chip->dev);
 930}
 931
 932MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
 933MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
 934MODULE_LICENSE("GPL");
 935
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.