18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MFD core driver for Intel Cherrytrail Whiskey Cove PMIC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: 88c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Intel Corporation. All rights reserved. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* PMIC device registers */ 228c2ecf20Sopenharmony_ci#define REG_OFFSET_MASK GENMASK(7, 0) 238c2ecf20Sopenharmony_ci#define REG_ADDR_MASK GENMASK(15, 8) 248c2ecf20Sopenharmony_ci#define REG_ADDR_SHIFT 8 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define CHT_WC_IRQLVL1 0x6e02 278c2ecf20Sopenharmony_ci#define CHT_WC_IRQLVL1_MASK 0x6e0e 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Whiskey Cove PMIC share same ACPI ID between different platforms */ 308c2ecf20Sopenharmony_ci#define CHT_WC_HRV 3 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Level 1 IRQs (level 2 IRQs are handled in the child device drivers) */ 338c2ecf20Sopenharmony_cienum { 348c2ecf20Sopenharmony_ci CHT_WC_PWRSRC_IRQ = 0, 358c2ecf20Sopenharmony_ci CHT_WC_THRM_IRQ, 368c2ecf20Sopenharmony_ci CHT_WC_BCU_IRQ, 378c2ecf20Sopenharmony_ci CHT_WC_ADC_IRQ, 388c2ecf20Sopenharmony_ci CHT_WC_EXT_CHGR_IRQ, 398c2ecf20Sopenharmony_ci CHT_WC_GPIO_IRQ, 408c2ecf20Sopenharmony_ci /* There is no irq 6 */ 418c2ecf20Sopenharmony_ci CHT_WC_CRIT_IRQ = 7, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic struct resource cht_wc_pwrsrc_resources[] = { 458c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(CHT_WC_PWRSRC_IRQ), 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct resource cht_wc_ext_charger_resources[] = { 498c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(CHT_WC_EXT_CHGR_IRQ), 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct mfd_cell cht_wc_dev[] = { 538c2ecf20Sopenharmony_ci { 548c2ecf20Sopenharmony_ci .name = "cht_wcove_pwrsrc", 558c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(cht_wc_pwrsrc_resources), 568c2ecf20Sopenharmony_ci .resources = cht_wc_pwrsrc_resources, 578c2ecf20Sopenharmony_ci }, { 588c2ecf20Sopenharmony_ci .name = "cht_wcove_ext_chgr", 598c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(cht_wc_ext_charger_resources), 608c2ecf20Sopenharmony_ci .resources = cht_wc_ext_charger_resources, 618c2ecf20Sopenharmony_ci }, 628c2ecf20Sopenharmony_ci { .name = "cht_wcove_region", }, 638c2ecf20Sopenharmony_ci { .name = "cht_wcove_leds", }, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * The CHT Whiskey Cove covers multiple I2C addresses, with a 1 Byte 688c2ecf20Sopenharmony_ci * register address space per I2C address, so we use 16 bit register 698c2ecf20Sopenharmony_ci * addresses where the high 8 bits contain the I2C client address. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cistatic int cht_wc_byte_reg_read(void *context, unsigned int reg, 728c2ecf20Sopenharmony_ci unsigned int *val) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct i2c_client *client = context; 758c2ecf20Sopenharmony_ci int ret, orig_addr = client->addr; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!(reg & REG_ADDR_MASK)) { 788c2ecf20Sopenharmony_ci dev_err(&client->dev, "Error I2C address not specified\n"); 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 838c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK); 848c2ecf20Sopenharmony_ci client->addr = orig_addr; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (ret < 0) 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci *val = ret; 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int cht_wc_byte_reg_write(void *context, unsigned int reg, 948c2ecf20Sopenharmony_ci unsigned int val) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct i2c_client *client = context; 978c2ecf20Sopenharmony_ci int ret, orig_addr = client->addr; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!(reg & REG_ADDR_MASK)) { 1008c2ecf20Sopenharmony_ci dev_err(&client->dev, "Error I2C address not specified\n"); 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 1058c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val); 1068c2ecf20Sopenharmony_ci client->addr = orig_addr; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct regmap_config cht_wc_regmap_cfg = { 1128c2ecf20Sopenharmony_ci .reg_bits = 16, 1138c2ecf20Sopenharmony_ci .val_bits = 8, 1148c2ecf20Sopenharmony_ci .reg_write = cht_wc_byte_reg_write, 1158c2ecf20Sopenharmony_ci .reg_read = cht_wc_byte_reg_read, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const struct regmap_irq cht_wc_regmap_irqs[] = { 1198c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_PWRSRC_IRQ, 0, BIT(CHT_WC_PWRSRC_IRQ)), 1208c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_THRM_IRQ, 0, BIT(CHT_WC_THRM_IRQ)), 1218c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_BCU_IRQ, 0, BIT(CHT_WC_BCU_IRQ)), 1228c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_ADC_IRQ, 0, BIT(CHT_WC_ADC_IRQ)), 1238c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_EXT_CHGR_IRQ, 0, BIT(CHT_WC_EXT_CHGR_IRQ)), 1248c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_GPIO_IRQ, 0, BIT(CHT_WC_GPIO_IRQ)), 1258c2ecf20Sopenharmony_ci REGMAP_IRQ_REG(CHT_WC_CRIT_IRQ, 0, BIT(CHT_WC_CRIT_IRQ)), 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct regmap_irq_chip cht_wc_regmap_irq_chip = { 1298c2ecf20Sopenharmony_ci .name = "cht_wc_irq_chip", 1308c2ecf20Sopenharmony_ci .status_base = CHT_WC_IRQLVL1, 1318c2ecf20Sopenharmony_ci .mask_base = CHT_WC_IRQLVL1_MASK, 1328c2ecf20Sopenharmony_ci .irqs = cht_wc_regmap_irqs, 1338c2ecf20Sopenharmony_ci .num_irqs = ARRAY_SIZE(cht_wc_regmap_irqs), 1348c2ecf20Sopenharmony_ci .num_regs = 1, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int cht_wc_probe(struct i2c_client *client) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 1408c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic; 1418c2ecf20Sopenharmony_ci acpi_status status; 1428c2ecf20Sopenharmony_ci unsigned long long hrv; 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); 1468c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 1478c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get PMIC hardware revision\n"); 1488c2ecf20Sopenharmony_ci return -ENODEV; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci if (hrv != CHT_WC_HRV) { 1518c2ecf20Sopenharmony_ci dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv); 1528c2ecf20Sopenharmony_ci return -ENODEV; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (client->irq < 0) { 1558c2ecf20Sopenharmony_ci dev_err(dev, "Invalid IRQ\n"); 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!pmic) 1618c2ecf20Sopenharmony_ci return -ENOMEM; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci pmic->irq = client->irq; 1648c2ecf20Sopenharmony_ci pmic->dev = dev; 1658c2ecf20Sopenharmony_ci i2c_set_clientdata(client, pmic); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg); 1688c2ecf20Sopenharmony_ci if (IS_ERR(pmic->regmap)) 1698c2ecf20Sopenharmony_ci return PTR_ERR(pmic->regmap); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, 1728c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 0, 1738c2ecf20Sopenharmony_ci &cht_wc_regmap_irq_chip, 1748c2ecf20Sopenharmony_ci &pmic->irq_chip_data); 1758c2ecf20Sopenharmony_ci if (ret) 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 1798c2ecf20Sopenharmony_ci cht_wc_dev, ARRAY_SIZE(cht_wc_dev), NULL, 0, 1808c2ecf20Sopenharmony_ci regmap_irq_get_domain(pmic->irq_chip_data)); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void cht_wc_shutdown(struct i2c_client *client) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic = i2c_get_clientdata(client); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci disable_irq(pmic->irq); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int __maybe_unused cht_wc_suspend(struct device *dev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci disable_irq(pmic->irq); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int __maybe_unused cht_wc_resume(struct device *dev) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci enable_irq(pmic->irq); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic const struct i2c_device_id cht_wc_i2c_id[] = { 2108c2ecf20Sopenharmony_ci { } 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic const struct acpi_device_id cht_wc_acpi_ids[] = { 2148c2ecf20Sopenharmony_ci { "INT34D3", }, 2158c2ecf20Sopenharmony_ci { } 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic struct i2c_driver cht_wc_driver = { 2198c2ecf20Sopenharmony_ci .driver = { 2208c2ecf20Sopenharmony_ci .name = "CHT Whiskey Cove PMIC", 2218c2ecf20Sopenharmony_ci .pm = &cht_wc_pm_ops, 2228c2ecf20Sopenharmony_ci .acpi_match_table = cht_wc_acpi_ids, 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci .probe_new = cht_wc_probe, 2258c2ecf20Sopenharmony_ci .shutdown = cht_wc_shutdown, 2268c2ecf20Sopenharmony_ci .id_table = cht_wc_i2c_id, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_cibuiltin_i2c_driver(cht_wc_driver); 229