intel_soc_pmic_chtwc.c (7162B)
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/dmi.h> 14#include <linux/err.h> 15#include <linux/i2c.h> 16#include <linux/interrupt.h> 17#include <linux/kernel.h> 18#include <linux/mfd/core.h> 19#include <linux/mfd/intel_soc_pmic.h> 20#include <linux/regmap.h> 21 22/* PMIC device registers */ 23#define REG_OFFSET_MASK GENMASK(7, 0) 24#define REG_ADDR_MASK GENMASK(15, 8) 25#define REG_ADDR_SHIFT 8 26 27#define CHT_WC_IRQLVL1 0x6e02 28#define CHT_WC_IRQLVL1_MASK 0x6e0e 29 30/* Whiskey Cove PMIC share same ACPI ID between different platforms */ 31#define CHT_WC_HRV 3 32 33/* Level 1 IRQs (level 2 IRQs are handled in the child device drivers) */ 34enum { 35 CHT_WC_PWRSRC_IRQ = 0, 36 CHT_WC_THRM_IRQ, 37 CHT_WC_BCU_IRQ, 38 CHT_WC_ADC_IRQ, 39 CHT_WC_EXT_CHGR_IRQ, 40 CHT_WC_GPIO_IRQ, 41 /* There is no irq 6 */ 42 CHT_WC_CRIT_IRQ = 7, 43}; 44 45static const struct resource cht_wc_pwrsrc_resources[] = { 46 DEFINE_RES_IRQ(CHT_WC_PWRSRC_IRQ), 47}; 48 49static const struct resource cht_wc_ext_charger_resources[] = { 50 DEFINE_RES_IRQ(CHT_WC_EXT_CHGR_IRQ), 51}; 52 53static struct mfd_cell cht_wc_dev[] = { 54 { 55 .name = "cht_wcove_pwrsrc", 56 .num_resources = ARRAY_SIZE(cht_wc_pwrsrc_resources), 57 .resources = cht_wc_pwrsrc_resources, 58 }, { 59 .name = "cht_wcove_ext_chgr", 60 .num_resources = ARRAY_SIZE(cht_wc_ext_charger_resources), 61 .resources = cht_wc_ext_charger_resources, 62 }, 63 { .name = "cht_wcove_region", }, 64 { .name = "cht_wcove_leds", }, 65}; 66 67/* 68 * The CHT Whiskey Cove covers multiple I2C addresses, with a 1 Byte 69 * register address space per I2C address, so we use 16 bit register 70 * addresses where the high 8 bits contain the I2C client address. 71 */ 72static int cht_wc_byte_reg_read(void *context, unsigned int reg, 73 unsigned int *val) 74{ 75 struct i2c_client *client = context; 76 int ret, orig_addr = client->addr; 77 78 if (!(reg & REG_ADDR_MASK)) { 79 dev_err(&client->dev, "Error I2C address not specified\n"); 80 return -EINVAL; 81 } 82 83 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 84 ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK); 85 client->addr = orig_addr; 86 87 if (ret < 0) 88 return ret; 89 90 *val = ret; 91 return 0; 92} 93 94static int cht_wc_byte_reg_write(void *context, unsigned int reg, 95 unsigned int val) 96{ 97 struct i2c_client *client = context; 98 int ret, orig_addr = client->addr; 99 100 if (!(reg & REG_ADDR_MASK)) { 101 dev_err(&client->dev, "Error I2C address not specified\n"); 102 return -EINVAL; 103 } 104 105 client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 106 ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val); 107 client->addr = orig_addr; 108 109 return ret; 110} 111 112static const struct regmap_config cht_wc_regmap_cfg = { 113 .reg_bits = 16, 114 .val_bits = 8, 115 .reg_write = cht_wc_byte_reg_write, 116 .reg_read = cht_wc_byte_reg_read, 117}; 118 119static const struct regmap_irq cht_wc_regmap_irqs[] = { 120 REGMAP_IRQ_REG(CHT_WC_PWRSRC_IRQ, 0, BIT(CHT_WC_PWRSRC_IRQ)), 121 REGMAP_IRQ_REG(CHT_WC_THRM_IRQ, 0, BIT(CHT_WC_THRM_IRQ)), 122 REGMAP_IRQ_REG(CHT_WC_BCU_IRQ, 0, BIT(CHT_WC_BCU_IRQ)), 123 REGMAP_IRQ_REG(CHT_WC_ADC_IRQ, 0, BIT(CHT_WC_ADC_IRQ)), 124 REGMAP_IRQ_REG(CHT_WC_EXT_CHGR_IRQ, 0, BIT(CHT_WC_EXT_CHGR_IRQ)), 125 REGMAP_IRQ_REG(CHT_WC_GPIO_IRQ, 0, BIT(CHT_WC_GPIO_IRQ)), 126 REGMAP_IRQ_REG(CHT_WC_CRIT_IRQ, 0, BIT(CHT_WC_CRIT_IRQ)), 127}; 128 129static const struct regmap_irq_chip cht_wc_regmap_irq_chip = { 130 .name = "cht_wc_irq_chip", 131 .status_base = CHT_WC_IRQLVL1, 132 .mask_base = CHT_WC_IRQLVL1_MASK, 133 .irqs = cht_wc_regmap_irqs, 134 .num_irqs = ARRAY_SIZE(cht_wc_regmap_irqs), 135 .num_regs = 1, 136}; 137 138static const struct dmi_system_id cht_wc_model_dmi_ids[] = { 139 { 140 /* GPD win / GPD pocket mini laptops */ 141 .driver_data = (void *)(long)INTEL_CHT_WC_GPD_WIN_POCKET, 142 /* 143 * This DMI match may not seem unique, but it is. In the 67000+ 144 * DMI decode dumps from linux-hardware.org only 116 have 145 * board_vendor set to "AMI Corporation" and of those 116 only 146 * the GPD win's and pocket's board_name is "Default string". 147 */ 148 .matches = { 149 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 150 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), 151 DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), 152 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), 153 }, 154 }, { 155 /* Xiaomi Mi Pad 2 */ 156 .driver_data = (void *)(long)INTEL_CHT_WC_XIAOMI_MIPAD2, 157 .matches = { 158 DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), 159 DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), 160 }, 161 }, { 162 /* Lenovo Yoga Book X90F / X91F / X91L */ 163 .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1, 164 .matches = { 165 /* Non exact match to match all versions */ 166 DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), 167 }, 168 }, 169 { } 170}; 171 172static int cht_wc_probe(struct i2c_client *client) 173{ 174 struct device *dev = &client->dev; 175 const struct dmi_system_id *id; 176 struct intel_soc_pmic *pmic; 177 acpi_status status; 178 unsigned long long hrv; 179 int ret; 180 181 status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); 182 if (ACPI_FAILURE(status)) { 183 dev_err(dev, "Failed to get PMIC hardware revision\n"); 184 return -ENODEV; 185 } 186 if (hrv != CHT_WC_HRV) { 187 dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv); 188 return -ENODEV; 189 } 190 if (client->irq < 0) { 191 dev_err(dev, "Invalid IRQ\n"); 192 return -EINVAL; 193 } 194 195 pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 196 if (!pmic) 197 return -ENOMEM; 198 199 id = dmi_first_match(cht_wc_model_dmi_ids); 200 if (id) 201 pmic->cht_wc_model = (long)id->driver_data; 202 203 pmic->irq = client->irq; 204 pmic->dev = dev; 205 i2c_set_clientdata(client, pmic); 206 207 pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg); 208 if (IS_ERR(pmic->regmap)) 209 return PTR_ERR(pmic->regmap); 210 211 ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, 212 IRQF_ONESHOT | IRQF_SHARED, 0, 213 &cht_wc_regmap_irq_chip, 214 &pmic->irq_chip_data); 215 if (ret) 216 return ret; 217 218 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 219 cht_wc_dev, ARRAY_SIZE(cht_wc_dev), NULL, 0, 220 regmap_irq_get_domain(pmic->irq_chip_data)); 221} 222 223static void cht_wc_shutdown(struct i2c_client *client) 224{ 225 struct intel_soc_pmic *pmic = i2c_get_clientdata(client); 226 227 disable_irq(pmic->irq); 228} 229 230static int __maybe_unused cht_wc_suspend(struct device *dev) 231{ 232 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 233 234 disable_irq(pmic->irq); 235 236 return 0; 237} 238 239static int __maybe_unused cht_wc_resume(struct device *dev) 240{ 241 struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 242 243 enable_irq(pmic->irq); 244 245 return 0; 246} 247static SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); 248 249static const struct i2c_device_id cht_wc_i2c_id[] = { 250 { } 251}; 252 253static const struct acpi_device_id cht_wc_acpi_ids[] = { 254 { "INT34D3", }, 255 { } 256}; 257 258static struct i2c_driver cht_wc_driver = { 259 .driver = { 260 .name = "CHT Whiskey Cove PMIC", 261 .pm = &cht_wc_pm_ops, 262 .acpi_match_table = cht_wc_acpi_ids, 263 }, 264 .probe_new = cht_wc_probe, 265 .shutdown = cht_wc_shutdown, 266 .id_table = cht_wc_i2c_id, 267}; 268builtin_i2c_driver(cht_wc_driver);