^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * I2C driver for Marvell 88PM80x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2012 Marvell International Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Haojian Zhuang <haojian.zhuang@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Joseph(Yossi) Hanin <yhanin@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Qiao Zhou <zhouqiao@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/i2c.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/mfd/88pm80x.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) /* 88pm80x chips have same definition for chip id register. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define PM80X_CHIP_ID (0x00)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define PM80X_CHIP_ID_NUM(x) (((x) >> 5) & 0x7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define PM80X_CHIP_ID_REVISION(x) ((x) & 0x1F)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct pm80x_chip_mapping {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) unsigned int id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) int type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static struct pm80x_chip_mapping chip_mapping[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) /* 88PM800 chip id number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) {0x3, CHIP_PM800},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* 88PM805 chip id number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) {0x0, CHIP_PM805},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /* 88PM860 chip id number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) {0x4, CHIP_PM860},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * workaround: some registers needed by pm805 are defined in pm800, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * need to use this global variable to maintain the relation between
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * pm800 and pm805. would remove it after HW chip fixes the issue.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static struct pm80x_chip *g_pm80x_chip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) const struct regmap_config pm80x_regmap_config = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) .reg_bits = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) .val_bits = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) EXPORT_SYMBOL_GPL(pm80x_regmap_config);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) int pm80x_init(struct i2c_client *client)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) struct pm80x_chip *chip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) struct regmap *map;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) int i, ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) chip =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (!chip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (IS_ERR(map)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) ret = PTR_ERR(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) dev_err(&client->dev, "Failed to allocate register map: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) chip->client = client;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) chip->regmap = map;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) chip->irq = client->irq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) chip->dev = &client->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) dev_set_drvdata(chip->dev, chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) i2c_set_clientdata(chip->client, chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) for (i = 0; i < ARRAY_SIZE(chip_mapping); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (chip_mapping[i].id == PM80X_CHIP_ID_NUM(val)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) chip->type = chip_mapping[i].type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (i == ARRAY_SIZE(chip_mapping)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) dev_err(chip->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) device_init_wakeup(&client->dev, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) * workaround: set g_pm80x_chip to the first probed chip. if the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) * second chip is probed, just point to the companion to each
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * other so that pm805 can access those specific register. would
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) * remove it after HW chip fixes the issue.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) if (!g_pm80x_chip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) g_pm80x_chip = chip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) chip->companion = g_pm80x_chip->client;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) g_pm80x_chip->companion = chip->client;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) EXPORT_SYMBOL_GPL(pm80x_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) int pm80x_deinit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) * workaround: clear the dependency between pm800 and pm805.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) * would remove it after HW chip fixes the issue.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (g_pm80x_chip->companion)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) g_pm80x_chip->companion = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) g_pm80x_chip = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) EXPORT_SYMBOL_GPL(pm80x_deinit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) #ifdef CONFIG_PM_SLEEP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static int pm80x_suspend(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) struct i2c_client *client = to_i2c_client(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) struct pm80x_chip *chip = i2c_get_clientdata(client);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) if (chip && chip->wu_flag)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (device_may_wakeup(chip->dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) enable_irq_wake(chip->irq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static int pm80x_resume(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct i2c_client *client = to_i2c_client(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) struct pm80x_chip *chip = i2c_get_clientdata(client);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if (chip && chip->wu_flag)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) if (device_may_wakeup(chip->dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) disable_irq_wake(chip->irq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) EXPORT_SYMBOL_GPL(pm80x_pm_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) MODULE_LICENSE("GPL");