1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MFD core driver for Intel Cherrytrail Whiskey Cove PMIC 4 * 5 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 6 * 7 * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: 8 * Copyright (C) 2013-2015 Intel Corporation. All rights reserved. 9 */ 10 11 #include <linux/acpi.h> 12 #include <linux/delay.h> 13 #include <linux/err.h> 14 #include <linux/i2c.h> 15 #include <linux/interrupt.h> 16 #include <linux/kernel.h> 17 #include <linux/mfd/core.h> 18 #include <linux/mfd/intel_soc_pmic.h> 19 #include <linux/regmap.h> 20 21 /* PMIC device registers */ 22 #define REG_OFFSET_MASK GENMASK(7, 0) 23 #define REG_ADDR_MASK GENMASK(15, 8) 24 #define REG_ADDR_SHIFT 8 25 26 #define CHT_WC_IRQLVL1 0x6e02 27 #define CHT_WC_IRQLVL1_MASK 0x6e0e 28 29 /* Whiskey Cove PMIC share same ACPI ID between different platforms */ 30 #define CHT_WC_HRV 3 31 32 /* Level 1 IRQs (level 2 IRQs are handled in the child device drivers) */ 33 enum { 34 CHT_WC_PWRSRC_IRQ = 0, 35 CHT_WC_THRM_IRQ, 36 CHT_WC_BCU_IRQ, 37 CHT_WC_ADC_IRQ, 38 CHT_WC_EXT_CHGR_IRQ, 39 CHT_WC_GPIO_IRQ, 40 /* There is no irq 6 */ 41 CHT_WC_CRIT_IRQ = 7, 42 }; 43 44 static const struct resource cht_wc_pwrsrc_resources[] = { 45 DEFINE_RES_IRQ(CHT_WC_PWRSRC_IRQ), 46 }; 47 48 static const struct resource cht_wc_ext_charger_resources[] = { 49 DEFINE_RES_IRQ(CHT_WC_EXT_CHGR_IRQ), 50 }; 51 52 static struct mfd_cell cht_wc_dev[] = { 53 { 54 .name = "cht_wcove_pwrsrc", 55 .num_resources = ARRAY_SIZE(cht_wc_pwrsrc_resources), 56 .resources = cht_wc_pwrsrc_resources, 57 }, { 58 .name = "cht_wcove_ext_chgr", 59 .num_resources = ARRAY_SIZE(cht_wc_ext_charger_resources), 60 .resources = cht_wc_ext_charger_resources, 61 }, 62 { .name = "cht_wcove_region", }, 63 { .name = "cht_wcove_leds", }, 64 }; 65 66 /* 67 * The CHT Whiskey Cove covers multiple I2C addresses, with a 1 Byte 68 * register address space per I2C address, so we use 16 bit register 69 * addresses where the high 8 bits contain the I2C client address. 70 */ 71 static int cht_wc_byte_reg_read(void *context, unsigned int reg, 72 unsigned int *val) 73 { 74 struct i2c_client *client = context; 75 int ret, orig_addr = client->addr; 76 77 if (!(reg & REG_ADDR_MASK)) { 78 dev_err(&client->dev, "Error I2C address not specified\n"); 79 return -EINVAL; 80 } 81 82 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 83 ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK); 84 client->addr = orig_addr; 85 86 if (ret < 0) 87 return ret; 88 89 *val = ret; 90 return 0; 91 } 92 93 static int cht_wc_byte_reg_write(void *context, unsigned int reg, 94 unsigned int val) 95 { 96 struct i2c_client *client = context; 97 int ret, orig_addr = client->addr; 98 99 if (!(reg & REG_ADDR_MASK)) { 100 dev_err(&client->dev, "Error I2C address not specified\n"); 101 return -EINVAL; 102 } 103 104 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 105 ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val); 106 client->addr = orig_addr; 107 108 return ret; 109 } 110 111 static const struct regmap_config cht_wc_regmap_cfg = { 112 .reg_bits = 16, 113 .val_bits = 8, 114 .reg_write = cht_wc_byte_reg_write, 115 .reg_read = cht_wc_byte_reg_read, 116 }; 117 118 static const struct regmap_irq cht_wc_regmap_irqs[] = { 119 REGMAP_IRQ_REG(CHT_WC_PWRSRC_IRQ, 0, BIT(CHT_WC_PWRSRC_IRQ)), 120 REGMAP_IRQ_REG(CHT_WC_THRM_IRQ, 0, BIT(CHT_WC_THRM_IRQ)), 121 REGMAP_IRQ_REG(CHT_WC_BCU_IRQ, 0, BIT(CHT_WC_BCU_IRQ)), 122 REGMAP_IRQ_REG(CHT_WC_ADC_IRQ, 0, BIT(CHT_WC_ADC_IRQ)), 123 REGMAP_IRQ_REG(CHT_WC_EXT_CHGR_IRQ, 0, BIT(CHT_WC_EXT_CHGR_IRQ)), 124 REGMAP_IRQ_REG(CHT_WC_GPIO_IRQ, 0, BIT(CHT_WC_GPIO_IRQ)), 125 REGMAP_IRQ_REG(CHT_WC_CRIT_IRQ, 0, BIT(CHT_WC_CRIT_IRQ)), 126 }; 127 128 static const struct regmap_irq_chip cht_wc_regmap_irq_chip = { 129 .name = "cht_wc_irq_chip", 130 .status_base = CHT_WC_IRQLVL1, 131 .mask_base = CHT_WC_IRQLVL1_MASK, 132 .irqs = cht_wc_regmap_irqs, 133 .num_irqs = ARRAY_SIZE(cht_wc_regmap_irqs), 134 .num_regs = 1, 135 }; 136 137 static int cht_wc_probe(struct i2c_client *client) 138 { 139 struct device *dev = &client->dev; 140 struct intel_soc_pmic *pmic; 141 acpi_status status; 142 unsigned long long hrv; 143 int ret; 144 145 status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); 146 if (ACPI_FAILURE(status)) { 147 dev_err(dev, "Failed to get PMIC hardware revision\n"); 148 return -ENODEV; 149 } 150 if (hrv != CHT_WC_HRV) { 151 dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv); 152 return -ENODEV; 153 } 154 if (client->irq < 0) { 155 dev_err(dev, "Invalid IRQ\n"); 156 return -EINVAL; 157 } 158 159 pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 160 if (!pmic) 161 return -ENOMEM; 162 163 pmic->irq = client->irq; 164 pmic->dev = dev; 165 i2c_set_clientdata(client, pmic); 166 167 pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg); 168 if (IS_ERR(pmic->regmap)) 169 return PTR_ERR(pmic->regmap); 170 171 ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, 172 IRQF_ONESHOT | IRQF_SHARED, 0, 173 &cht_wc_regmap_irq_chip, 174 &pmic->irq_chip_data); 175 if (ret) 176 return ret; 177 178 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 179 cht_wc_dev, ARRAY_SIZE(cht_wc_dev), NULL, 0, 180 regmap_irq_get_domain(pmic->irq_chip_data)); 181 } 182 183 static void cht_wc_shutdown(struct i2c_client *client) 184 { 185 struct intel_soc_pmic *pmic = i2c_get_clientdata(client); 186 187 disable_irq(pmic->irq); 188 } 189 190 static int __maybe_unused cht_wc_suspend(struct device *dev) 191 { 192 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 193 194 disable_irq(pmic->irq); 195 196 return 0; 197 } 198 199 static int __maybe_unused cht_wc_resume(struct device *dev) 200 { 201 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 202 203 enable_irq(pmic->irq); 204 205 return 0; 206 } 207 static SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); 208 209 static const struct i2c_device_id cht_wc_i2c_id[] = { 210 { } 211 }; 212 213 static const struct acpi_device_id cht_wc_acpi_ids[] = { 214 { "INT34D3", }, 215 { } 216 }; 217 218 static struct i2c_driver cht_wc_driver = { 219 .driver = { 220 .name = "CHT Whiskey Cove PMIC", 221 .pm = &cht_wc_pm_ops, 222 .acpi_match_table = cht_wc_acpi_ids, 223 }, 224 .probe_new = cht_wc_probe, 225 .shutdown = cht_wc_shutdown, 226 .id_table = cht_wc_i2c_id, 227 }; 228 builtin_i2c_driver(cht_wc_driver); 229