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 struct resource cht_wc_pwrsrc_resources[] = { 45 DEFINE_RES_IRQ(CHT_WC_PWRSRC_IRQ), 46 }; 47 48 static 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 }; 64 65 /* 66 * The CHT Whiskey Cove covers multiple I2C addresses, with a 1 Byte 67 * register address space per I2C address, so we use 16 bit register 68 * addresses where the high 8 bits contain the I2C client address. 69 */ 70 static int cht_wc_byte_reg_read(void *context, unsigned int reg, 71 unsigned int *val) 72 { 73 struct i2c_client *client = context; 74 int ret, orig_addr = client->addr; 75 76 if (!(reg & REG_ADDR_MASK)) { 77 dev_err(&client->dev, "Error I2C address not specified\n"); 78 return -EINVAL; 79 } 80 81 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 82 ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK); 83 client->addr = orig_addr; 84 85 if (ret < 0) 86 return ret; 87 88 *val = ret; 89 return 0; 90 } 91 92 static int cht_wc_byte_reg_write(void *context, unsigned int reg, 93 unsigned int val) 94 { 95 struct i2c_client *client = context; 96 int ret, orig_addr = client->addr; 97 98 if (!(reg & REG_ADDR_MASK)) { 99 dev_err(&client->dev, "Error I2C address not specified\n"); 100 return -EINVAL; 101 } 102 103 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 104 ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val); 105 client->addr = orig_addr; 106 107 return ret; 108 } 109 110 static const struct regmap_config cht_wc_regmap_cfg = { 111 .reg_bits = 16, 112 .val_bits = 8, 113 .reg_write = cht_wc_byte_reg_write, 114 .reg_read = cht_wc_byte_reg_read, 115 }; 116 117 static const struct regmap_irq cht_wc_regmap_irqs[] = { 118 REGMAP_IRQ_REG(CHT_WC_PWRSRC_IRQ, 0, BIT(CHT_WC_PWRSRC_IRQ)), 119 REGMAP_IRQ_REG(CHT_WC_THRM_IRQ, 0, BIT(CHT_WC_THRM_IRQ)), 120 REGMAP_IRQ_REG(CHT_WC_BCU_IRQ, 0, BIT(CHT_WC_BCU_IRQ)), 121 REGMAP_IRQ_REG(CHT_WC_ADC_IRQ, 0, BIT(CHT_WC_ADC_IRQ)), 122 REGMAP_IRQ_REG(CHT_WC_EXT_CHGR_IRQ, 0, BIT(CHT_WC_EXT_CHGR_IRQ)), 123 REGMAP_IRQ_REG(CHT_WC_GPIO_IRQ, 0, BIT(CHT_WC_GPIO_IRQ)), 124 REGMAP_IRQ_REG(CHT_WC_CRIT_IRQ, 0, BIT(CHT_WC_CRIT_IRQ)), 125 }; 126 127 static const struct regmap_irq_chip cht_wc_regmap_irq_chip = { 128 .name = "cht_wc_irq_chip", 129 .status_base = CHT_WC_IRQLVL1, 130 .mask_base = CHT_WC_IRQLVL1_MASK, 131 .irqs = cht_wc_regmap_irqs, 132 .num_irqs = ARRAY_SIZE(cht_wc_regmap_irqs), 133 .num_regs = 1, 134 }; 135 136 static int cht_wc_probe(struct i2c_client *client) 137 { 138 struct device *dev = &client->dev; 139 struct intel_soc_pmic *pmic; 140 acpi_status status; 141 unsigned long long hrv; 142 int ret; 143 144 status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); 145 if (ACPI_FAILURE(status)) { 146 dev_err(dev, "Failed to get PMIC hardware revision\n"); 147 return -ENODEV; 148 } 149 if (hrv != CHT_WC_HRV) { 150 dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv); 151 return -ENODEV; 152 } 153 if (client->irq < 0) { 154 dev_err(dev, "Invalid IRQ\n"); 155 return -EINVAL; 156 } 157 158 pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 159 if (!pmic) 160 return -ENOMEM; 161 162 pmic->irq = client->irq; 163 pmic->dev = dev; 164 i2c_set_clientdata(client, pmic); 165 166 pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg); 167 if (IS_ERR(pmic->regmap)) 168 return PTR_ERR(pmic->regmap); 169 170 ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, 171 IRQF_ONESHOT | IRQF_SHARED, 0, 172 &cht_wc_regmap_irq_chip, 173 &pmic->irq_chip_data); 174 if (ret) 175 return ret; 176 177 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 178 cht_wc_dev, ARRAY_SIZE(cht_wc_dev), NULL, 0, 179 regmap_irq_get_domain(pmic->irq_chip_data)); 180 } 181 182 static void cht_wc_shutdown(struct i2c_client *client) 183 { 184 struct intel_soc_pmic *pmic = i2c_get_clientdata(client); 185 186 disable_irq(pmic->irq); 187 } 188 189 static int __maybe_unused cht_wc_suspend(struct device *dev) 190 { 191 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 192 193 disable_irq(pmic->irq); 194 195 return 0; 196 } 197 198 static int __maybe_unused cht_wc_resume(struct device *dev) 199 { 200 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 201 202 enable_irq(pmic->irq); 203 204 return 0; 205 } 206 static SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); 207 208 static const struct i2c_device_id cht_wc_i2c_id[] = { 209 { } 210 }; 211 212 static const struct acpi_device_id cht_wc_acpi_ids[] = { 213 { "INT34D3", }, 214 { } 215 }; 216 217 static struct i2c_driver cht_wc_driver = { 218 .driver = { 219 .name = "CHT Whiskey Cove PMIC", 220 .pm = &cht_wc_pm_ops, 221 .acpi_match_table = cht_wc_acpi_ids, 222 }, 223 .probe_new = cht_wc_probe, 224 .shutdown = cht_wc_shutdown, 225 .id_table = cht_wc_i2c_id, 226 }; 227 builtin_i2c_driver(cht_wc_driver); 228