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