linux/drivers/mfd/88pm805.c
<<
>>
Prefs
   1/*
   2 * Base driver for Marvell 88PM805
   3 *
   4 * Copyright (C) 2012 Marvell International Ltd.
   5 * Haojian Zhuang <haojian.zhuang@marvell.com>
   6 * Joseph(Yossi) Hanin <yhanin@marvell.com>
   7 * Qiao Zhou <zhouqiao@marvell.com>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General
  10 * Public License. See the file "COPYING" in the main directory of this
  11 * archive for more details.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21 */
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/i2c.h>
  26#include <linux/irq.h>
  27#include <linux/mfd/core.h>
  28#include <linux/mfd/88pm80x.h>
  29#include <linux/slab.h>
  30#include <linux/delay.h>
  31
  32#define PM805_CHIP_ID                   (0x00)
  33
  34static const struct i2c_device_id pm80x_id_table[] = {
  35        {"88PM805", CHIP_PM805},
  36        {} /* NULL terminated */
  37};
  38MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
  39
  40/* Interrupt Number in 88PM805 */
  41enum {
  42        PM805_IRQ_LDO_OFF,      /*0 */
  43        PM805_IRQ_SRC_DPLL_LOCK,        /*1 */
  44        PM805_IRQ_CLIP_FAULT,
  45        PM805_IRQ_MIC_CONFLICT,
  46        PM805_IRQ_HP2_SHRT,
  47        PM805_IRQ_HP1_SHRT,     /*5 */
  48        PM805_IRQ_FINE_PLL_FAULT,
  49        PM805_IRQ_RAW_PLL_FAULT,
  50        PM805_IRQ_VOLP_BTN_DET,
  51        PM805_IRQ_VOLM_BTN_DET,
  52        PM805_IRQ_SHRT_BTN_DET, /*10 */
  53        PM805_IRQ_MIC_DET,      /*11 */
  54
  55        PM805_MAX_IRQ,
  56};
  57
  58static struct resource codec_resources[] = {
  59        {
  60         /* Headset microphone insertion or removal */
  61         .name = "micin",
  62         .start = PM805_IRQ_MIC_DET,
  63         .end = PM805_IRQ_MIC_DET,
  64         .flags = IORESOURCE_IRQ,
  65         },
  66        {
  67         /* Audio short HP1 */
  68         .name = "audio-short1",
  69         .start = PM805_IRQ_HP1_SHRT,
  70         .end = PM805_IRQ_HP1_SHRT,
  71         .flags = IORESOURCE_IRQ,
  72         },
  73        {
  74         /* Audio short HP2 */
  75         .name = "audio-short2",
  76         .start = PM805_IRQ_HP2_SHRT,
  77         .end = PM805_IRQ_HP2_SHRT,
  78         .flags = IORESOURCE_IRQ,
  79         },
  80};
  81
  82static struct mfd_cell codec_devs[] = {
  83        {
  84         .name = "88pm80x-codec",
  85         .num_resources = ARRAY_SIZE(codec_resources),
  86         .resources = &codec_resources[0],
  87         .id = -1,
  88         },
  89};
  90
  91static struct regmap_irq pm805_irqs[] = {
  92        /* INT0 */
  93        [PM805_IRQ_LDO_OFF] = {
  94                .mask = PM805_INT1_HP1_SHRT,
  95        },
  96        [PM805_IRQ_SRC_DPLL_LOCK] = {
  97                .mask = PM805_INT1_HP2_SHRT,
  98        },
  99        [PM805_IRQ_CLIP_FAULT] = {
 100                .mask = PM805_INT1_MIC_CONFLICT,
 101        },
 102        [PM805_IRQ_MIC_CONFLICT] = {
 103                .mask = PM805_INT1_CLIP_FAULT,
 104        },
 105        [PM805_IRQ_HP2_SHRT] = {
 106                .mask = PM805_INT1_LDO_OFF,
 107        },
 108        [PM805_IRQ_HP1_SHRT] = {
 109                .mask = PM805_INT1_SRC_DPLL_LOCK,
 110        },
 111        /* INT1 */
 112        [PM805_IRQ_FINE_PLL_FAULT] = {
 113                .reg_offset = 1,
 114                .mask = PM805_INT2_MIC_DET,
 115        },
 116        [PM805_IRQ_RAW_PLL_FAULT] = {
 117                .reg_offset = 1,
 118                .mask = PM805_INT2_SHRT_BTN_DET,
 119        },
 120        [PM805_IRQ_VOLP_BTN_DET] = {
 121                .reg_offset = 1,
 122                .mask = PM805_INT2_VOLM_BTN_DET,
 123        },
 124        [PM805_IRQ_VOLM_BTN_DET] = {
 125                .reg_offset = 1,
 126                .mask = PM805_INT2_VOLP_BTN_DET,
 127        },
 128        [PM805_IRQ_SHRT_BTN_DET] = {
 129                .reg_offset = 1,
 130                .mask = PM805_INT2_RAW_PLL_FAULT,
 131        },
 132        [PM805_IRQ_MIC_DET] = {
 133                .reg_offset = 1,
 134                .mask = PM805_INT2_FINE_PLL_FAULT,
 135        },
 136};
 137
 138static int device_irq_init_805(struct pm80x_chip *chip)
 139{
 140        struct regmap *map = chip->regmap;
 141        unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
 142        int data, mask, ret = -EINVAL;
 143
 144        if (!map || !chip->irq) {
 145                dev_err(chip->dev, "incorrect parameters\n");
 146                return -EINVAL;
 147        }
 148
 149        /*
 150         * irq_mode defines the way of clearing interrupt. it's read-clear by
 151         * default.
 152         */
 153        mask =
 154            PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
 155            PM800_STATUS0_INT_MASK;
 156
 157        data = PM805_STATUS0_INT_CLEAR;
 158        ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
 159        /*
 160         * PM805_INT_STATUS is under 32K clock domain, so need to
 161         * add proper delay before the next I2C register access.
 162         */
 163        msleep(1);
 164
 165        if (ret < 0)
 166                goto out;
 167
 168        ret =
 169            regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
 170                                chip->regmap_irq_chip, &chip->irq_data);
 171
 172out:
 173        return ret;
 174}
 175
 176static void device_irq_exit_805(struct pm80x_chip *chip)
 177{
 178        regmap_del_irq_chip(chip->irq, chip->irq_data);
 179}
 180
 181static struct regmap_irq_chip pm805_irq_chip = {
 182        .name = "88pm805",
 183        .irqs = pm805_irqs,
 184        .num_irqs = ARRAY_SIZE(pm805_irqs),
 185
 186        .num_regs = 2,
 187        .status_base = PM805_INT_STATUS1,
 188        .mask_base = PM805_INT_MASK1,
 189        .ack_base = PM805_INT_STATUS1,
 190};
 191
 192static int device_805_init(struct pm80x_chip *chip)
 193{
 194        int ret = 0;
 195        unsigned int val;
 196        struct regmap *map = chip->regmap;
 197
 198        if (!map) {
 199                dev_err(chip->dev, "regmap is invalid\n");
 200                return -EINVAL;
 201        }
 202
 203        ret = regmap_read(map, PM805_CHIP_ID, &val);
 204        if (ret < 0) {
 205                dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
 206                goto out_irq_init;
 207        }
 208        chip->version = val;
 209
 210        chip->regmap_irq_chip = &pm805_irq_chip;
 211
 212        ret = device_irq_init_805(chip);
 213        if (ret < 0) {
 214                dev_err(chip->dev, "Failed to init pm805 irq!\n");
 215                goto out_irq_init;
 216        }
 217
 218        ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
 219                              ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
 220                              NULL);
 221        if (ret < 0) {
 222                dev_err(chip->dev, "Failed to add codec subdev\n");
 223                goto out_codec;
 224        } else
 225                dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
 226
 227        return 0;
 228
 229out_codec:
 230        device_irq_exit_805(chip);
 231out_irq_init:
 232        return ret;
 233}
 234
 235static int pm805_probe(struct i2c_client *client,
 236                                 const struct i2c_device_id *id)
 237{
 238        int ret = 0;
 239        struct pm80x_chip *chip;
 240        struct pm80x_platform_data *pdata = client->dev.platform_data;
 241
 242        ret = pm80x_init(client, id);
 243        if (ret) {
 244                dev_err(&client->dev, "pm805_init fail!\n");
 245                goto out_init;
 246        }
 247
 248        chip = i2c_get_clientdata(client);
 249
 250        ret = device_805_init(chip);
 251        if (ret) {
 252                dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
 253                goto err_805_init;
 254        }
 255
 256        if (pdata->plat_config)
 257                pdata->plat_config(chip, pdata);
 258
 259err_805_init:
 260        pm80x_deinit(client);
 261out_init:
 262        return ret;
 263}
 264
 265static int pm805_remove(struct i2c_client *client)
 266{
 267        struct pm80x_chip *chip = i2c_get_clientdata(client);
 268
 269        mfd_remove_devices(chip->dev);
 270        device_irq_exit_805(chip);
 271
 272        pm80x_deinit(client);
 273
 274        return 0;
 275}
 276
 277static struct i2c_driver pm805_driver = {
 278        .driver = {
 279                .name = "88PM80X",
 280                .owner = THIS_MODULE,
 281                .pm = &pm80x_pm_ops,
 282                },
 283        .probe = pm805_probe,
 284        .remove = pm805_remove,
 285        .id_table = pm80x_id_table,
 286};
 287
 288static int __init pm805_i2c_init(void)
 289{
 290        return i2c_add_driver(&pm805_driver);
 291}
 292subsys_initcall(pm805_i2c_init);
 293
 294static void __exit pm805_i2c_exit(void)
 295{
 296        i2c_del_driver(&pm805_driver);
 297}
 298module_exit(pm805_i2c_exit);
 299
 300MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
 301MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
 302MODULE_LICENSE("GPL");
 303
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.