139d047c0SQipeng Zha /* 239d047c0SQipeng Zha * MFD core driver for Intel Broxton Whiskey Cove PMIC 339d047c0SQipeng Zha * 439d047c0SQipeng Zha * Copyright (C) 2015 Intel Corporation. All rights reserved. 539d047c0SQipeng Zha * 639d047c0SQipeng Zha * This program is free software; you can redistribute it and/or modify it 739d047c0SQipeng Zha * under the terms and conditions of the GNU General Public License, 839d047c0SQipeng Zha * version 2, as published by the Free Software Foundation. 939d047c0SQipeng Zha * 1039d047c0SQipeng Zha * This program is distributed in the hope it will be useful, but WITHOUT 1139d047c0SQipeng Zha * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1239d047c0SQipeng Zha * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1339d047c0SQipeng Zha * more details. 1439d047c0SQipeng Zha */ 1539d047c0SQipeng Zha 1639d047c0SQipeng Zha #include <linux/module.h> 1739d047c0SQipeng Zha #include <linux/acpi.h> 1839d047c0SQipeng Zha #include <linux/err.h> 1939d047c0SQipeng Zha #include <linux/delay.h> 2039d047c0SQipeng Zha #include <linux/interrupt.h> 2139d047c0SQipeng Zha #include <linux/kernel.h> 2239d047c0SQipeng Zha #include <linux/mfd/core.h> 23f1e34ad8SAndy Shevchenko #include <linux/mfd/intel_soc_pmic.h> 240c227c51SAndy Shevchenko #include <linux/mfd/intel_soc_pmic_bxtwc.h> 2539d047c0SQipeng Zha #include <asm/intel_pmc_ipc.h> 2639d047c0SQipeng Zha 2739d047c0SQipeng Zha /* PMIC device registers */ 2839d047c0SQipeng Zha #define REG_ADDR_MASK 0xFF00 2939d047c0SQipeng Zha #define REG_ADDR_SHIFT 8 3039d047c0SQipeng Zha #define REG_OFFSET_MASK 0xFF 3139d047c0SQipeng Zha 3239d047c0SQipeng Zha /* Interrupt Status Registers */ 3339d047c0SQipeng Zha #define BXTWC_IRQLVL1 0x4E02 3439d047c0SQipeng Zha #define BXTWC_PWRBTNIRQ 0x4E03 3539d047c0SQipeng Zha 3639d047c0SQipeng Zha #define BXTWC_THRM0IRQ 0x4E04 3739d047c0SQipeng Zha #define BXTWC_THRM1IRQ 0x4E05 3839d047c0SQipeng Zha #define BXTWC_THRM2IRQ 0x4E06 3939d047c0SQipeng Zha #define BXTWC_BCUIRQ 0x4E07 4039d047c0SQipeng Zha #define BXTWC_ADCIRQ 0x4E08 4139d047c0SQipeng Zha #define BXTWC_CHGR0IRQ 0x4E09 4239d047c0SQipeng Zha #define BXTWC_CHGR1IRQ 0x4E0A 4339d047c0SQipeng Zha #define BXTWC_GPIOIRQ0 0x4E0B 4439d047c0SQipeng Zha #define BXTWC_GPIOIRQ1 0x4E0C 4539d047c0SQipeng Zha #define BXTWC_CRITIRQ 0x4E0D 46957ae509SNilesh Bacchewar #define BXTWC_TMUIRQ 0x4FB6 4739d047c0SQipeng Zha 4839d047c0SQipeng Zha /* Interrupt MASK Registers */ 4939d047c0SQipeng Zha #define BXTWC_MIRQLVL1 0x4E0E 5039d047c0SQipeng Zha #define BXTWC_MPWRTNIRQ 0x4E0F 5139d047c0SQipeng Zha 529c6235c8SBin Gao #define BXTWC_MIRQLVL1_MCHGR BIT(5) 539c6235c8SBin Gao 5439d047c0SQipeng Zha #define BXTWC_MTHRM0IRQ 0x4E12 5539d047c0SQipeng Zha #define BXTWC_MTHRM1IRQ 0x4E13 5639d047c0SQipeng Zha #define BXTWC_MTHRM2IRQ 0x4E14 5739d047c0SQipeng Zha #define BXTWC_MBCUIRQ 0x4E15 5839d047c0SQipeng Zha #define BXTWC_MADCIRQ 0x4E16 5939d047c0SQipeng Zha #define BXTWC_MCHGR0IRQ 0x4E17 6039d047c0SQipeng Zha #define BXTWC_MCHGR1IRQ 0x4E18 6139d047c0SQipeng Zha #define BXTWC_MGPIO0IRQ 0x4E19 6239d047c0SQipeng Zha #define BXTWC_MGPIO1IRQ 0x4E1A 6339d047c0SQipeng Zha #define BXTWC_MCRITIRQ 0x4E1B 64957ae509SNilesh Bacchewar #define BXTWC_MTMUIRQ 0x4FB7 6539d047c0SQipeng Zha 6639d047c0SQipeng Zha /* Whiskey Cove PMIC share same ACPI ID between different platforms */ 6739d047c0SQipeng Zha #define BROXTON_PMIC_WC_HRV 4 6839d047c0SQipeng Zha 6939d047c0SQipeng Zha /* Manage in two IRQ chips since mask registers are not consecutive */ 7039d047c0SQipeng Zha enum bxtwc_irqs { 7139d047c0SQipeng Zha /* Level 1 */ 7239d047c0SQipeng Zha BXTWC_PWRBTN_LVL1_IRQ = 0, 7339d047c0SQipeng Zha BXTWC_TMU_LVL1_IRQ, 7439d047c0SQipeng Zha BXTWC_THRM_LVL1_IRQ, 7539d047c0SQipeng Zha BXTWC_BCU_LVL1_IRQ, 7639d047c0SQipeng Zha BXTWC_ADC_LVL1_IRQ, 7739d047c0SQipeng Zha BXTWC_CHGR_LVL1_IRQ, 7839d047c0SQipeng Zha BXTWC_GPIO_LVL1_IRQ, 7939d047c0SQipeng Zha BXTWC_CRIT_LVL1_IRQ, 8039d047c0SQipeng Zha 8139d047c0SQipeng Zha /* Level 2 */ 8239d047c0SQipeng Zha BXTWC_PWRBTN_IRQ, 8339d047c0SQipeng Zha }; 8439d047c0SQipeng Zha 8539d047c0SQipeng Zha enum bxtwc_irqs_level2 { 8639d047c0SQipeng Zha /* Level 2 */ 8739d047c0SQipeng Zha BXTWC_THRM0_IRQ = 0, 8839d047c0SQipeng Zha BXTWC_THRM1_IRQ, 8939d047c0SQipeng Zha BXTWC_THRM2_IRQ, 9039d047c0SQipeng Zha BXTWC_BCU_IRQ, 9139d047c0SQipeng Zha BXTWC_ADC_IRQ, 9296007020SHeikki Krogerus BXTWC_USBC_IRQ, 9339d047c0SQipeng Zha BXTWC_CHGR0_IRQ, 9439d047c0SQipeng Zha BXTWC_CHGR1_IRQ, 9539d047c0SQipeng Zha BXTWC_GPIO0_IRQ, 9639d047c0SQipeng Zha BXTWC_GPIO1_IRQ, 9739d047c0SQipeng Zha BXTWC_CRIT_IRQ, 98957ae509SNilesh Bacchewar BXTWC_TMU_IRQ, 9939d047c0SQipeng Zha }; 10039d047c0SQipeng Zha 10139d047c0SQipeng Zha static const struct regmap_irq bxtwc_regmap_irqs[] = { 10239d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_PWRBTN_LVL1_IRQ, 0, BIT(0)), 10339d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_TMU_LVL1_IRQ, 0, BIT(1)), 10439d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM_LVL1_IRQ, 0, BIT(2)), 10539d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_BCU_LVL1_IRQ, 0, BIT(3)), 10639d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_ADC_LVL1_IRQ, 0, BIT(4)), 10739d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CHGR_LVL1_IRQ, 0, BIT(5)), 10839d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_GPIO_LVL1_IRQ, 0, BIT(6)), 10939d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CRIT_LVL1_IRQ, 0, BIT(7)), 11039d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 1, 0x03), 11139d047c0SQipeng Zha }; 11239d047c0SQipeng Zha 11339d047c0SQipeng Zha static const struct regmap_irq bxtwc_regmap_irqs_level2[] = { 11439d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM0_IRQ, 0, 0xff), 11539d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM1_IRQ, 1, 0xbf), 11639d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM2_IRQ, 2, 0xff), 11739d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 3, 0x1f), 11839d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 4, 0xff), 11996007020SHeikki Krogerus REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 5, BIT(5)), 12096007020SHeikki Krogerus REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 5, 0x1f), 12139d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 6, 0x1f), 12239d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_GPIO0_IRQ, 7, 0xff), 12339d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_GPIO1_IRQ, 8, 0x3f), 12439d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 9, 0x03), 12539d047c0SQipeng Zha }; 12639d047c0SQipeng Zha 127957ae509SNilesh Bacchewar static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { 128957ae509SNilesh Bacchewar REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), 129957ae509SNilesh Bacchewar }; 130957ae509SNilesh Bacchewar 13139d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip = { 13239d047c0SQipeng Zha .name = "bxtwc_irq_chip", 13339d047c0SQipeng Zha .status_base = BXTWC_IRQLVL1, 13439d047c0SQipeng Zha .mask_base = BXTWC_MIRQLVL1, 13539d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs, 13639d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs), 13739d047c0SQipeng Zha .num_regs = 2, 13839d047c0SQipeng Zha }; 13939d047c0SQipeng Zha 14039d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip_level2 = { 14139d047c0SQipeng Zha .name = "bxtwc_irq_chip_level2", 14239d047c0SQipeng Zha .status_base = BXTWC_THRM0IRQ, 14339d047c0SQipeng Zha .mask_base = BXTWC_MTHRM0IRQ, 14439d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs_level2, 14539d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_level2), 14639d047c0SQipeng Zha .num_regs = 10, 14739d047c0SQipeng Zha }; 14839d047c0SQipeng Zha 149957ae509SNilesh Bacchewar static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = { 150957ae509SNilesh Bacchewar .name = "bxtwc_irq_chip_tmu", 151957ae509SNilesh Bacchewar .status_base = BXTWC_TMUIRQ, 152957ae509SNilesh Bacchewar .mask_base = BXTWC_MTMUIRQ, 153957ae509SNilesh Bacchewar .irqs = bxtwc_regmap_irqs_tmu, 154957ae509SNilesh Bacchewar .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_tmu), 155957ae509SNilesh Bacchewar .num_regs = 1, 156957ae509SNilesh Bacchewar }; 157957ae509SNilesh Bacchewar 15839d047c0SQipeng Zha static struct resource gpio_resources[] = { 15939d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_GPIO0_IRQ, "GPIO0"), 16039d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_GPIO1_IRQ, "GPIO1"), 16139d047c0SQipeng Zha }; 16239d047c0SQipeng Zha 16339d047c0SQipeng Zha static struct resource adc_resources[] = { 16439d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"), 16539d047c0SQipeng Zha }; 16639d047c0SQipeng Zha 1679c6235c8SBin Gao static struct resource usbc_resources[] = { 16896007020SHeikki Krogerus DEFINE_RES_IRQ(BXTWC_USBC_IRQ), 1699c6235c8SBin Gao }; 1709c6235c8SBin Gao 17139d047c0SQipeng Zha static struct resource charger_resources[] = { 17239d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"), 17339d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"), 17439d047c0SQipeng Zha }; 17539d047c0SQipeng Zha 17639d047c0SQipeng Zha static struct resource thermal_resources[] = { 17739d047c0SQipeng Zha DEFINE_RES_IRQ(BXTWC_THRM0_IRQ), 17839d047c0SQipeng Zha DEFINE_RES_IRQ(BXTWC_THRM1_IRQ), 17939d047c0SQipeng Zha DEFINE_RES_IRQ(BXTWC_THRM2_IRQ), 18039d047c0SQipeng Zha }; 18139d047c0SQipeng Zha 18239d047c0SQipeng Zha static struct resource bcu_resources[] = { 18339d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"), 18439d047c0SQipeng Zha }; 18539d047c0SQipeng Zha 186957ae509SNilesh Bacchewar static struct resource tmu_resources[] = { 187957ae509SNilesh Bacchewar DEFINE_RES_IRQ_NAMED(BXTWC_TMU_IRQ, "TMU"), 188957ae509SNilesh Bacchewar }; 189957ae509SNilesh Bacchewar 19039d047c0SQipeng Zha static struct mfd_cell bxt_wc_dev[] = { 19139d047c0SQipeng Zha { 19239d047c0SQipeng Zha .name = "bxt_wcove_gpadc", 19339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(adc_resources), 19439d047c0SQipeng Zha .resources = adc_resources, 19539d047c0SQipeng Zha }, 19639d047c0SQipeng Zha { 19739d047c0SQipeng Zha .name = "bxt_wcove_thermal", 19839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(thermal_resources), 19939d047c0SQipeng Zha .resources = thermal_resources, 20039d047c0SQipeng Zha }, 20139d047c0SQipeng Zha { 2029c6235c8SBin Gao .name = "bxt_wcove_usbc", 2039c6235c8SBin Gao .num_resources = ARRAY_SIZE(usbc_resources), 2049c6235c8SBin Gao .resources = usbc_resources, 2059c6235c8SBin Gao }, 2069c6235c8SBin Gao { 20739d047c0SQipeng Zha .name = "bxt_wcove_ext_charger", 20839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(charger_resources), 20939d047c0SQipeng Zha .resources = charger_resources, 21039d047c0SQipeng Zha }, 21139d047c0SQipeng Zha { 21239d047c0SQipeng Zha .name = "bxt_wcove_bcu", 21339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(bcu_resources), 21439d047c0SQipeng Zha .resources = bcu_resources, 21539d047c0SQipeng Zha }, 21639d047c0SQipeng Zha { 217957ae509SNilesh Bacchewar .name = "bxt_wcove_tmu", 218957ae509SNilesh Bacchewar .num_resources = ARRAY_SIZE(tmu_resources), 219957ae509SNilesh Bacchewar .resources = tmu_resources, 220957ae509SNilesh Bacchewar }, 221957ae509SNilesh Bacchewar 222957ae509SNilesh Bacchewar { 22339d047c0SQipeng Zha .name = "bxt_wcove_gpio", 22439d047c0SQipeng Zha .num_resources = ARRAY_SIZE(gpio_resources), 22539d047c0SQipeng Zha .resources = gpio_resources, 22639d047c0SQipeng Zha }, 22739d047c0SQipeng Zha { 22839d047c0SQipeng Zha .name = "bxt_wcove_region", 22939d047c0SQipeng Zha }, 23039d047c0SQipeng Zha }; 23139d047c0SQipeng Zha 23239d047c0SQipeng Zha static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, 23339d047c0SQipeng Zha unsigned int *val) 23439d047c0SQipeng Zha { 23539d047c0SQipeng Zha int ret; 23639d047c0SQipeng Zha int i2c_addr; 23739d047c0SQipeng Zha u8 ipc_in[2]; 23839d047c0SQipeng Zha u8 ipc_out[4]; 23939d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 24039d047c0SQipeng Zha 241*b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 242*b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 243*b4ccc4d2SKuppuswamy Sathyanarayanan 24439d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 24539d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 246*b4ccc4d2SKuppuswamy Sathyanarayanan else 24739d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 248*b4ccc4d2SKuppuswamy Sathyanarayanan 24939d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 25039d047c0SQipeng Zha 25139d047c0SQipeng Zha ipc_in[0] = reg; 25239d047c0SQipeng Zha ipc_in[1] = i2c_addr; 25339d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 25439d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_READ, 25539d047c0SQipeng Zha ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); 25639d047c0SQipeng Zha if (ret) { 25739d047c0SQipeng Zha dev_err(pmic->dev, "Failed to read from PMIC\n"); 25839d047c0SQipeng Zha return ret; 25939d047c0SQipeng Zha } 26039d047c0SQipeng Zha *val = ipc_out[0]; 26139d047c0SQipeng Zha 26239d047c0SQipeng Zha return 0; 26339d047c0SQipeng Zha } 26439d047c0SQipeng Zha 26539d047c0SQipeng Zha static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, 26639d047c0SQipeng Zha unsigned int val) 26739d047c0SQipeng Zha { 26839d047c0SQipeng Zha int ret; 26939d047c0SQipeng Zha int i2c_addr; 27039d047c0SQipeng Zha u8 ipc_in[3]; 27139d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 27239d047c0SQipeng Zha 273*b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 274*b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 275*b4ccc4d2SKuppuswamy Sathyanarayanan 27639d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 27739d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 278*b4ccc4d2SKuppuswamy Sathyanarayanan else 27939d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 280*b4ccc4d2SKuppuswamy Sathyanarayanan 28139d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 28239d047c0SQipeng Zha 28339d047c0SQipeng Zha ipc_in[0] = reg; 28439d047c0SQipeng Zha ipc_in[1] = i2c_addr; 28539d047c0SQipeng Zha ipc_in[2] = val; 28639d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 28739d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_WRITE, 28839d047c0SQipeng Zha ipc_in, sizeof(ipc_in), NULL, 0); 28939d047c0SQipeng Zha if (ret) { 29039d047c0SQipeng Zha dev_err(pmic->dev, "Failed to write to PMIC\n"); 29139d047c0SQipeng Zha return ret; 29239d047c0SQipeng Zha } 29339d047c0SQipeng Zha 29439d047c0SQipeng Zha return 0; 29539d047c0SQipeng Zha } 29639d047c0SQipeng Zha 29739d047c0SQipeng Zha /* sysfs interfaces to r/w PMIC registers, required by initial script */ 29839d047c0SQipeng Zha static unsigned long bxtwc_reg_addr; 29939d047c0SQipeng Zha static ssize_t bxtwc_reg_show(struct device *dev, 30039d047c0SQipeng Zha struct device_attribute *attr, char *buf) 30139d047c0SQipeng Zha { 30239d047c0SQipeng Zha return sprintf(buf, "0x%lx\n", bxtwc_reg_addr); 30339d047c0SQipeng Zha } 30439d047c0SQipeng Zha 30539d047c0SQipeng Zha static ssize_t bxtwc_reg_store(struct device *dev, 30639d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 30739d047c0SQipeng Zha { 30839d047c0SQipeng Zha if (kstrtoul(buf, 0, &bxtwc_reg_addr)) { 30939d047c0SQipeng Zha dev_err(dev, "Invalid register address\n"); 31039d047c0SQipeng Zha return -EINVAL; 31139d047c0SQipeng Zha } 31239d047c0SQipeng Zha return (ssize_t)count; 31339d047c0SQipeng Zha } 31439d047c0SQipeng Zha 31539d047c0SQipeng Zha static ssize_t bxtwc_val_show(struct device *dev, 31639d047c0SQipeng Zha struct device_attribute *attr, char *buf) 31739d047c0SQipeng Zha { 31839d047c0SQipeng Zha int ret; 31939d047c0SQipeng Zha unsigned int val; 32039d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 32139d047c0SQipeng Zha 32239d047c0SQipeng Zha ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val); 32339d047c0SQipeng Zha if (ret < 0) { 32439d047c0SQipeng Zha dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr); 32539d047c0SQipeng Zha return -EIO; 32639d047c0SQipeng Zha } 32739d047c0SQipeng Zha 32839d047c0SQipeng Zha return sprintf(buf, "0x%02x\n", val); 32939d047c0SQipeng Zha } 33039d047c0SQipeng Zha 33139d047c0SQipeng Zha static ssize_t bxtwc_val_store(struct device *dev, 33239d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 33339d047c0SQipeng Zha { 33439d047c0SQipeng Zha int ret; 33539d047c0SQipeng Zha unsigned int val; 33639d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 33739d047c0SQipeng Zha 338f3a654c5SDan Carpenter ret = kstrtouint(buf, 0, &val); 339f3a654c5SDan Carpenter if (ret) 340f3a654c5SDan Carpenter return ret; 34139d047c0SQipeng Zha 34239d047c0SQipeng Zha ret = regmap_write(pmic->regmap, bxtwc_reg_addr, val); 34339d047c0SQipeng Zha if (ret) { 34439d047c0SQipeng Zha dev_err(dev, "Failed to write value 0x%02x to address 0x%lx", 34539d047c0SQipeng Zha val, bxtwc_reg_addr); 34639d047c0SQipeng Zha return -EIO; 34739d047c0SQipeng Zha } 34839d047c0SQipeng Zha return count; 34939d047c0SQipeng Zha } 35039d047c0SQipeng Zha 35139d047c0SQipeng Zha static DEVICE_ATTR(addr, S_IWUSR | S_IRUSR, bxtwc_reg_show, bxtwc_reg_store); 35239d047c0SQipeng Zha static DEVICE_ATTR(val, S_IWUSR | S_IRUSR, bxtwc_val_show, bxtwc_val_store); 35339d047c0SQipeng Zha static struct attribute *bxtwc_attrs[] = { 35439d047c0SQipeng Zha &dev_attr_addr.attr, 35539d047c0SQipeng Zha &dev_attr_val.attr, 35639d047c0SQipeng Zha NULL 35739d047c0SQipeng Zha }; 35839d047c0SQipeng Zha 35939d047c0SQipeng Zha static const struct attribute_group bxtwc_group = { 36039d047c0SQipeng Zha .attrs = bxtwc_attrs, 36139d047c0SQipeng Zha }; 36239d047c0SQipeng Zha 36339d047c0SQipeng Zha static const struct regmap_config bxtwc_regmap_config = { 36439d047c0SQipeng Zha .reg_bits = 16, 36539d047c0SQipeng Zha .val_bits = 8, 36639d047c0SQipeng Zha .reg_write = regmap_ipc_byte_reg_write, 36739d047c0SQipeng Zha .reg_read = regmap_ipc_byte_reg_read, 36839d047c0SQipeng Zha }; 36939d047c0SQipeng Zha 37039d047c0SQipeng Zha static int bxtwc_probe(struct platform_device *pdev) 37139d047c0SQipeng Zha { 37239d047c0SQipeng Zha int ret; 37339d047c0SQipeng Zha acpi_handle handle; 37439d047c0SQipeng Zha acpi_status status; 37539d047c0SQipeng Zha unsigned long long hrv; 37639d047c0SQipeng Zha struct intel_soc_pmic *pmic; 37739d047c0SQipeng Zha 37839d047c0SQipeng Zha handle = ACPI_HANDLE(&pdev->dev); 37939d047c0SQipeng Zha status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); 38039d047c0SQipeng Zha if (ACPI_FAILURE(status)) { 38139d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n"); 38239d047c0SQipeng Zha return -ENODEV; 38339d047c0SQipeng Zha } 38439d047c0SQipeng Zha if (hrv != BROXTON_PMIC_WC_HRV) { 38539d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n", 38639d047c0SQipeng Zha hrv); 38739d047c0SQipeng Zha return -ENODEV; 38839d047c0SQipeng Zha } 38939d047c0SQipeng Zha 39039d047c0SQipeng Zha pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); 39139d047c0SQipeng Zha if (!pmic) 39239d047c0SQipeng Zha return -ENOMEM; 39339d047c0SQipeng Zha 39439d047c0SQipeng Zha ret = platform_get_irq(pdev, 0); 39539d047c0SQipeng Zha if (ret < 0) { 39639d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid IRQ\n"); 39739d047c0SQipeng Zha return ret; 39839d047c0SQipeng Zha } 39939d047c0SQipeng Zha pmic->irq = ret; 40039d047c0SQipeng Zha 40139d047c0SQipeng Zha dev_set_drvdata(&pdev->dev, pmic); 40239d047c0SQipeng Zha pmic->dev = &pdev->dev; 40339d047c0SQipeng Zha 40439d047c0SQipeng Zha pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, 40539d047c0SQipeng Zha &bxtwc_regmap_config); 40639d047c0SQipeng Zha if (IS_ERR(pmic->regmap)) { 40739d047c0SQipeng Zha ret = PTR_ERR(pmic->regmap); 40839d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret); 40939d047c0SQipeng Zha return ret; 41039d047c0SQipeng Zha } 41139d047c0SQipeng Zha 41239d047c0SQipeng Zha ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 41339d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 41439d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip, 41539d047c0SQipeng Zha &pmic->irq_chip_data); 41639d047c0SQipeng Zha if (ret) { 41739d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add IRQ chip\n"); 41839d047c0SQipeng Zha return ret; 41939d047c0SQipeng Zha } 42039d047c0SQipeng Zha 42139d047c0SQipeng Zha ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 42239d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 42339d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip_level2, 42439d047c0SQipeng Zha &pmic->irq_chip_data_level2); 42539d047c0SQipeng Zha if (ret) { 42639d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add secondary IRQ chip\n"); 42739d047c0SQipeng Zha goto err_irq_chip_level2; 42839d047c0SQipeng Zha } 42939d047c0SQipeng Zha 430957ae509SNilesh Bacchewar ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 431957ae509SNilesh Bacchewar IRQF_ONESHOT | IRQF_SHARED, 432957ae509SNilesh Bacchewar 0, &bxtwc_regmap_irq_chip_tmu, 433957ae509SNilesh Bacchewar &pmic->irq_chip_data_tmu); 434957ae509SNilesh Bacchewar if (ret) { 435957ae509SNilesh Bacchewar dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); 436957ae509SNilesh Bacchewar goto err_irq_chip_tmu; 437957ae509SNilesh Bacchewar } 438957ae509SNilesh Bacchewar 43939d047c0SQipeng Zha ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, 44039d047c0SQipeng Zha ARRAY_SIZE(bxt_wc_dev), NULL, 0, 44139d047c0SQipeng Zha NULL); 44239d047c0SQipeng Zha if (ret) { 44339d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add devices\n"); 44439d047c0SQipeng Zha goto err_mfd; 44539d047c0SQipeng Zha } 44639d047c0SQipeng Zha 44739d047c0SQipeng Zha ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group); 44839d047c0SQipeng Zha if (ret) { 44939d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret); 45039d047c0SQipeng Zha goto err_sysfs; 45139d047c0SQipeng Zha } 45239d047c0SQipeng Zha 4539c6235c8SBin Gao /* 4549c6235c8SBin Gao * There is known hw bug. Upon reset BIT 5 of register 4559c6235c8SBin Gao * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, 4569c6235c8SBin Gao * later it's set to 1(masked) automatically by hardware. So we 4579c6235c8SBin Gao * have the software workaround here to unmaksed it in order to let 4589c6235c8SBin Gao * charger interrutp work. 4599c6235c8SBin Gao */ 4609c6235c8SBin Gao regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, 4619c6235c8SBin Gao BXTWC_MIRQLVL1_MCHGR, 0); 4629c6235c8SBin Gao 46339d047c0SQipeng Zha return 0; 46439d047c0SQipeng Zha 46539d047c0SQipeng Zha err_sysfs: 46639d047c0SQipeng Zha mfd_remove_devices(&pdev->dev); 46739d047c0SQipeng Zha err_mfd: 468957ae509SNilesh Bacchewar regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); 469957ae509SNilesh Bacchewar err_irq_chip_tmu: 47039d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); 47139d047c0SQipeng Zha err_irq_chip_level2: 47239d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); 47339d047c0SQipeng Zha 47439d047c0SQipeng Zha return ret; 47539d047c0SQipeng Zha } 47639d047c0SQipeng Zha 47739d047c0SQipeng Zha static int bxtwc_remove(struct platform_device *pdev) 47839d047c0SQipeng Zha { 47939d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 48039d047c0SQipeng Zha 48139d047c0SQipeng Zha sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group); 48239d047c0SQipeng Zha mfd_remove_devices(&pdev->dev); 48339d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); 48439d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); 485957ae509SNilesh Bacchewar regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); 48639d047c0SQipeng Zha 48739d047c0SQipeng Zha return 0; 48839d047c0SQipeng Zha } 48939d047c0SQipeng Zha 49039d047c0SQipeng Zha static void bxtwc_shutdown(struct platform_device *pdev) 49139d047c0SQipeng Zha { 49239d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 49339d047c0SQipeng Zha 49439d047c0SQipeng Zha disable_irq(pmic->irq); 49539d047c0SQipeng Zha } 49639d047c0SQipeng Zha 49739d047c0SQipeng Zha #ifdef CONFIG_PM_SLEEP 49839d047c0SQipeng Zha static int bxtwc_suspend(struct device *dev) 49939d047c0SQipeng Zha { 50039d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 50139d047c0SQipeng Zha 50239d047c0SQipeng Zha disable_irq(pmic->irq); 50339d047c0SQipeng Zha 50439d047c0SQipeng Zha return 0; 50539d047c0SQipeng Zha } 50639d047c0SQipeng Zha 50739d047c0SQipeng Zha static int bxtwc_resume(struct device *dev) 50839d047c0SQipeng Zha { 50939d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 51039d047c0SQipeng Zha 51139d047c0SQipeng Zha enable_irq(pmic->irq); 51239d047c0SQipeng Zha return 0; 51339d047c0SQipeng Zha } 51439d047c0SQipeng Zha #endif 51539d047c0SQipeng Zha static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); 51639d047c0SQipeng Zha 51739d047c0SQipeng Zha static const struct acpi_device_id bxtwc_acpi_ids[] = { 51839d047c0SQipeng Zha { "INT34D3", }, 51939d047c0SQipeng Zha { } 52039d047c0SQipeng Zha }; 521f57576e7SWei Yongjun MODULE_DEVICE_TABLE(acpi, bxtwc_acpi_ids); 52239d047c0SQipeng Zha 52339d047c0SQipeng Zha static struct platform_driver bxtwc_driver = { 52439d047c0SQipeng Zha .probe = bxtwc_probe, 52539d047c0SQipeng Zha .remove = bxtwc_remove, 52639d047c0SQipeng Zha .shutdown = bxtwc_shutdown, 52739d047c0SQipeng Zha .driver = { 52839d047c0SQipeng Zha .name = "BXTWC PMIC", 52939d047c0SQipeng Zha .pm = &bxtwc_pm_ops, 53039d047c0SQipeng Zha .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids), 53139d047c0SQipeng Zha }, 53239d047c0SQipeng Zha }; 53339d047c0SQipeng Zha 53439d047c0SQipeng Zha module_platform_driver(bxtwc_driver); 53539d047c0SQipeng Zha 53639d047c0SQipeng Zha MODULE_LICENSE("GPL v2"); 53739d047c0SQipeng Zha MODULE_AUTHOR("Qipeng Zha<qipeng.zha@intel.com>"); 538