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 */ 87*c4949630SKuppuswamy Sathyanarayanan BXTWC_BCU_IRQ = 0, 8839d047c0SQipeng Zha BXTWC_ADC_IRQ, 8996007020SHeikki Krogerus BXTWC_USBC_IRQ, 9039d047c0SQipeng Zha BXTWC_CHGR0_IRQ, 9139d047c0SQipeng Zha BXTWC_CHGR1_IRQ, 9239d047c0SQipeng Zha BXTWC_GPIO0_IRQ, 9339d047c0SQipeng Zha BXTWC_GPIO1_IRQ, 9439d047c0SQipeng Zha BXTWC_CRIT_IRQ, 954533d855SKuppuswamy Sathyanarayanan }; 964533d855SKuppuswamy Sathyanarayanan 974533d855SKuppuswamy Sathyanarayanan enum bxtwc_irqs_tmu { 984533d855SKuppuswamy Sathyanarayanan BXTWC_TMU_IRQ = 0, 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[] = { 114*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 0, 0x1f), 115*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 1, 0xff), 116*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 2, BIT(5)), 117*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 2, 0x1f), 118*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 3, 0x1f), 119*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_GPIO0_IRQ, 4, 0xff), 120*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_GPIO1_IRQ, 5, 0x3f), 121*c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 6, 0x03), 12239d047c0SQipeng Zha }; 12339d047c0SQipeng Zha 124957ae509SNilesh Bacchewar static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { 125957ae509SNilesh Bacchewar REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), 126957ae509SNilesh Bacchewar }; 127957ae509SNilesh Bacchewar 12839d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip = { 12939d047c0SQipeng Zha .name = "bxtwc_irq_chip", 13039d047c0SQipeng Zha .status_base = BXTWC_IRQLVL1, 13139d047c0SQipeng Zha .mask_base = BXTWC_MIRQLVL1, 13239d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs, 13339d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs), 13439d047c0SQipeng Zha .num_regs = 2, 13539d047c0SQipeng Zha }; 13639d047c0SQipeng Zha 13739d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip_level2 = { 13839d047c0SQipeng Zha .name = "bxtwc_irq_chip_level2", 139*c4949630SKuppuswamy Sathyanarayanan .status_base = BXTWC_BCUIRQ, 140*c4949630SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MBCUIRQ, 14139d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs_level2, 14239d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_level2), 14339d047c0SQipeng Zha .num_regs = 10, 14439d047c0SQipeng Zha }; 14539d047c0SQipeng Zha 146957ae509SNilesh Bacchewar static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = { 147957ae509SNilesh Bacchewar .name = "bxtwc_irq_chip_tmu", 148957ae509SNilesh Bacchewar .status_base = BXTWC_TMUIRQ, 149957ae509SNilesh Bacchewar .mask_base = BXTWC_MTMUIRQ, 150957ae509SNilesh Bacchewar .irqs = bxtwc_regmap_irqs_tmu, 151957ae509SNilesh Bacchewar .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_tmu), 152957ae509SNilesh Bacchewar .num_regs = 1, 153957ae509SNilesh Bacchewar }; 154957ae509SNilesh Bacchewar 15539d047c0SQipeng Zha static struct resource gpio_resources[] = { 15639d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_GPIO0_IRQ, "GPIO0"), 15739d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_GPIO1_IRQ, "GPIO1"), 15839d047c0SQipeng Zha }; 15939d047c0SQipeng Zha 16039d047c0SQipeng Zha static struct resource adc_resources[] = { 16139d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"), 16239d047c0SQipeng Zha }; 16339d047c0SQipeng Zha 1649c6235c8SBin Gao static struct resource usbc_resources[] = { 16596007020SHeikki Krogerus DEFINE_RES_IRQ(BXTWC_USBC_IRQ), 1669c6235c8SBin Gao }; 1679c6235c8SBin Gao 16839d047c0SQipeng Zha static struct resource charger_resources[] = { 16939d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"), 17039d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"), 17139d047c0SQipeng Zha }; 17239d047c0SQipeng Zha 17339d047c0SQipeng Zha static struct resource thermal_resources[] = { 174*c4949630SKuppuswamy Sathyanarayanan DEFINE_RES_IRQ(BXTWC_THRM_LVL1_IRQ), 17539d047c0SQipeng Zha }; 17639d047c0SQipeng Zha 17739d047c0SQipeng Zha static struct resource bcu_resources[] = { 17839d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"), 17939d047c0SQipeng Zha }; 18039d047c0SQipeng Zha 181957ae509SNilesh Bacchewar static struct resource tmu_resources[] = { 182957ae509SNilesh Bacchewar DEFINE_RES_IRQ_NAMED(BXTWC_TMU_IRQ, "TMU"), 183957ae509SNilesh Bacchewar }; 184957ae509SNilesh Bacchewar 18539d047c0SQipeng Zha static struct mfd_cell bxt_wc_dev[] = { 18639d047c0SQipeng Zha { 18739d047c0SQipeng Zha .name = "bxt_wcove_gpadc", 18839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(adc_resources), 18939d047c0SQipeng Zha .resources = adc_resources, 19039d047c0SQipeng Zha }, 19139d047c0SQipeng Zha { 19239d047c0SQipeng Zha .name = "bxt_wcove_thermal", 19339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(thermal_resources), 19439d047c0SQipeng Zha .resources = thermal_resources, 19539d047c0SQipeng Zha }, 19639d047c0SQipeng Zha { 1979c6235c8SBin Gao .name = "bxt_wcove_usbc", 1989c6235c8SBin Gao .num_resources = ARRAY_SIZE(usbc_resources), 1999c6235c8SBin Gao .resources = usbc_resources, 2009c6235c8SBin Gao }, 2019c6235c8SBin Gao { 20239d047c0SQipeng Zha .name = "bxt_wcove_ext_charger", 20339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(charger_resources), 20439d047c0SQipeng Zha .resources = charger_resources, 20539d047c0SQipeng Zha }, 20639d047c0SQipeng Zha { 20739d047c0SQipeng Zha .name = "bxt_wcove_bcu", 20839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(bcu_resources), 20939d047c0SQipeng Zha .resources = bcu_resources, 21039d047c0SQipeng Zha }, 21139d047c0SQipeng Zha { 212957ae509SNilesh Bacchewar .name = "bxt_wcove_tmu", 213957ae509SNilesh Bacchewar .num_resources = ARRAY_SIZE(tmu_resources), 214957ae509SNilesh Bacchewar .resources = tmu_resources, 215957ae509SNilesh Bacchewar }, 216957ae509SNilesh Bacchewar 217957ae509SNilesh Bacchewar { 21839d047c0SQipeng Zha .name = "bxt_wcove_gpio", 21939d047c0SQipeng Zha .num_resources = ARRAY_SIZE(gpio_resources), 22039d047c0SQipeng Zha .resources = gpio_resources, 22139d047c0SQipeng Zha }, 22239d047c0SQipeng Zha { 22339d047c0SQipeng Zha .name = "bxt_wcove_region", 22439d047c0SQipeng Zha }, 22539d047c0SQipeng Zha }; 22639d047c0SQipeng Zha 22739d047c0SQipeng Zha static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, 22839d047c0SQipeng Zha unsigned int *val) 22939d047c0SQipeng Zha { 23039d047c0SQipeng Zha int ret; 23139d047c0SQipeng Zha int i2c_addr; 23239d047c0SQipeng Zha u8 ipc_in[2]; 23339d047c0SQipeng Zha u8 ipc_out[4]; 23439d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 23539d047c0SQipeng Zha 236b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 237b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 238b4ccc4d2SKuppuswamy Sathyanarayanan 23939d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 24039d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 241b4ccc4d2SKuppuswamy Sathyanarayanan else 24239d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 243b4ccc4d2SKuppuswamy Sathyanarayanan 24439d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 24539d047c0SQipeng Zha 24639d047c0SQipeng Zha ipc_in[0] = reg; 24739d047c0SQipeng Zha ipc_in[1] = i2c_addr; 24839d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 24939d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_READ, 25039d047c0SQipeng Zha ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); 25139d047c0SQipeng Zha if (ret) { 25239d047c0SQipeng Zha dev_err(pmic->dev, "Failed to read from PMIC\n"); 25339d047c0SQipeng Zha return ret; 25439d047c0SQipeng Zha } 25539d047c0SQipeng Zha *val = ipc_out[0]; 25639d047c0SQipeng Zha 25739d047c0SQipeng Zha return 0; 25839d047c0SQipeng Zha } 25939d047c0SQipeng Zha 26039d047c0SQipeng Zha static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, 26139d047c0SQipeng Zha unsigned int val) 26239d047c0SQipeng Zha { 26339d047c0SQipeng Zha int ret; 26439d047c0SQipeng Zha int i2c_addr; 26539d047c0SQipeng Zha u8 ipc_in[3]; 26639d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 26739d047c0SQipeng Zha 268b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 269b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 270b4ccc4d2SKuppuswamy Sathyanarayanan 27139d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 27239d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 273b4ccc4d2SKuppuswamy Sathyanarayanan else 27439d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 275b4ccc4d2SKuppuswamy Sathyanarayanan 27639d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 27739d047c0SQipeng Zha 27839d047c0SQipeng Zha ipc_in[0] = reg; 27939d047c0SQipeng Zha ipc_in[1] = i2c_addr; 28039d047c0SQipeng Zha ipc_in[2] = val; 28139d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 28239d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_WRITE, 28339d047c0SQipeng Zha ipc_in, sizeof(ipc_in), NULL, 0); 28439d047c0SQipeng Zha if (ret) { 28539d047c0SQipeng Zha dev_err(pmic->dev, "Failed to write to PMIC\n"); 28639d047c0SQipeng Zha return ret; 28739d047c0SQipeng Zha } 28839d047c0SQipeng Zha 28939d047c0SQipeng Zha return 0; 29039d047c0SQipeng Zha } 29139d047c0SQipeng Zha 29239d047c0SQipeng Zha /* sysfs interfaces to r/w PMIC registers, required by initial script */ 29339d047c0SQipeng Zha static unsigned long bxtwc_reg_addr; 29439d047c0SQipeng Zha static ssize_t bxtwc_reg_show(struct device *dev, 29539d047c0SQipeng Zha struct device_attribute *attr, char *buf) 29639d047c0SQipeng Zha { 29739d047c0SQipeng Zha return sprintf(buf, "0x%lx\n", bxtwc_reg_addr); 29839d047c0SQipeng Zha } 29939d047c0SQipeng Zha 30039d047c0SQipeng Zha static ssize_t bxtwc_reg_store(struct device *dev, 30139d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 30239d047c0SQipeng Zha { 30339d047c0SQipeng Zha if (kstrtoul(buf, 0, &bxtwc_reg_addr)) { 30439d047c0SQipeng Zha dev_err(dev, "Invalid register address\n"); 30539d047c0SQipeng Zha return -EINVAL; 30639d047c0SQipeng Zha } 30739d047c0SQipeng Zha return (ssize_t)count; 30839d047c0SQipeng Zha } 30939d047c0SQipeng Zha 31039d047c0SQipeng Zha static ssize_t bxtwc_val_show(struct device *dev, 31139d047c0SQipeng Zha struct device_attribute *attr, char *buf) 31239d047c0SQipeng Zha { 31339d047c0SQipeng Zha int ret; 31439d047c0SQipeng Zha unsigned int val; 31539d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 31639d047c0SQipeng Zha 31739d047c0SQipeng Zha ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val); 31839d047c0SQipeng Zha if (ret < 0) { 31939d047c0SQipeng Zha dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr); 32039d047c0SQipeng Zha return -EIO; 32139d047c0SQipeng Zha } 32239d047c0SQipeng Zha 32339d047c0SQipeng Zha return sprintf(buf, "0x%02x\n", val); 32439d047c0SQipeng Zha } 32539d047c0SQipeng Zha 32639d047c0SQipeng Zha static ssize_t bxtwc_val_store(struct device *dev, 32739d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 32839d047c0SQipeng Zha { 32939d047c0SQipeng Zha int ret; 33039d047c0SQipeng Zha unsigned int val; 33139d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 33239d047c0SQipeng Zha 333f3a654c5SDan Carpenter ret = kstrtouint(buf, 0, &val); 334f3a654c5SDan Carpenter if (ret) 335f3a654c5SDan Carpenter return ret; 33639d047c0SQipeng Zha 33739d047c0SQipeng Zha ret = regmap_write(pmic->regmap, bxtwc_reg_addr, val); 33839d047c0SQipeng Zha if (ret) { 33939d047c0SQipeng Zha dev_err(dev, "Failed to write value 0x%02x to address 0x%lx", 34039d047c0SQipeng Zha val, bxtwc_reg_addr); 34139d047c0SQipeng Zha return -EIO; 34239d047c0SQipeng Zha } 34339d047c0SQipeng Zha return count; 34439d047c0SQipeng Zha } 34539d047c0SQipeng Zha 34639d047c0SQipeng Zha static DEVICE_ATTR(addr, S_IWUSR | S_IRUSR, bxtwc_reg_show, bxtwc_reg_store); 34739d047c0SQipeng Zha static DEVICE_ATTR(val, S_IWUSR | S_IRUSR, bxtwc_val_show, bxtwc_val_store); 34839d047c0SQipeng Zha static struct attribute *bxtwc_attrs[] = { 34939d047c0SQipeng Zha &dev_attr_addr.attr, 35039d047c0SQipeng Zha &dev_attr_val.attr, 35139d047c0SQipeng Zha NULL 35239d047c0SQipeng Zha }; 35339d047c0SQipeng Zha 35439d047c0SQipeng Zha static const struct attribute_group bxtwc_group = { 35539d047c0SQipeng Zha .attrs = bxtwc_attrs, 35639d047c0SQipeng Zha }; 35739d047c0SQipeng Zha 35839d047c0SQipeng Zha static const struct regmap_config bxtwc_regmap_config = { 35939d047c0SQipeng Zha .reg_bits = 16, 36039d047c0SQipeng Zha .val_bits = 8, 36139d047c0SQipeng Zha .reg_write = regmap_ipc_byte_reg_write, 36239d047c0SQipeng Zha .reg_read = regmap_ipc_byte_reg_read, 36339d047c0SQipeng Zha }; 36439d047c0SQipeng Zha 36539d047c0SQipeng Zha static int bxtwc_probe(struct platform_device *pdev) 36639d047c0SQipeng Zha { 36739d047c0SQipeng Zha int ret; 36839d047c0SQipeng Zha acpi_handle handle; 36939d047c0SQipeng Zha acpi_status status; 37039d047c0SQipeng Zha unsigned long long hrv; 37139d047c0SQipeng Zha struct intel_soc_pmic *pmic; 37239d047c0SQipeng Zha 37339d047c0SQipeng Zha handle = ACPI_HANDLE(&pdev->dev); 37439d047c0SQipeng Zha status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); 37539d047c0SQipeng Zha if (ACPI_FAILURE(status)) { 37639d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n"); 37739d047c0SQipeng Zha return -ENODEV; 37839d047c0SQipeng Zha } 37939d047c0SQipeng Zha if (hrv != BROXTON_PMIC_WC_HRV) { 38039d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n", 38139d047c0SQipeng Zha hrv); 38239d047c0SQipeng Zha return -ENODEV; 38339d047c0SQipeng Zha } 38439d047c0SQipeng Zha 38539d047c0SQipeng Zha pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); 38639d047c0SQipeng Zha if (!pmic) 38739d047c0SQipeng Zha return -ENOMEM; 38839d047c0SQipeng Zha 38939d047c0SQipeng Zha ret = platform_get_irq(pdev, 0); 39039d047c0SQipeng Zha if (ret < 0) { 39139d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid IRQ\n"); 39239d047c0SQipeng Zha return ret; 39339d047c0SQipeng Zha } 39439d047c0SQipeng Zha pmic->irq = ret; 39539d047c0SQipeng Zha 39639d047c0SQipeng Zha dev_set_drvdata(&pdev->dev, pmic); 39739d047c0SQipeng Zha pmic->dev = &pdev->dev; 39839d047c0SQipeng Zha 39939d047c0SQipeng Zha pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, 40039d047c0SQipeng Zha &bxtwc_regmap_config); 40139d047c0SQipeng Zha if (IS_ERR(pmic->regmap)) { 40239d047c0SQipeng Zha ret = PTR_ERR(pmic->regmap); 40339d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret); 40439d047c0SQipeng Zha return ret; 40539d047c0SQipeng Zha } 40639d047c0SQipeng Zha 40739d047c0SQipeng Zha ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 40839d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 40939d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip, 41039d047c0SQipeng Zha &pmic->irq_chip_data); 41139d047c0SQipeng Zha if (ret) { 41239d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add IRQ chip\n"); 41339d047c0SQipeng Zha return ret; 41439d047c0SQipeng Zha } 41539d047c0SQipeng Zha 41639d047c0SQipeng Zha ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 41739d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 41839d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip_level2, 41939d047c0SQipeng Zha &pmic->irq_chip_data_level2); 42039d047c0SQipeng Zha if (ret) { 42139d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add secondary IRQ chip\n"); 42239d047c0SQipeng Zha goto err_irq_chip_level2; 42339d047c0SQipeng Zha } 42439d047c0SQipeng Zha 425957ae509SNilesh Bacchewar ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, 426957ae509SNilesh Bacchewar IRQF_ONESHOT | IRQF_SHARED, 427957ae509SNilesh Bacchewar 0, &bxtwc_regmap_irq_chip_tmu, 428957ae509SNilesh Bacchewar &pmic->irq_chip_data_tmu); 429957ae509SNilesh Bacchewar if (ret) { 430957ae509SNilesh Bacchewar dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); 431957ae509SNilesh Bacchewar goto err_irq_chip_tmu; 432957ae509SNilesh Bacchewar } 433957ae509SNilesh Bacchewar 43439d047c0SQipeng Zha ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, 43539d047c0SQipeng Zha ARRAY_SIZE(bxt_wc_dev), NULL, 0, 43639d047c0SQipeng Zha NULL); 43739d047c0SQipeng Zha if (ret) { 43839d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add devices\n"); 43939d047c0SQipeng Zha goto err_mfd; 44039d047c0SQipeng Zha } 44139d047c0SQipeng Zha 44239d047c0SQipeng Zha ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group); 44339d047c0SQipeng Zha if (ret) { 44439d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret); 44539d047c0SQipeng Zha goto err_sysfs; 44639d047c0SQipeng Zha } 44739d047c0SQipeng Zha 4489c6235c8SBin Gao /* 4499c6235c8SBin Gao * There is known hw bug. Upon reset BIT 5 of register 4509c6235c8SBin Gao * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, 4519c6235c8SBin Gao * later it's set to 1(masked) automatically by hardware. So we 4529c6235c8SBin Gao * have the software workaround here to unmaksed it in order to let 4539c6235c8SBin Gao * charger interrutp work. 4549c6235c8SBin Gao */ 4559c6235c8SBin Gao regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, 4569c6235c8SBin Gao BXTWC_MIRQLVL1_MCHGR, 0); 4579c6235c8SBin Gao 45839d047c0SQipeng Zha return 0; 45939d047c0SQipeng Zha 46039d047c0SQipeng Zha err_sysfs: 46139d047c0SQipeng Zha mfd_remove_devices(&pdev->dev); 46239d047c0SQipeng Zha err_mfd: 463957ae509SNilesh Bacchewar regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); 464957ae509SNilesh Bacchewar err_irq_chip_tmu: 46539d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); 46639d047c0SQipeng Zha err_irq_chip_level2: 46739d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); 46839d047c0SQipeng Zha 46939d047c0SQipeng Zha return ret; 47039d047c0SQipeng Zha } 47139d047c0SQipeng Zha 47239d047c0SQipeng Zha static int bxtwc_remove(struct platform_device *pdev) 47339d047c0SQipeng Zha { 47439d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 47539d047c0SQipeng Zha 47639d047c0SQipeng Zha sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group); 47739d047c0SQipeng Zha mfd_remove_devices(&pdev->dev); 47839d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); 47939d047c0SQipeng Zha regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); 480957ae509SNilesh Bacchewar regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); 48139d047c0SQipeng Zha 48239d047c0SQipeng Zha return 0; 48339d047c0SQipeng Zha } 48439d047c0SQipeng Zha 48539d047c0SQipeng Zha static void bxtwc_shutdown(struct platform_device *pdev) 48639d047c0SQipeng Zha { 48739d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 48839d047c0SQipeng Zha 48939d047c0SQipeng Zha disable_irq(pmic->irq); 49039d047c0SQipeng Zha } 49139d047c0SQipeng Zha 49239d047c0SQipeng Zha #ifdef CONFIG_PM_SLEEP 49339d047c0SQipeng Zha static int bxtwc_suspend(struct device *dev) 49439d047c0SQipeng Zha { 49539d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 49639d047c0SQipeng Zha 49739d047c0SQipeng Zha disable_irq(pmic->irq); 49839d047c0SQipeng Zha 49939d047c0SQipeng Zha return 0; 50039d047c0SQipeng Zha } 50139d047c0SQipeng Zha 50239d047c0SQipeng Zha static int bxtwc_resume(struct device *dev) 50339d047c0SQipeng Zha { 50439d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 50539d047c0SQipeng Zha 50639d047c0SQipeng Zha enable_irq(pmic->irq); 50739d047c0SQipeng Zha return 0; 50839d047c0SQipeng Zha } 50939d047c0SQipeng Zha #endif 51039d047c0SQipeng Zha static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); 51139d047c0SQipeng Zha 51239d047c0SQipeng Zha static const struct acpi_device_id bxtwc_acpi_ids[] = { 51339d047c0SQipeng Zha { "INT34D3", }, 51439d047c0SQipeng Zha { } 51539d047c0SQipeng Zha }; 516f57576e7SWei Yongjun MODULE_DEVICE_TABLE(acpi, bxtwc_acpi_ids); 51739d047c0SQipeng Zha 51839d047c0SQipeng Zha static struct platform_driver bxtwc_driver = { 51939d047c0SQipeng Zha .probe = bxtwc_probe, 52039d047c0SQipeng Zha .remove = bxtwc_remove, 52139d047c0SQipeng Zha .shutdown = bxtwc_shutdown, 52239d047c0SQipeng Zha .driver = { 52339d047c0SQipeng Zha .name = "BXTWC PMIC", 52439d047c0SQipeng Zha .pm = &bxtwc_pm_ops, 52539d047c0SQipeng Zha .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids), 52639d047c0SQipeng Zha }, 52739d047c0SQipeng Zha }; 52839d047c0SQipeng Zha 52939d047c0SQipeng Zha module_platform_driver(bxtwc_driver); 53039d047c0SQipeng Zha 53139d047c0SQipeng Zha MODULE_LICENSE("GPL v2"); 53239d047c0SQipeng Zha MODULE_AUTHOR("Qipeng Zha<qipeng.zha@intel.com>"); 533