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 85*57129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_bcu { 86c4949630SKuppuswamy Sathyanarayanan BXTWC_BCU_IRQ = 0, 87*57129044SKuppuswamy Sathyanarayanan }; 88*57129044SKuppuswamy Sathyanarayanan 89*57129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_adc { 90*57129044SKuppuswamy Sathyanarayanan BXTWC_ADC_IRQ = 0, 91*57129044SKuppuswamy Sathyanarayanan }; 92*57129044SKuppuswamy Sathyanarayanan 93*57129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_chgr { 94*57129044SKuppuswamy Sathyanarayanan BXTWC_USBC_IRQ = 0, 9539d047c0SQipeng Zha BXTWC_CHGR0_IRQ, 9639d047c0SQipeng Zha BXTWC_CHGR1_IRQ, 974533d855SKuppuswamy Sathyanarayanan }; 984533d855SKuppuswamy Sathyanarayanan 994533d855SKuppuswamy Sathyanarayanan enum bxtwc_irqs_tmu { 1004533d855SKuppuswamy Sathyanarayanan BXTWC_TMU_IRQ = 0, 10139d047c0SQipeng Zha }; 10239d047c0SQipeng Zha 103*57129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_crit { 104*57129044SKuppuswamy Sathyanarayanan BXTWC_CRIT_IRQ = 0, 105*57129044SKuppuswamy Sathyanarayanan }; 106*57129044SKuppuswamy Sathyanarayanan 10739d047c0SQipeng Zha static const struct regmap_irq bxtwc_regmap_irqs[] = { 10839d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_PWRBTN_LVL1_IRQ, 0, BIT(0)), 10939d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_TMU_LVL1_IRQ, 0, BIT(1)), 11039d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM_LVL1_IRQ, 0, BIT(2)), 11139d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_BCU_LVL1_IRQ, 0, BIT(3)), 11239d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_ADC_LVL1_IRQ, 0, BIT(4)), 11339d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CHGR_LVL1_IRQ, 0, BIT(5)), 11439d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_GPIO_LVL1_IRQ, 0, BIT(6)), 11539d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CRIT_LVL1_IRQ, 0, BIT(7)), 11639d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 1, 0x03), 11739d047c0SQipeng Zha }; 11839d047c0SQipeng Zha 119*57129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_bcu[] = { 120c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 0, 0x1f), 121*57129044SKuppuswamy Sathyanarayanan }; 122*57129044SKuppuswamy Sathyanarayanan 123*57129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_adc[] = { 124*57129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 0, 0xff), 125*57129044SKuppuswamy Sathyanarayanan }; 126*57129044SKuppuswamy Sathyanarayanan 127*57129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_chgr[] = { 128*57129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, BIT(5)), 129*57129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 0, 0x1f), 130*57129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 1, 0x1f), 13139d047c0SQipeng Zha }; 13239d047c0SQipeng Zha 133957ae509SNilesh Bacchewar static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { 134957ae509SNilesh Bacchewar REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), 135957ae509SNilesh Bacchewar }; 136957ae509SNilesh Bacchewar 137*57129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_crit[] = { 138*57129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 0, 0x03), 139*57129044SKuppuswamy Sathyanarayanan }; 140*57129044SKuppuswamy Sathyanarayanan 14139d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip = { 14239d047c0SQipeng Zha .name = "bxtwc_irq_chip", 14339d047c0SQipeng Zha .status_base = BXTWC_IRQLVL1, 14439d047c0SQipeng Zha .mask_base = BXTWC_MIRQLVL1, 14539d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs, 14639d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs), 14739d047c0SQipeng Zha .num_regs = 2, 14839d047c0SQipeng Zha }; 14939d047c0SQipeng Zha 150957ae509SNilesh Bacchewar static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = { 151957ae509SNilesh Bacchewar .name = "bxtwc_irq_chip_tmu", 152957ae509SNilesh Bacchewar .status_base = BXTWC_TMUIRQ, 153957ae509SNilesh Bacchewar .mask_base = BXTWC_MTMUIRQ, 154957ae509SNilesh Bacchewar .irqs = bxtwc_regmap_irqs_tmu, 155957ae509SNilesh Bacchewar .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_tmu), 156957ae509SNilesh Bacchewar .num_regs = 1, 157957ae509SNilesh Bacchewar }; 158957ae509SNilesh Bacchewar 159*57129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_bcu = { 160*57129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_bcu", 161*57129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_BCUIRQ, 162*57129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MBCUIRQ, 163*57129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_bcu, 164*57129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_bcu), 165*57129044SKuppuswamy Sathyanarayanan .num_regs = 1, 166*57129044SKuppuswamy Sathyanarayanan }; 167*57129044SKuppuswamy Sathyanarayanan 168*57129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_adc = { 169*57129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_adc", 170*57129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_ADCIRQ, 171*57129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MADCIRQ, 172*57129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_adc, 173*57129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_adc), 174*57129044SKuppuswamy Sathyanarayanan .num_regs = 1, 175*57129044SKuppuswamy Sathyanarayanan }; 176*57129044SKuppuswamy Sathyanarayanan 177*57129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_chgr = { 178*57129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_chgr", 179*57129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_CHGR0IRQ, 180*57129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MCHGR0IRQ, 181*57129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_chgr, 182*57129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_chgr), 183*57129044SKuppuswamy Sathyanarayanan .num_regs = 2, 184*57129044SKuppuswamy Sathyanarayanan }; 185*57129044SKuppuswamy Sathyanarayanan 186*57129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_crit = { 187*57129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_crit", 188*57129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_CRITIRQ, 189*57129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MCRITIRQ, 190*57129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_crit, 191*57129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_crit), 192*57129044SKuppuswamy Sathyanarayanan .num_regs = 1, 193*57129044SKuppuswamy Sathyanarayanan }; 194*57129044SKuppuswamy Sathyanarayanan 19539d047c0SQipeng Zha static struct resource gpio_resources[] = { 196a1d28c59SKuppuswamy Sathyanarayanan DEFINE_RES_IRQ_NAMED(BXTWC_GPIO_LVL1_IRQ, "GPIO"), 19739d047c0SQipeng Zha }; 19839d047c0SQipeng Zha 19939d047c0SQipeng Zha static struct resource adc_resources[] = { 20039d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"), 20139d047c0SQipeng Zha }; 20239d047c0SQipeng Zha 2039c6235c8SBin Gao static struct resource usbc_resources[] = { 20496007020SHeikki Krogerus DEFINE_RES_IRQ(BXTWC_USBC_IRQ), 2059c6235c8SBin Gao }; 2069c6235c8SBin Gao 20739d047c0SQipeng Zha static struct resource charger_resources[] = { 20839d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"), 20939d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"), 21039d047c0SQipeng Zha }; 21139d047c0SQipeng Zha 21239d047c0SQipeng Zha static struct resource thermal_resources[] = { 213c4949630SKuppuswamy Sathyanarayanan DEFINE_RES_IRQ(BXTWC_THRM_LVL1_IRQ), 21439d047c0SQipeng Zha }; 21539d047c0SQipeng Zha 21639d047c0SQipeng Zha static struct resource bcu_resources[] = { 21739d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"), 21839d047c0SQipeng Zha }; 21939d047c0SQipeng Zha 220957ae509SNilesh Bacchewar static struct resource tmu_resources[] = { 221957ae509SNilesh Bacchewar DEFINE_RES_IRQ_NAMED(BXTWC_TMU_IRQ, "TMU"), 222957ae509SNilesh Bacchewar }; 223957ae509SNilesh Bacchewar 22439d047c0SQipeng Zha static struct mfd_cell bxt_wc_dev[] = { 22539d047c0SQipeng Zha { 22639d047c0SQipeng Zha .name = "bxt_wcove_gpadc", 22739d047c0SQipeng Zha .num_resources = ARRAY_SIZE(adc_resources), 22839d047c0SQipeng Zha .resources = adc_resources, 22939d047c0SQipeng Zha }, 23039d047c0SQipeng Zha { 23139d047c0SQipeng Zha .name = "bxt_wcove_thermal", 23239d047c0SQipeng Zha .num_resources = ARRAY_SIZE(thermal_resources), 23339d047c0SQipeng Zha .resources = thermal_resources, 23439d047c0SQipeng Zha }, 23539d047c0SQipeng Zha { 2369c6235c8SBin Gao .name = "bxt_wcove_usbc", 2379c6235c8SBin Gao .num_resources = ARRAY_SIZE(usbc_resources), 2389c6235c8SBin Gao .resources = usbc_resources, 2399c6235c8SBin Gao }, 2409c6235c8SBin Gao { 24139d047c0SQipeng Zha .name = "bxt_wcove_ext_charger", 24239d047c0SQipeng Zha .num_resources = ARRAY_SIZE(charger_resources), 24339d047c0SQipeng Zha .resources = charger_resources, 24439d047c0SQipeng Zha }, 24539d047c0SQipeng Zha { 24639d047c0SQipeng Zha .name = "bxt_wcove_bcu", 24739d047c0SQipeng Zha .num_resources = ARRAY_SIZE(bcu_resources), 24839d047c0SQipeng Zha .resources = bcu_resources, 24939d047c0SQipeng Zha }, 25039d047c0SQipeng Zha { 251957ae509SNilesh Bacchewar .name = "bxt_wcove_tmu", 252957ae509SNilesh Bacchewar .num_resources = ARRAY_SIZE(tmu_resources), 253957ae509SNilesh Bacchewar .resources = tmu_resources, 254957ae509SNilesh Bacchewar }, 255957ae509SNilesh Bacchewar 256957ae509SNilesh Bacchewar { 25739d047c0SQipeng Zha .name = "bxt_wcove_gpio", 25839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(gpio_resources), 25939d047c0SQipeng Zha .resources = gpio_resources, 26039d047c0SQipeng Zha }, 26139d047c0SQipeng Zha { 26239d047c0SQipeng Zha .name = "bxt_wcove_region", 26339d047c0SQipeng Zha }, 26439d047c0SQipeng Zha }; 26539d047c0SQipeng Zha 26639d047c0SQipeng Zha static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, 26739d047c0SQipeng Zha unsigned int *val) 26839d047c0SQipeng Zha { 26939d047c0SQipeng Zha int ret; 27039d047c0SQipeng Zha int i2c_addr; 27139d047c0SQipeng Zha u8 ipc_in[2]; 27239d047c0SQipeng Zha u8 ipc_out[4]; 27339d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 27439d047c0SQipeng Zha 275b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 276b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 277b4ccc4d2SKuppuswamy Sathyanarayanan 27839d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 27939d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 280b4ccc4d2SKuppuswamy Sathyanarayanan else 28139d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 282b4ccc4d2SKuppuswamy Sathyanarayanan 28339d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 28439d047c0SQipeng Zha 28539d047c0SQipeng Zha ipc_in[0] = reg; 28639d047c0SQipeng Zha ipc_in[1] = i2c_addr; 28739d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 28839d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_READ, 28939d047c0SQipeng Zha ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); 29039d047c0SQipeng Zha if (ret) { 29139d047c0SQipeng Zha dev_err(pmic->dev, "Failed to read from PMIC\n"); 29239d047c0SQipeng Zha return ret; 29339d047c0SQipeng Zha } 29439d047c0SQipeng Zha *val = ipc_out[0]; 29539d047c0SQipeng Zha 29639d047c0SQipeng Zha return 0; 29739d047c0SQipeng Zha } 29839d047c0SQipeng Zha 29939d047c0SQipeng Zha static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, 30039d047c0SQipeng Zha unsigned int val) 30139d047c0SQipeng Zha { 30239d047c0SQipeng Zha int ret; 30339d047c0SQipeng Zha int i2c_addr; 30439d047c0SQipeng Zha u8 ipc_in[3]; 30539d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 30639d047c0SQipeng Zha 307b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 308b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 309b4ccc4d2SKuppuswamy Sathyanarayanan 31039d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 31139d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 312b4ccc4d2SKuppuswamy Sathyanarayanan else 31339d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 314b4ccc4d2SKuppuswamy Sathyanarayanan 31539d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 31639d047c0SQipeng Zha 31739d047c0SQipeng Zha ipc_in[0] = reg; 31839d047c0SQipeng Zha ipc_in[1] = i2c_addr; 31939d047c0SQipeng Zha ipc_in[2] = val; 32039d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 32139d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_WRITE, 32239d047c0SQipeng Zha ipc_in, sizeof(ipc_in), NULL, 0); 32339d047c0SQipeng Zha if (ret) { 32439d047c0SQipeng Zha dev_err(pmic->dev, "Failed to write to PMIC\n"); 32539d047c0SQipeng Zha return ret; 32639d047c0SQipeng Zha } 32739d047c0SQipeng Zha 32839d047c0SQipeng Zha return 0; 32939d047c0SQipeng Zha } 33039d047c0SQipeng Zha 33139d047c0SQipeng Zha /* sysfs interfaces to r/w PMIC registers, required by initial script */ 33239d047c0SQipeng Zha static unsigned long bxtwc_reg_addr; 33339d047c0SQipeng Zha static ssize_t bxtwc_reg_show(struct device *dev, 33439d047c0SQipeng Zha struct device_attribute *attr, char *buf) 33539d047c0SQipeng Zha { 33639d047c0SQipeng Zha return sprintf(buf, "0x%lx\n", bxtwc_reg_addr); 33739d047c0SQipeng Zha } 33839d047c0SQipeng Zha 33939d047c0SQipeng Zha static ssize_t bxtwc_reg_store(struct device *dev, 34039d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 34139d047c0SQipeng Zha { 34239d047c0SQipeng Zha if (kstrtoul(buf, 0, &bxtwc_reg_addr)) { 34339d047c0SQipeng Zha dev_err(dev, "Invalid register address\n"); 34439d047c0SQipeng Zha return -EINVAL; 34539d047c0SQipeng Zha } 34639d047c0SQipeng Zha return (ssize_t)count; 34739d047c0SQipeng Zha } 34839d047c0SQipeng Zha 34939d047c0SQipeng Zha static ssize_t bxtwc_val_show(struct device *dev, 35039d047c0SQipeng Zha struct device_attribute *attr, char *buf) 35139d047c0SQipeng Zha { 35239d047c0SQipeng Zha int ret; 35339d047c0SQipeng Zha unsigned int val; 35439d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 35539d047c0SQipeng Zha 35639d047c0SQipeng Zha ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val); 35739d047c0SQipeng Zha if (ret < 0) { 35839d047c0SQipeng Zha dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr); 35939d047c0SQipeng Zha return -EIO; 36039d047c0SQipeng Zha } 36139d047c0SQipeng Zha 36239d047c0SQipeng Zha return sprintf(buf, "0x%02x\n", val); 36339d047c0SQipeng Zha } 36439d047c0SQipeng Zha 36539d047c0SQipeng Zha static ssize_t bxtwc_val_store(struct device *dev, 36639d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 36739d047c0SQipeng Zha { 36839d047c0SQipeng Zha int ret; 36939d047c0SQipeng Zha unsigned int val; 37039d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 37139d047c0SQipeng Zha 372f3a654c5SDan Carpenter ret = kstrtouint(buf, 0, &val); 373f3a654c5SDan Carpenter if (ret) 374f3a654c5SDan Carpenter return ret; 37539d047c0SQipeng Zha 37639d047c0SQipeng Zha ret = regmap_write(pmic->regmap, bxtwc_reg_addr, val); 37739d047c0SQipeng Zha if (ret) { 37839d047c0SQipeng Zha dev_err(dev, "Failed to write value 0x%02x to address 0x%lx", 37939d047c0SQipeng Zha val, bxtwc_reg_addr); 38039d047c0SQipeng Zha return -EIO; 38139d047c0SQipeng Zha } 38239d047c0SQipeng Zha return count; 38339d047c0SQipeng Zha } 38439d047c0SQipeng Zha 38539d047c0SQipeng Zha static DEVICE_ATTR(addr, S_IWUSR | S_IRUSR, bxtwc_reg_show, bxtwc_reg_store); 38639d047c0SQipeng Zha static DEVICE_ATTR(val, S_IWUSR | S_IRUSR, bxtwc_val_show, bxtwc_val_store); 38739d047c0SQipeng Zha static struct attribute *bxtwc_attrs[] = { 38839d047c0SQipeng Zha &dev_attr_addr.attr, 38939d047c0SQipeng Zha &dev_attr_val.attr, 39039d047c0SQipeng Zha NULL 39139d047c0SQipeng Zha }; 39239d047c0SQipeng Zha 39339d047c0SQipeng Zha static const struct attribute_group bxtwc_group = { 39439d047c0SQipeng Zha .attrs = bxtwc_attrs, 39539d047c0SQipeng Zha }; 39639d047c0SQipeng Zha 39739d047c0SQipeng Zha static const struct regmap_config bxtwc_regmap_config = { 39839d047c0SQipeng Zha .reg_bits = 16, 39939d047c0SQipeng Zha .val_bits = 8, 40039d047c0SQipeng Zha .reg_write = regmap_ipc_byte_reg_write, 40139d047c0SQipeng Zha .reg_read = regmap_ipc_byte_reg_read, 40239d047c0SQipeng Zha }; 40339d047c0SQipeng Zha 404*57129044SKuppuswamy Sathyanarayanan static int bxtwc_add_chained_irq_chip(struct intel_soc_pmic *pmic, 405*57129044SKuppuswamy Sathyanarayanan struct regmap_irq_chip_data *pdata, 406*57129044SKuppuswamy Sathyanarayanan int pirq, int irq_flags, 407*57129044SKuppuswamy Sathyanarayanan const struct regmap_irq_chip *chip, 408*57129044SKuppuswamy Sathyanarayanan struct regmap_irq_chip_data **data) 409*57129044SKuppuswamy Sathyanarayanan { 410*57129044SKuppuswamy Sathyanarayanan int irq; 411*57129044SKuppuswamy Sathyanarayanan 412*57129044SKuppuswamy Sathyanarayanan irq = regmap_irq_get_virq(pdata, pirq); 413*57129044SKuppuswamy Sathyanarayanan if (irq < 0) { 414*57129044SKuppuswamy Sathyanarayanan dev_err(pmic->dev, 415*57129044SKuppuswamy Sathyanarayanan "Failed to get parent vIRQ(%d) for chip %s, ret:%d\n", 416*57129044SKuppuswamy Sathyanarayanan pirq, chip->name, irq); 417*57129044SKuppuswamy Sathyanarayanan return irq; 418*57129044SKuppuswamy Sathyanarayanan } 419*57129044SKuppuswamy Sathyanarayanan 420*57129044SKuppuswamy Sathyanarayanan return devm_regmap_add_irq_chip(pmic->dev, pmic->regmap, irq, irq_flags, 421*57129044SKuppuswamy Sathyanarayanan 0, chip, data); 422*57129044SKuppuswamy Sathyanarayanan } 423*57129044SKuppuswamy Sathyanarayanan 42439d047c0SQipeng Zha static int bxtwc_probe(struct platform_device *pdev) 42539d047c0SQipeng Zha { 42639d047c0SQipeng Zha int ret; 42739d047c0SQipeng Zha acpi_handle handle; 42839d047c0SQipeng Zha acpi_status status; 42939d047c0SQipeng Zha unsigned long long hrv; 43039d047c0SQipeng Zha struct intel_soc_pmic *pmic; 43139d047c0SQipeng Zha 43239d047c0SQipeng Zha handle = ACPI_HANDLE(&pdev->dev); 43339d047c0SQipeng Zha status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); 43439d047c0SQipeng Zha if (ACPI_FAILURE(status)) { 43539d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n"); 43639d047c0SQipeng Zha return -ENODEV; 43739d047c0SQipeng Zha } 43839d047c0SQipeng Zha if (hrv != BROXTON_PMIC_WC_HRV) { 43939d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n", 44039d047c0SQipeng Zha hrv); 44139d047c0SQipeng Zha return -ENODEV; 44239d047c0SQipeng Zha } 44339d047c0SQipeng Zha 44439d047c0SQipeng Zha pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); 44539d047c0SQipeng Zha if (!pmic) 44639d047c0SQipeng Zha return -ENOMEM; 44739d047c0SQipeng Zha 44839d047c0SQipeng Zha ret = platform_get_irq(pdev, 0); 44939d047c0SQipeng Zha if (ret < 0) { 45039d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid IRQ\n"); 45139d047c0SQipeng Zha return ret; 45239d047c0SQipeng Zha } 45339d047c0SQipeng Zha pmic->irq = ret; 45439d047c0SQipeng Zha 45539d047c0SQipeng Zha dev_set_drvdata(&pdev->dev, pmic); 45639d047c0SQipeng Zha pmic->dev = &pdev->dev; 45739d047c0SQipeng Zha 45839d047c0SQipeng Zha pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, 45939d047c0SQipeng Zha &bxtwc_regmap_config); 46039d047c0SQipeng Zha if (IS_ERR(pmic->regmap)) { 46139d047c0SQipeng Zha ret = PTR_ERR(pmic->regmap); 46239d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret); 46339d047c0SQipeng Zha return ret; 46439d047c0SQipeng Zha } 46539d047c0SQipeng Zha 4665131f072SKuppuswamy Sathyanarayanan ret = devm_regmap_add_irq_chip(&pdev->dev, pmic->regmap, pmic->irq, 46739d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 46839d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip, 46939d047c0SQipeng Zha &pmic->irq_chip_data); 47039d047c0SQipeng Zha if (ret) { 47139d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add IRQ chip\n"); 47239d047c0SQipeng Zha return ret; 47339d047c0SQipeng Zha } 47439d047c0SQipeng Zha 475*57129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 476*57129044SKuppuswamy Sathyanarayanan BXTWC_TMU_LVL1_IRQ, 477*57129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 478*57129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_tmu, 479957ae509SNilesh Bacchewar &pmic->irq_chip_data_tmu); 480957ae509SNilesh Bacchewar if (ret) { 481957ae509SNilesh Bacchewar dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); 4825131f072SKuppuswamy Sathyanarayanan return ret; 483957ae509SNilesh Bacchewar } 484957ae509SNilesh Bacchewar 485*57129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for BCU IRQs */ 486*57129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 487*57129044SKuppuswamy Sathyanarayanan BXTWC_BCU_LVL1_IRQ, 488*57129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 489*57129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_bcu, 490*57129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_bcu); 491*57129044SKuppuswamy Sathyanarayanan 492*57129044SKuppuswamy Sathyanarayanan 493*57129044SKuppuswamy Sathyanarayanan if (ret) { 494*57129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add BUC IRQ chip\n"); 495*57129044SKuppuswamy Sathyanarayanan return ret; 496*57129044SKuppuswamy Sathyanarayanan } 497*57129044SKuppuswamy Sathyanarayanan 498*57129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for ADC IRQs */ 499*57129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 500*57129044SKuppuswamy Sathyanarayanan BXTWC_ADC_LVL1_IRQ, 501*57129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 502*57129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_adc, 503*57129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_adc); 504*57129044SKuppuswamy Sathyanarayanan 505*57129044SKuppuswamy Sathyanarayanan 506*57129044SKuppuswamy Sathyanarayanan if (ret) { 507*57129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add ADC IRQ chip\n"); 508*57129044SKuppuswamy Sathyanarayanan return ret; 509*57129044SKuppuswamy Sathyanarayanan } 510*57129044SKuppuswamy Sathyanarayanan 511*57129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for CHGR IRQs */ 512*57129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 513*57129044SKuppuswamy Sathyanarayanan BXTWC_CHGR_LVL1_IRQ, 514*57129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 515*57129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_chgr, 516*57129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_chgr); 517*57129044SKuppuswamy Sathyanarayanan 518*57129044SKuppuswamy Sathyanarayanan 519*57129044SKuppuswamy Sathyanarayanan if (ret) { 520*57129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add CHGR IRQ chip\n"); 521*57129044SKuppuswamy Sathyanarayanan return ret; 522*57129044SKuppuswamy Sathyanarayanan } 523*57129044SKuppuswamy Sathyanarayanan 524*57129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for CRIT IRQs */ 525*57129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 526*57129044SKuppuswamy Sathyanarayanan BXTWC_CRIT_LVL1_IRQ, 527*57129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 528*57129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_crit, 529*57129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_crit); 530*57129044SKuppuswamy Sathyanarayanan 531*57129044SKuppuswamy Sathyanarayanan 532*57129044SKuppuswamy Sathyanarayanan if (ret) { 533*57129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add CRIT IRQ chip\n"); 534*57129044SKuppuswamy Sathyanarayanan return ret; 535*57129044SKuppuswamy Sathyanarayanan } 536*57129044SKuppuswamy Sathyanarayanan 5375131f072SKuppuswamy Sathyanarayanan ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, 5385131f072SKuppuswamy Sathyanarayanan ARRAY_SIZE(bxt_wc_dev), NULL, 0, NULL); 53939d047c0SQipeng Zha if (ret) { 54039d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add devices\n"); 5415131f072SKuppuswamy Sathyanarayanan return ret; 54239d047c0SQipeng Zha } 54339d047c0SQipeng Zha 54439d047c0SQipeng Zha ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group); 54539d047c0SQipeng Zha if (ret) { 54639d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret); 5475131f072SKuppuswamy Sathyanarayanan return ret; 54839d047c0SQipeng Zha } 54939d047c0SQipeng Zha 5509c6235c8SBin Gao /* 5519c6235c8SBin Gao * There is known hw bug. Upon reset BIT 5 of register 5529c6235c8SBin Gao * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, 5539c6235c8SBin Gao * later it's set to 1(masked) automatically by hardware. So we 5549c6235c8SBin Gao * have the software workaround here to unmaksed it in order to let 5559c6235c8SBin Gao * charger interrutp work. 5569c6235c8SBin Gao */ 5579c6235c8SBin Gao regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, 5589c6235c8SBin Gao BXTWC_MIRQLVL1_MCHGR, 0); 5599c6235c8SBin Gao 56039d047c0SQipeng Zha return 0; 56139d047c0SQipeng Zha } 56239d047c0SQipeng Zha 56339d047c0SQipeng Zha static int bxtwc_remove(struct platform_device *pdev) 56439d047c0SQipeng Zha { 56539d047c0SQipeng Zha sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group); 56639d047c0SQipeng Zha 56739d047c0SQipeng Zha return 0; 56839d047c0SQipeng Zha } 56939d047c0SQipeng Zha 57039d047c0SQipeng Zha static void bxtwc_shutdown(struct platform_device *pdev) 57139d047c0SQipeng Zha { 57239d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 57339d047c0SQipeng Zha 57439d047c0SQipeng Zha disable_irq(pmic->irq); 57539d047c0SQipeng Zha } 57639d047c0SQipeng Zha 57739d047c0SQipeng Zha #ifdef CONFIG_PM_SLEEP 57839d047c0SQipeng Zha static int bxtwc_suspend(struct device *dev) 57939d047c0SQipeng Zha { 58039d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 58139d047c0SQipeng Zha 58239d047c0SQipeng Zha disable_irq(pmic->irq); 58339d047c0SQipeng Zha 58439d047c0SQipeng Zha return 0; 58539d047c0SQipeng Zha } 58639d047c0SQipeng Zha 58739d047c0SQipeng Zha static int bxtwc_resume(struct device *dev) 58839d047c0SQipeng Zha { 58939d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 59039d047c0SQipeng Zha 59139d047c0SQipeng Zha enable_irq(pmic->irq); 59239d047c0SQipeng Zha return 0; 59339d047c0SQipeng Zha } 59439d047c0SQipeng Zha #endif 59539d047c0SQipeng Zha static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); 59639d047c0SQipeng Zha 59739d047c0SQipeng Zha static const struct acpi_device_id bxtwc_acpi_ids[] = { 59839d047c0SQipeng Zha { "INT34D3", }, 59939d047c0SQipeng Zha { } 60039d047c0SQipeng Zha }; 601f57576e7SWei Yongjun MODULE_DEVICE_TABLE(acpi, bxtwc_acpi_ids); 60239d047c0SQipeng Zha 60339d047c0SQipeng Zha static struct platform_driver bxtwc_driver = { 60439d047c0SQipeng Zha .probe = bxtwc_probe, 60539d047c0SQipeng Zha .remove = bxtwc_remove, 60639d047c0SQipeng Zha .shutdown = bxtwc_shutdown, 60739d047c0SQipeng Zha .driver = { 60839d047c0SQipeng Zha .name = "BXTWC PMIC", 60939d047c0SQipeng Zha .pm = &bxtwc_pm_ops, 61039d047c0SQipeng Zha .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids), 61139d047c0SQipeng Zha }, 61239d047c0SQipeng Zha }; 61339d047c0SQipeng Zha 61439d047c0SQipeng Zha module_platform_driver(bxtwc_driver); 61539d047c0SQipeng Zha 61639d047c0SQipeng Zha MODULE_LICENSE("GPL v2"); 61739d047c0SQipeng Zha MODULE_AUTHOR("Qipeng Zha<qipeng.zha@intel.com>"); 618