Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Base driver for Marvell 88PM805
0003  *
0004  * Copyright (C) 2012 Marvell International Ltd.
0005  * Haojian Zhuang <haojian.zhuang@marvell.com>
0006  * Joseph(Yossi) Hanin <yhanin@marvell.com>
0007  * Qiao Zhou <zhouqiao@marvell.com>
0008  *
0009  * This file is subject to the terms and conditions of the GNU General
0010  * Public License. See the file "COPYING" in the main directory of this
0011  * archive for more details.
0012  *
0013  * This program is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016  * GNU General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU General Public License
0019  * along with this program; if not, write to the Free Software
0020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0021  */
0022 
0023 #include <linux/kernel.h>
0024 #include <linux/module.h>
0025 #include <linux/i2c.h>
0026 #include <linux/irq.h>
0027 #include <linux/mfd/core.h>
0028 #include <linux/mfd/88pm80x.h>
0029 #include <linux/slab.h>
0030 #include <linux/delay.h>
0031 
0032 static const struct i2c_device_id pm80x_id_table[] = {
0033     {"88PM805", 0},
0034     {} /* NULL terminated */
0035 };
0036 MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
0037 
0038 /* Interrupt Number in 88PM805 */
0039 enum {
0040     PM805_IRQ_LDO_OFF,  /*0 */
0041     PM805_IRQ_SRC_DPLL_LOCK,    /*1 */
0042     PM805_IRQ_CLIP_FAULT,
0043     PM805_IRQ_MIC_CONFLICT,
0044     PM805_IRQ_HP2_SHRT,
0045     PM805_IRQ_HP1_SHRT, /*5 */
0046     PM805_IRQ_FINE_PLL_FAULT,
0047     PM805_IRQ_RAW_PLL_FAULT,
0048     PM805_IRQ_VOLP_BTN_DET,
0049     PM805_IRQ_VOLM_BTN_DET,
0050     PM805_IRQ_SHRT_BTN_DET, /*10 */
0051     PM805_IRQ_MIC_DET,  /*11 */
0052 
0053     PM805_MAX_IRQ,
0054 };
0055 
0056 static struct resource codec_resources[] = {
0057     /* Headset microphone insertion or removal */
0058     DEFINE_RES_IRQ_NAMED(PM805_IRQ_MIC_DET, "micin"),
0059 
0060     /* Audio short HP1 */
0061     DEFINE_RES_IRQ_NAMED(PM805_IRQ_HP1_SHRT, "audio-short1"),
0062 
0063     /* Audio short HP2 */
0064     DEFINE_RES_IRQ_NAMED(PM805_IRQ_HP2_SHRT, "audio-short2"),
0065 };
0066 
0067 static const struct mfd_cell codec_devs[] = {
0068     {
0069      .name = "88pm80x-codec",
0070      .num_resources = ARRAY_SIZE(codec_resources),
0071      .resources = &codec_resources[0],
0072      .id = -1,
0073      },
0074 };
0075 
0076 static struct regmap_irq pm805_irqs[] = {
0077     /* INT0 */
0078     [PM805_IRQ_LDO_OFF] = {
0079         .mask = PM805_INT1_HP1_SHRT,
0080     },
0081     [PM805_IRQ_SRC_DPLL_LOCK] = {
0082         .mask = PM805_INT1_HP2_SHRT,
0083     },
0084     [PM805_IRQ_CLIP_FAULT] = {
0085         .mask = PM805_INT1_MIC_CONFLICT,
0086     },
0087     [PM805_IRQ_MIC_CONFLICT] = {
0088         .mask = PM805_INT1_CLIP_FAULT,
0089     },
0090     [PM805_IRQ_HP2_SHRT] = {
0091         .mask = PM805_INT1_LDO_OFF,
0092     },
0093     [PM805_IRQ_HP1_SHRT] = {
0094         .mask = PM805_INT1_SRC_DPLL_LOCK,
0095     },
0096     /* INT1 */
0097     [PM805_IRQ_FINE_PLL_FAULT] = {
0098         .reg_offset = 1,
0099         .mask = PM805_INT2_MIC_DET,
0100     },
0101     [PM805_IRQ_RAW_PLL_FAULT] = {
0102         .reg_offset = 1,
0103         .mask = PM805_INT2_SHRT_BTN_DET,
0104     },
0105     [PM805_IRQ_VOLP_BTN_DET] = {
0106         .reg_offset = 1,
0107         .mask = PM805_INT2_VOLM_BTN_DET,
0108     },
0109     [PM805_IRQ_VOLM_BTN_DET] = {
0110         .reg_offset = 1,
0111         .mask = PM805_INT2_VOLP_BTN_DET,
0112     },
0113     [PM805_IRQ_SHRT_BTN_DET] = {
0114         .reg_offset = 1,
0115         .mask = PM805_INT2_RAW_PLL_FAULT,
0116     },
0117     [PM805_IRQ_MIC_DET] = {
0118         .reg_offset = 1,
0119         .mask = PM805_INT2_FINE_PLL_FAULT,
0120     },
0121 };
0122 
0123 static int device_irq_init_805(struct pm80x_chip *chip)
0124 {
0125     struct regmap *map = chip->regmap;
0126     unsigned long flags = IRQF_ONESHOT;
0127     int data, mask, ret = -EINVAL;
0128 
0129     if (!map || !chip->irq) {
0130         dev_err(chip->dev, "incorrect parameters\n");
0131         return -EINVAL;
0132     }
0133 
0134     /*
0135      * irq_mode defines the way of clearing interrupt. it's read-clear by
0136      * default.
0137      */
0138     mask =
0139         PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
0140         PM800_STATUS0_INT_MASK;
0141 
0142     data = PM805_STATUS0_INT_CLEAR;
0143     ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
0144     /*
0145      * PM805_INT_STATUS is under 32K clock domain, so need to
0146      * add proper delay before the next I2C register access.
0147      */
0148     usleep_range(1000, 3000);
0149 
0150     if (ret < 0)
0151         goto out;
0152 
0153     ret =
0154         regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
0155                 chip->regmap_irq_chip, &chip->irq_data);
0156 
0157 out:
0158     return ret;
0159 }
0160 
0161 static void device_irq_exit_805(struct pm80x_chip *chip)
0162 {
0163     regmap_del_irq_chip(chip->irq, chip->irq_data);
0164 }
0165 
0166 static struct regmap_irq_chip pm805_irq_chip = {
0167     .name = "88pm805",
0168     .irqs = pm805_irqs,
0169     .num_irqs = ARRAY_SIZE(pm805_irqs),
0170 
0171     .num_regs = 2,
0172     .status_base = PM805_INT_STATUS1,
0173     .mask_base = PM805_INT_MASK1,
0174     .ack_base = PM805_INT_STATUS1,
0175 };
0176 
0177 static int device_805_init(struct pm80x_chip *chip)
0178 {
0179     int ret = 0;
0180     struct regmap *map = chip->regmap;
0181 
0182     if (!map) {
0183         dev_err(chip->dev, "regmap is invalid\n");
0184         return -EINVAL;
0185     }
0186 
0187     chip->regmap_irq_chip = &pm805_irq_chip;
0188 
0189     ret = device_irq_init_805(chip);
0190     if (ret < 0) {
0191         dev_err(chip->dev, "Failed to init pm805 irq!\n");
0192         goto out_irq_init;
0193     }
0194 
0195     ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
0196                   ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
0197                   NULL);
0198     if (ret < 0) {
0199         dev_err(chip->dev, "Failed to add codec subdev\n");
0200         goto out_codec;
0201     } else
0202         dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
0203 
0204     return 0;
0205 
0206 out_codec:
0207     device_irq_exit_805(chip);
0208 out_irq_init:
0209     return ret;
0210 }
0211 
0212 static int pm805_probe(struct i2c_client *client,
0213                  const struct i2c_device_id *id)
0214 {
0215     int ret = 0;
0216     struct pm80x_chip *chip;
0217     struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev);
0218 
0219     ret = pm80x_init(client);
0220     if (ret) {
0221         dev_err(&client->dev, "pm805_init fail!\n");
0222         goto out_init;
0223     }
0224 
0225     chip = i2c_get_clientdata(client);
0226 
0227     ret = device_805_init(chip);
0228     if (ret) {
0229         dev_err(chip->dev, "Failed to initialize 88pm805 devices\n");
0230         goto err_805_init;
0231     }
0232 
0233     if (pdata && pdata->plat_config)
0234         pdata->plat_config(chip, pdata);
0235 
0236 err_805_init:
0237     pm80x_deinit();
0238 out_init:
0239     return ret;
0240 }
0241 
0242 static int pm805_remove(struct i2c_client *client)
0243 {
0244     struct pm80x_chip *chip = i2c_get_clientdata(client);
0245 
0246     mfd_remove_devices(chip->dev);
0247     device_irq_exit_805(chip);
0248 
0249     pm80x_deinit();
0250 
0251     return 0;
0252 }
0253 
0254 static struct i2c_driver pm805_driver = {
0255     .driver = {
0256         .name = "88PM805",
0257         .pm = &pm80x_pm_ops,
0258         },
0259     .probe = pm805_probe,
0260     .remove = pm805_remove,
0261     .id_table = pm80x_id_table,
0262 };
0263 
0264 static int __init pm805_i2c_init(void)
0265 {
0266     return i2c_add_driver(&pm805_driver);
0267 }
0268 subsys_initcall(pm805_i2c_init);
0269 
0270 static void __exit pm805_i2c_exit(void)
0271 {
0272     i2c_del_driver(&pm805_driver);
0273 }
0274 module_exit(pm805_i2c_exit);
0275 
0276 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
0277 MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
0278 MODULE_LICENSE("GPL");