Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * I2C driver for Marvell 88PM80x
0004  *
0005  * Copyright (C) 2012 Marvell International Ltd.
0006  * Haojian Zhuang <haojian.zhuang@marvell.com>
0007  * Joseph(Yossi) Hanin <yhanin@marvell.com>
0008  * Qiao Zhou <zhouqiao@marvell.com>
0009  */
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/i2c.h>
0013 #include <linux/mfd/88pm80x.h>
0014 #include <linux/slab.h>
0015 #include <linux/uaccess.h>
0016 #include <linux/err.h>
0017 
0018 /* 88pm80x chips have same definition for chip id register. */
0019 #define PM80X_CHIP_ID           (0x00)
0020 #define PM80X_CHIP_ID_NUM(x)        (((x) >> 5) & 0x7)
0021 #define PM80X_CHIP_ID_REVISION(x)   ((x) & 0x1F)
0022 
0023 struct pm80x_chip_mapping {
0024     unsigned int    id;
0025     int     type;
0026 };
0027 
0028 static struct pm80x_chip_mapping chip_mapping[] = {
0029     /* 88PM800 chip id number */
0030     {0x3,   CHIP_PM800},
0031     /* 88PM805 chip id number */
0032     {0x0,   CHIP_PM805},
0033     /* 88PM860 chip id number */
0034     {0x4,   CHIP_PM860},
0035 };
0036 
0037 /*
0038  * workaround: some registers needed by pm805 are defined in pm800, so
0039  * need to use this global variable to maintain the relation between
0040  * pm800 and pm805. would remove it after HW chip fixes the issue.
0041  */
0042 static struct pm80x_chip *g_pm80x_chip;
0043 
0044 const struct regmap_config pm80x_regmap_config = {
0045     .reg_bits = 8,
0046     .val_bits = 8,
0047 };
0048 EXPORT_SYMBOL_GPL(pm80x_regmap_config);
0049 
0050 
0051 int pm80x_init(struct i2c_client *client)
0052 {
0053     struct pm80x_chip *chip;
0054     struct regmap *map;
0055     unsigned int val;
0056     int i, ret = 0;
0057 
0058     chip =
0059         devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
0060     if (!chip)
0061         return -ENOMEM;
0062 
0063     map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
0064     if (IS_ERR(map)) {
0065         ret = PTR_ERR(map);
0066         dev_err(&client->dev, "Failed to allocate register map: %d\n",
0067             ret);
0068         return ret;
0069     }
0070 
0071     chip->client = client;
0072     chip->regmap = map;
0073 
0074     chip->irq = client->irq;
0075 
0076     chip->dev = &client->dev;
0077     dev_set_drvdata(chip->dev, chip);
0078     i2c_set_clientdata(chip->client, chip);
0079 
0080     ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val);
0081     if (ret < 0) {
0082         dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
0083         return ret;
0084     }
0085 
0086     for (i = 0; i < ARRAY_SIZE(chip_mapping); i++) {
0087         if (chip_mapping[i].id == PM80X_CHIP_ID_NUM(val)) {
0088             chip->type = chip_mapping[i].type;
0089             break;
0090         }
0091     }
0092 
0093     if (i == ARRAY_SIZE(chip_mapping)) {
0094         dev_err(chip->dev,
0095             "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
0096         return -EINVAL;
0097     }
0098 
0099     device_init_wakeup(&client->dev, 1);
0100 
0101     /*
0102      * workaround: set g_pm80x_chip to the first probed chip. if the
0103      * second chip is probed, just point to the companion to each
0104      * other so that pm805 can access those specific register. would
0105      * remove it after HW chip fixes the issue.
0106      */
0107     if (!g_pm80x_chip)
0108         g_pm80x_chip = chip;
0109     else {
0110         chip->companion = g_pm80x_chip->client;
0111         g_pm80x_chip->companion = chip->client;
0112     }
0113 
0114     return 0;
0115 }
0116 EXPORT_SYMBOL_GPL(pm80x_init);
0117 
0118 int pm80x_deinit(void)
0119 {
0120     /*
0121      * workaround: clear the dependency between pm800 and pm805.
0122      * would remove it after HW chip fixes the issue.
0123      */
0124     if (g_pm80x_chip->companion)
0125         g_pm80x_chip->companion = NULL;
0126     else
0127         g_pm80x_chip = NULL;
0128     return 0;
0129 }
0130 EXPORT_SYMBOL_GPL(pm80x_deinit);
0131 
0132 #ifdef CONFIG_PM_SLEEP
0133 static int pm80x_suspend(struct device *dev)
0134 {
0135     struct i2c_client *client = to_i2c_client(dev);
0136     struct pm80x_chip *chip = i2c_get_clientdata(client);
0137 
0138     if (chip && chip->wu_flag)
0139         if (device_may_wakeup(chip->dev))
0140             enable_irq_wake(chip->irq);
0141 
0142     return 0;
0143 }
0144 
0145 static int pm80x_resume(struct device *dev)
0146 {
0147     struct i2c_client *client = to_i2c_client(dev);
0148     struct pm80x_chip *chip = i2c_get_clientdata(client);
0149 
0150     if (chip && chip->wu_flag)
0151         if (device_may_wakeup(chip->dev))
0152             disable_irq_wake(chip->irq);
0153 
0154     return 0;
0155 }
0156 #endif
0157 
0158 SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
0159 EXPORT_SYMBOL_GPL(pm80x_pm_ops);
0160 
0161 MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
0162 MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
0163 MODULE_LICENSE("GPL");