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 35*9f8ddee1SAndy Shevchenko #define BXTWC_PWRBTNIRQ 0x4E03 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 509c6235c8SBin Gao #define BXTWC_MIRQLVL1_MCHGR BIT(5) 519c6235c8SBin Gao 52*9f8ddee1SAndy Shevchenko #define BXTWC_MPWRBTNIRQ 0x4E0F 5339d047c0SQipeng Zha #define BXTWC_MTHRM0IRQ 0x4E12 5439d047c0SQipeng Zha #define BXTWC_MTHRM1IRQ 0x4E13 5539d047c0SQipeng Zha #define BXTWC_MTHRM2IRQ 0x4E14 5639d047c0SQipeng Zha #define BXTWC_MBCUIRQ 0x4E15 5739d047c0SQipeng Zha #define BXTWC_MADCIRQ 0x4E16 5839d047c0SQipeng Zha #define BXTWC_MCHGR0IRQ 0x4E17 5939d047c0SQipeng Zha #define BXTWC_MCHGR1IRQ 0x4E18 6039d047c0SQipeng Zha #define BXTWC_MGPIO0IRQ 0x4E19 6139d047c0SQipeng Zha #define BXTWC_MGPIO1IRQ 0x4E1A 6239d047c0SQipeng Zha #define BXTWC_MCRITIRQ 0x4E1B 63957ae509SNilesh Bacchewar #define BXTWC_MTMUIRQ 0x4FB7 6439d047c0SQipeng Zha 6539d047c0SQipeng Zha /* Whiskey Cove PMIC share same ACPI ID between different platforms */ 6639d047c0SQipeng Zha #define BROXTON_PMIC_WC_HRV 4 6739d047c0SQipeng Zha 6839d047c0SQipeng Zha enum bxtwc_irqs { 6939d047c0SQipeng Zha BXTWC_PWRBTN_LVL1_IRQ = 0, 7039d047c0SQipeng Zha BXTWC_TMU_LVL1_IRQ, 7139d047c0SQipeng Zha BXTWC_THRM_LVL1_IRQ, 7239d047c0SQipeng Zha BXTWC_BCU_LVL1_IRQ, 7339d047c0SQipeng Zha BXTWC_ADC_LVL1_IRQ, 7439d047c0SQipeng Zha BXTWC_CHGR_LVL1_IRQ, 7539d047c0SQipeng Zha BXTWC_GPIO_LVL1_IRQ, 7639d047c0SQipeng Zha BXTWC_CRIT_LVL1_IRQ, 77*9f8ddee1SAndy Shevchenko }; 7839d047c0SQipeng Zha 79*9f8ddee1SAndy Shevchenko enum bxtwc_irqs_pwrbtn { 80*9f8ddee1SAndy Shevchenko BXTWC_PWRBTN_IRQ = 0, 81*9f8ddee1SAndy Shevchenko BXTWC_UIBTN_IRQ, 8239d047c0SQipeng Zha }; 8339d047c0SQipeng Zha 8457129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_bcu { 85c4949630SKuppuswamy Sathyanarayanan BXTWC_BCU_IRQ = 0, 8657129044SKuppuswamy Sathyanarayanan }; 8757129044SKuppuswamy Sathyanarayanan 8857129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_adc { 8957129044SKuppuswamy Sathyanarayanan BXTWC_ADC_IRQ = 0, 9057129044SKuppuswamy Sathyanarayanan }; 9157129044SKuppuswamy Sathyanarayanan 9257129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_chgr { 9357129044SKuppuswamy Sathyanarayanan BXTWC_USBC_IRQ = 0, 9439d047c0SQipeng Zha BXTWC_CHGR0_IRQ, 9539d047c0SQipeng Zha BXTWC_CHGR1_IRQ, 964533d855SKuppuswamy Sathyanarayanan }; 974533d855SKuppuswamy Sathyanarayanan 984533d855SKuppuswamy Sathyanarayanan enum bxtwc_irqs_tmu { 994533d855SKuppuswamy Sathyanarayanan BXTWC_TMU_IRQ = 0, 10039d047c0SQipeng Zha }; 10139d047c0SQipeng Zha 10257129044SKuppuswamy Sathyanarayanan enum bxtwc_irqs_crit { 10357129044SKuppuswamy Sathyanarayanan BXTWC_CRIT_IRQ = 0, 10457129044SKuppuswamy Sathyanarayanan }; 10557129044SKuppuswamy Sathyanarayanan 10639d047c0SQipeng Zha static const struct regmap_irq bxtwc_regmap_irqs[] = { 10739d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_PWRBTN_LVL1_IRQ, 0, BIT(0)), 10839d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_TMU_LVL1_IRQ, 0, BIT(1)), 10939d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_THRM_LVL1_IRQ, 0, BIT(2)), 11039d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_BCU_LVL1_IRQ, 0, BIT(3)), 11139d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_ADC_LVL1_IRQ, 0, BIT(4)), 11239d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CHGR_LVL1_IRQ, 0, BIT(5)), 11339d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_GPIO_LVL1_IRQ, 0, BIT(6)), 11439d047c0SQipeng Zha REGMAP_IRQ_REG(BXTWC_CRIT_LVL1_IRQ, 0, BIT(7)), 115*9f8ddee1SAndy Shevchenko }; 116*9f8ddee1SAndy Shevchenko 117*9f8ddee1SAndy Shevchenko static const struct regmap_irq bxtwc_regmap_irqs_pwrbtn[] = { 118*9f8ddee1SAndy Shevchenko REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 0, 0x01), 11939d047c0SQipeng Zha }; 12039d047c0SQipeng Zha 12157129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_bcu[] = { 122c4949630SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 0, 0x1f), 12357129044SKuppuswamy Sathyanarayanan }; 12457129044SKuppuswamy Sathyanarayanan 12557129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_adc[] = { 12657129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 0, 0xff), 12757129044SKuppuswamy Sathyanarayanan }; 12857129044SKuppuswamy Sathyanarayanan 12957129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_chgr[] = { 130*9f8ddee1SAndy Shevchenko REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, 0x20), 13157129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 0, 0x1f), 13257129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 1, 0x1f), 13339d047c0SQipeng Zha }; 13439d047c0SQipeng Zha 135957ae509SNilesh Bacchewar static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { 136957ae509SNilesh Bacchewar REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), 137957ae509SNilesh Bacchewar }; 138957ae509SNilesh Bacchewar 13957129044SKuppuswamy Sathyanarayanan static const struct regmap_irq bxtwc_regmap_irqs_crit[] = { 14057129044SKuppuswamy Sathyanarayanan REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 0, 0x03), 14157129044SKuppuswamy Sathyanarayanan }; 14257129044SKuppuswamy Sathyanarayanan 14339d047c0SQipeng Zha static struct regmap_irq_chip bxtwc_regmap_irq_chip = { 14439d047c0SQipeng Zha .name = "bxtwc_irq_chip", 14539d047c0SQipeng Zha .status_base = BXTWC_IRQLVL1, 14639d047c0SQipeng Zha .mask_base = BXTWC_MIRQLVL1, 14739d047c0SQipeng Zha .irqs = bxtwc_regmap_irqs, 14839d047c0SQipeng Zha .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs), 149*9f8ddee1SAndy Shevchenko .num_regs = 1, 150*9f8ddee1SAndy Shevchenko }; 151*9f8ddee1SAndy Shevchenko 152*9f8ddee1SAndy Shevchenko static struct regmap_irq_chip bxtwc_regmap_irq_chip_pwrbtn = { 153*9f8ddee1SAndy Shevchenko .name = "bxtwc_irq_chip_pwrbtn", 154*9f8ddee1SAndy Shevchenko .status_base = BXTWC_PWRBTNIRQ, 155*9f8ddee1SAndy Shevchenko .mask_base = BXTWC_MPWRBTNIRQ, 156*9f8ddee1SAndy Shevchenko .irqs = bxtwc_regmap_irqs_pwrbtn, 157*9f8ddee1SAndy Shevchenko .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_pwrbtn), 158*9f8ddee1SAndy Shevchenko .num_regs = 1, 15939d047c0SQipeng Zha }; 16039d047c0SQipeng Zha 161957ae509SNilesh Bacchewar static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = { 162957ae509SNilesh Bacchewar .name = "bxtwc_irq_chip_tmu", 163957ae509SNilesh Bacchewar .status_base = BXTWC_TMUIRQ, 164957ae509SNilesh Bacchewar .mask_base = BXTWC_MTMUIRQ, 165957ae509SNilesh Bacchewar .irqs = bxtwc_regmap_irqs_tmu, 166957ae509SNilesh Bacchewar .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_tmu), 167957ae509SNilesh Bacchewar .num_regs = 1, 168957ae509SNilesh Bacchewar }; 169957ae509SNilesh Bacchewar 17057129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_bcu = { 17157129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_bcu", 17257129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_BCUIRQ, 17357129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MBCUIRQ, 17457129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_bcu, 17557129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_bcu), 17657129044SKuppuswamy Sathyanarayanan .num_regs = 1, 17757129044SKuppuswamy Sathyanarayanan }; 17857129044SKuppuswamy Sathyanarayanan 17957129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_adc = { 18057129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_adc", 18157129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_ADCIRQ, 18257129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MADCIRQ, 18357129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_adc, 18457129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_adc), 18557129044SKuppuswamy Sathyanarayanan .num_regs = 1, 18657129044SKuppuswamy Sathyanarayanan }; 18757129044SKuppuswamy Sathyanarayanan 18857129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_chgr = { 18957129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_chgr", 19057129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_CHGR0IRQ, 19157129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MCHGR0IRQ, 19257129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_chgr, 19357129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_chgr), 19457129044SKuppuswamy Sathyanarayanan .num_regs = 2, 19557129044SKuppuswamy Sathyanarayanan }; 19657129044SKuppuswamy Sathyanarayanan 19757129044SKuppuswamy Sathyanarayanan static struct regmap_irq_chip bxtwc_regmap_irq_chip_crit = { 19857129044SKuppuswamy Sathyanarayanan .name = "bxtwc_irq_chip_crit", 19957129044SKuppuswamy Sathyanarayanan .status_base = BXTWC_CRITIRQ, 20057129044SKuppuswamy Sathyanarayanan .mask_base = BXTWC_MCRITIRQ, 20157129044SKuppuswamy Sathyanarayanan .irqs = bxtwc_regmap_irqs_crit, 20257129044SKuppuswamy Sathyanarayanan .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_crit), 20357129044SKuppuswamy Sathyanarayanan .num_regs = 1, 20457129044SKuppuswamy Sathyanarayanan }; 20557129044SKuppuswamy Sathyanarayanan 20639d047c0SQipeng Zha static struct resource gpio_resources[] = { 207a1d28c59SKuppuswamy Sathyanarayanan DEFINE_RES_IRQ_NAMED(BXTWC_GPIO_LVL1_IRQ, "GPIO"), 20839d047c0SQipeng Zha }; 20939d047c0SQipeng Zha 21039d047c0SQipeng Zha static struct resource adc_resources[] = { 21139d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"), 21239d047c0SQipeng Zha }; 21339d047c0SQipeng Zha 2149c6235c8SBin Gao static struct resource usbc_resources[] = { 21596007020SHeikki Krogerus DEFINE_RES_IRQ(BXTWC_USBC_IRQ), 2169c6235c8SBin Gao }; 2179c6235c8SBin Gao 21839d047c0SQipeng Zha static struct resource charger_resources[] = { 21939d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"), 22039d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"), 22139d047c0SQipeng Zha }; 22239d047c0SQipeng Zha 22339d047c0SQipeng Zha static struct resource thermal_resources[] = { 224c4949630SKuppuswamy Sathyanarayanan DEFINE_RES_IRQ(BXTWC_THRM_LVL1_IRQ), 22539d047c0SQipeng Zha }; 22639d047c0SQipeng Zha 22739d047c0SQipeng Zha static struct resource bcu_resources[] = { 22839d047c0SQipeng Zha DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"), 22939d047c0SQipeng Zha }; 23039d047c0SQipeng Zha 231957ae509SNilesh Bacchewar static struct resource tmu_resources[] = { 232957ae509SNilesh Bacchewar DEFINE_RES_IRQ_NAMED(BXTWC_TMU_IRQ, "TMU"), 233957ae509SNilesh Bacchewar }; 234957ae509SNilesh Bacchewar 23539d047c0SQipeng Zha static struct mfd_cell bxt_wc_dev[] = { 23639d047c0SQipeng Zha { 23739d047c0SQipeng Zha .name = "bxt_wcove_gpadc", 23839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(adc_resources), 23939d047c0SQipeng Zha .resources = adc_resources, 24039d047c0SQipeng Zha }, 24139d047c0SQipeng Zha { 24239d047c0SQipeng Zha .name = "bxt_wcove_thermal", 24339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(thermal_resources), 24439d047c0SQipeng Zha .resources = thermal_resources, 24539d047c0SQipeng Zha }, 24639d047c0SQipeng Zha { 2479c6235c8SBin Gao .name = "bxt_wcove_usbc", 2489c6235c8SBin Gao .num_resources = ARRAY_SIZE(usbc_resources), 2499c6235c8SBin Gao .resources = usbc_resources, 2509c6235c8SBin Gao }, 2519c6235c8SBin Gao { 25239d047c0SQipeng Zha .name = "bxt_wcove_ext_charger", 25339d047c0SQipeng Zha .num_resources = ARRAY_SIZE(charger_resources), 25439d047c0SQipeng Zha .resources = charger_resources, 25539d047c0SQipeng Zha }, 25639d047c0SQipeng Zha { 25739d047c0SQipeng Zha .name = "bxt_wcove_bcu", 25839d047c0SQipeng Zha .num_resources = ARRAY_SIZE(bcu_resources), 25939d047c0SQipeng Zha .resources = bcu_resources, 26039d047c0SQipeng Zha }, 26139d047c0SQipeng Zha { 262957ae509SNilesh Bacchewar .name = "bxt_wcove_tmu", 263957ae509SNilesh Bacchewar .num_resources = ARRAY_SIZE(tmu_resources), 264957ae509SNilesh Bacchewar .resources = tmu_resources, 265957ae509SNilesh Bacchewar }, 266957ae509SNilesh Bacchewar 267957ae509SNilesh Bacchewar { 26839d047c0SQipeng Zha .name = "bxt_wcove_gpio", 26939d047c0SQipeng Zha .num_resources = ARRAY_SIZE(gpio_resources), 27039d047c0SQipeng Zha .resources = gpio_resources, 27139d047c0SQipeng Zha }, 27239d047c0SQipeng Zha { 27339d047c0SQipeng Zha .name = "bxt_wcove_region", 27439d047c0SQipeng Zha }, 27539d047c0SQipeng Zha }; 27639d047c0SQipeng Zha 27739d047c0SQipeng Zha static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, 27839d047c0SQipeng Zha unsigned int *val) 27939d047c0SQipeng Zha { 28039d047c0SQipeng Zha int ret; 28139d047c0SQipeng Zha int i2c_addr; 28239d047c0SQipeng Zha u8 ipc_in[2]; 28339d047c0SQipeng Zha u8 ipc_out[4]; 28439d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 28539d047c0SQipeng Zha 286b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 287b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 288b4ccc4d2SKuppuswamy Sathyanarayanan 28939d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 29039d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 291b4ccc4d2SKuppuswamy Sathyanarayanan else 29239d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 293b4ccc4d2SKuppuswamy Sathyanarayanan 29439d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 29539d047c0SQipeng Zha 29639d047c0SQipeng Zha ipc_in[0] = reg; 29739d047c0SQipeng Zha ipc_in[1] = i2c_addr; 29839d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 29939d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_READ, 30039d047c0SQipeng Zha ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); 30139d047c0SQipeng Zha if (ret) { 30239d047c0SQipeng Zha dev_err(pmic->dev, "Failed to read from PMIC\n"); 30339d047c0SQipeng Zha return ret; 30439d047c0SQipeng Zha } 30539d047c0SQipeng Zha *val = ipc_out[0]; 30639d047c0SQipeng Zha 30739d047c0SQipeng Zha return 0; 30839d047c0SQipeng Zha } 30939d047c0SQipeng Zha 31039d047c0SQipeng Zha static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, 31139d047c0SQipeng Zha unsigned int val) 31239d047c0SQipeng Zha { 31339d047c0SQipeng Zha int ret; 31439d047c0SQipeng Zha int i2c_addr; 31539d047c0SQipeng Zha u8 ipc_in[3]; 31639d047c0SQipeng Zha struct intel_soc_pmic *pmic = context; 31739d047c0SQipeng Zha 318b4ccc4d2SKuppuswamy Sathyanarayanan if (!pmic) 319b4ccc4d2SKuppuswamy Sathyanarayanan return -EINVAL; 320b4ccc4d2SKuppuswamy Sathyanarayanan 32139d047c0SQipeng Zha if (reg & REG_ADDR_MASK) 32239d047c0SQipeng Zha i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; 323b4ccc4d2SKuppuswamy Sathyanarayanan else 32439d047c0SQipeng Zha i2c_addr = BXTWC_DEVICE1_ADDR; 325b4ccc4d2SKuppuswamy Sathyanarayanan 32639d047c0SQipeng Zha reg &= REG_OFFSET_MASK; 32739d047c0SQipeng Zha 32839d047c0SQipeng Zha ipc_in[0] = reg; 32939d047c0SQipeng Zha ipc_in[1] = i2c_addr; 33039d047c0SQipeng Zha ipc_in[2] = val; 33139d047c0SQipeng Zha ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, 33239d047c0SQipeng Zha PMC_IPC_PMIC_ACCESS_WRITE, 33339d047c0SQipeng Zha ipc_in, sizeof(ipc_in), NULL, 0); 33439d047c0SQipeng Zha if (ret) { 33539d047c0SQipeng Zha dev_err(pmic->dev, "Failed to write to PMIC\n"); 33639d047c0SQipeng Zha return ret; 33739d047c0SQipeng Zha } 33839d047c0SQipeng Zha 33939d047c0SQipeng Zha return 0; 34039d047c0SQipeng Zha } 34139d047c0SQipeng Zha 34239d047c0SQipeng Zha /* sysfs interfaces to r/w PMIC registers, required by initial script */ 34339d047c0SQipeng Zha static unsigned long bxtwc_reg_addr; 34439d047c0SQipeng Zha static ssize_t bxtwc_reg_show(struct device *dev, 34539d047c0SQipeng Zha struct device_attribute *attr, char *buf) 34639d047c0SQipeng Zha { 34739d047c0SQipeng Zha return sprintf(buf, "0x%lx\n", bxtwc_reg_addr); 34839d047c0SQipeng Zha } 34939d047c0SQipeng Zha 35039d047c0SQipeng Zha static ssize_t bxtwc_reg_store(struct device *dev, 35139d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 35239d047c0SQipeng Zha { 35339d047c0SQipeng Zha if (kstrtoul(buf, 0, &bxtwc_reg_addr)) { 35439d047c0SQipeng Zha dev_err(dev, "Invalid register address\n"); 35539d047c0SQipeng Zha return -EINVAL; 35639d047c0SQipeng Zha } 35739d047c0SQipeng Zha return (ssize_t)count; 35839d047c0SQipeng Zha } 35939d047c0SQipeng Zha 36039d047c0SQipeng Zha static ssize_t bxtwc_val_show(struct device *dev, 36139d047c0SQipeng Zha struct device_attribute *attr, char *buf) 36239d047c0SQipeng Zha { 36339d047c0SQipeng Zha int ret; 36439d047c0SQipeng Zha unsigned int val; 36539d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 36639d047c0SQipeng Zha 36739d047c0SQipeng Zha ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val); 36839d047c0SQipeng Zha if (ret < 0) { 36939d047c0SQipeng Zha dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr); 37039d047c0SQipeng Zha return -EIO; 37139d047c0SQipeng Zha } 37239d047c0SQipeng Zha 37339d047c0SQipeng Zha return sprintf(buf, "0x%02x\n", val); 37439d047c0SQipeng Zha } 37539d047c0SQipeng Zha 37639d047c0SQipeng Zha static ssize_t bxtwc_val_store(struct device *dev, 37739d047c0SQipeng Zha struct device_attribute *attr, const char *buf, size_t count) 37839d047c0SQipeng Zha { 37939d047c0SQipeng Zha int ret; 38039d047c0SQipeng Zha unsigned int val; 38139d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 38239d047c0SQipeng Zha 383f3a654c5SDan Carpenter ret = kstrtouint(buf, 0, &val); 384f3a654c5SDan Carpenter if (ret) 385f3a654c5SDan Carpenter return ret; 38639d047c0SQipeng Zha 38739d047c0SQipeng Zha ret = regmap_write(pmic->regmap, bxtwc_reg_addr, val); 38839d047c0SQipeng Zha if (ret) { 38939d047c0SQipeng Zha dev_err(dev, "Failed to write value 0x%02x to address 0x%lx", 39039d047c0SQipeng Zha val, bxtwc_reg_addr); 39139d047c0SQipeng Zha return -EIO; 39239d047c0SQipeng Zha } 39339d047c0SQipeng Zha return count; 39439d047c0SQipeng Zha } 39539d047c0SQipeng Zha 39639d047c0SQipeng Zha static DEVICE_ATTR(addr, S_IWUSR | S_IRUSR, bxtwc_reg_show, bxtwc_reg_store); 39739d047c0SQipeng Zha static DEVICE_ATTR(val, S_IWUSR | S_IRUSR, bxtwc_val_show, bxtwc_val_store); 39839d047c0SQipeng Zha static struct attribute *bxtwc_attrs[] = { 39939d047c0SQipeng Zha &dev_attr_addr.attr, 40039d047c0SQipeng Zha &dev_attr_val.attr, 40139d047c0SQipeng Zha NULL 40239d047c0SQipeng Zha }; 40339d047c0SQipeng Zha 40439d047c0SQipeng Zha static const struct attribute_group bxtwc_group = { 40539d047c0SQipeng Zha .attrs = bxtwc_attrs, 40639d047c0SQipeng Zha }; 40739d047c0SQipeng Zha 40839d047c0SQipeng Zha static const struct regmap_config bxtwc_regmap_config = { 40939d047c0SQipeng Zha .reg_bits = 16, 41039d047c0SQipeng Zha .val_bits = 8, 41139d047c0SQipeng Zha .reg_write = regmap_ipc_byte_reg_write, 41239d047c0SQipeng Zha .reg_read = regmap_ipc_byte_reg_read, 41339d047c0SQipeng Zha }; 41439d047c0SQipeng Zha 41557129044SKuppuswamy Sathyanarayanan static int bxtwc_add_chained_irq_chip(struct intel_soc_pmic *pmic, 41657129044SKuppuswamy Sathyanarayanan struct regmap_irq_chip_data *pdata, 41757129044SKuppuswamy Sathyanarayanan int pirq, int irq_flags, 41857129044SKuppuswamy Sathyanarayanan const struct regmap_irq_chip *chip, 41957129044SKuppuswamy Sathyanarayanan struct regmap_irq_chip_data **data) 42057129044SKuppuswamy Sathyanarayanan { 42157129044SKuppuswamy Sathyanarayanan int irq; 42257129044SKuppuswamy Sathyanarayanan 42357129044SKuppuswamy Sathyanarayanan irq = regmap_irq_get_virq(pdata, pirq); 42457129044SKuppuswamy Sathyanarayanan if (irq < 0) { 42557129044SKuppuswamy Sathyanarayanan dev_err(pmic->dev, 42657129044SKuppuswamy Sathyanarayanan "Failed to get parent vIRQ(%d) for chip %s, ret:%d\n", 42757129044SKuppuswamy Sathyanarayanan pirq, chip->name, irq); 42857129044SKuppuswamy Sathyanarayanan return irq; 42957129044SKuppuswamy Sathyanarayanan } 43057129044SKuppuswamy Sathyanarayanan 43157129044SKuppuswamy Sathyanarayanan return devm_regmap_add_irq_chip(pmic->dev, pmic->regmap, irq, irq_flags, 43257129044SKuppuswamy Sathyanarayanan 0, chip, data); 43357129044SKuppuswamy Sathyanarayanan } 43457129044SKuppuswamy Sathyanarayanan 43539d047c0SQipeng Zha static int bxtwc_probe(struct platform_device *pdev) 43639d047c0SQipeng Zha { 43739d047c0SQipeng Zha int ret; 43839d047c0SQipeng Zha acpi_handle handle; 43939d047c0SQipeng Zha acpi_status status; 44039d047c0SQipeng Zha unsigned long long hrv; 44139d047c0SQipeng Zha struct intel_soc_pmic *pmic; 44239d047c0SQipeng Zha 44339d047c0SQipeng Zha handle = ACPI_HANDLE(&pdev->dev); 44439d047c0SQipeng Zha status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); 44539d047c0SQipeng Zha if (ACPI_FAILURE(status)) { 44639d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n"); 44739d047c0SQipeng Zha return -ENODEV; 44839d047c0SQipeng Zha } 44939d047c0SQipeng Zha if (hrv != BROXTON_PMIC_WC_HRV) { 45039d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n", 45139d047c0SQipeng Zha hrv); 45239d047c0SQipeng Zha return -ENODEV; 45339d047c0SQipeng Zha } 45439d047c0SQipeng Zha 45539d047c0SQipeng Zha pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); 45639d047c0SQipeng Zha if (!pmic) 45739d047c0SQipeng Zha return -ENOMEM; 45839d047c0SQipeng Zha 45939d047c0SQipeng Zha ret = platform_get_irq(pdev, 0); 46039d047c0SQipeng Zha if (ret < 0) { 46139d047c0SQipeng Zha dev_err(&pdev->dev, "Invalid IRQ\n"); 46239d047c0SQipeng Zha return ret; 46339d047c0SQipeng Zha } 46439d047c0SQipeng Zha pmic->irq = ret; 46539d047c0SQipeng Zha 46639d047c0SQipeng Zha dev_set_drvdata(&pdev->dev, pmic); 46739d047c0SQipeng Zha pmic->dev = &pdev->dev; 46839d047c0SQipeng Zha 46939d047c0SQipeng Zha pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, 47039d047c0SQipeng Zha &bxtwc_regmap_config); 47139d047c0SQipeng Zha if (IS_ERR(pmic->regmap)) { 47239d047c0SQipeng Zha ret = PTR_ERR(pmic->regmap); 47339d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret); 47439d047c0SQipeng Zha return ret; 47539d047c0SQipeng Zha } 47639d047c0SQipeng Zha 4775131f072SKuppuswamy Sathyanarayanan ret = devm_regmap_add_irq_chip(&pdev->dev, pmic->regmap, pmic->irq, 47839d047c0SQipeng Zha IRQF_ONESHOT | IRQF_SHARED, 47939d047c0SQipeng Zha 0, &bxtwc_regmap_irq_chip, 48039d047c0SQipeng Zha &pmic->irq_chip_data); 48139d047c0SQipeng Zha if (ret) { 48239d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add IRQ chip\n"); 48339d047c0SQipeng Zha return ret; 48439d047c0SQipeng Zha } 48539d047c0SQipeng Zha 48657129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 487*9f8ddee1SAndy Shevchenko BXTWC_PWRBTN_LVL1_IRQ, 488*9f8ddee1SAndy Shevchenko IRQF_ONESHOT, 489*9f8ddee1SAndy Shevchenko &bxtwc_regmap_irq_chip_pwrbtn, 490*9f8ddee1SAndy Shevchenko &pmic->irq_chip_data_pwrbtn); 491*9f8ddee1SAndy Shevchenko if (ret) { 492*9f8ddee1SAndy Shevchenko dev_err(&pdev->dev, "Failed to add PWRBTN IRQ chip\n"); 493*9f8ddee1SAndy Shevchenko return ret; 494*9f8ddee1SAndy Shevchenko } 495*9f8ddee1SAndy Shevchenko 496*9f8ddee1SAndy Shevchenko ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 49757129044SKuppuswamy Sathyanarayanan BXTWC_TMU_LVL1_IRQ, 49857129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 49957129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_tmu, 500957ae509SNilesh Bacchewar &pmic->irq_chip_data_tmu); 501957ae509SNilesh Bacchewar if (ret) { 502957ae509SNilesh Bacchewar dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); 5035131f072SKuppuswamy Sathyanarayanan return ret; 504957ae509SNilesh Bacchewar } 505957ae509SNilesh Bacchewar 50657129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for BCU IRQs */ 50757129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 50857129044SKuppuswamy Sathyanarayanan BXTWC_BCU_LVL1_IRQ, 50957129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 51057129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_bcu, 51157129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_bcu); 51257129044SKuppuswamy Sathyanarayanan 51357129044SKuppuswamy Sathyanarayanan 51457129044SKuppuswamy Sathyanarayanan if (ret) { 51557129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add BUC IRQ chip\n"); 51657129044SKuppuswamy Sathyanarayanan return ret; 51757129044SKuppuswamy Sathyanarayanan } 51857129044SKuppuswamy Sathyanarayanan 51957129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for ADC IRQs */ 52057129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 52157129044SKuppuswamy Sathyanarayanan BXTWC_ADC_LVL1_IRQ, 52257129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 52357129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_adc, 52457129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_adc); 52557129044SKuppuswamy Sathyanarayanan 52657129044SKuppuswamy Sathyanarayanan 52757129044SKuppuswamy Sathyanarayanan if (ret) { 52857129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add ADC IRQ chip\n"); 52957129044SKuppuswamy Sathyanarayanan return ret; 53057129044SKuppuswamy Sathyanarayanan } 53157129044SKuppuswamy Sathyanarayanan 53257129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for CHGR IRQs */ 53357129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 53457129044SKuppuswamy Sathyanarayanan BXTWC_CHGR_LVL1_IRQ, 53557129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 53657129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_chgr, 53757129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_chgr); 53857129044SKuppuswamy Sathyanarayanan 53957129044SKuppuswamy Sathyanarayanan 54057129044SKuppuswamy Sathyanarayanan if (ret) { 54157129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add CHGR IRQ chip\n"); 54257129044SKuppuswamy Sathyanarayanan return ret; 54357129044SKuppuswamy Sathyanarayanan } 54457129044SKuppuswamy Sathyanarayanan 54557129044SKuppuswamy Sathyanarayanan /* Add chained IRQ handler for CRIT IRQs */ 54657129044SKuppuswamy Sathyanarayanan ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, 54757129044SKuppuswamy Sathyanarayanan BXTWC_CRIT_LVL1_IRQ, 54857129044SKuppuswamy Sathyanarayanan IRQF_ONESHOT, 54957129044SKuppuswamy Sathyanarayanan &bxtwc_regmap_irq_chip_crit, 55057129044SKuppuswamy Sathyanarayanan &pmic->irq_chip_data_crit); 55157129044SKuppuswamy Sathyanarayanan 55257129044SKuppuswamy Sathyanarayanan 55357129044SKuppuswamy Sathyanarayanan if (ret) { 55457129044SKuppuswamy Sathyanarayanan dev_err(&pdev->dev, "Failed to add CRIT IRQ chip\n"); 55557129044SKuppuswamy Sathyanarayanan return ret; 55657129044SKuppuswamy Sathyanarayanan } 55757129044SKuppuswamy Sathyanarayanan 5585131f072SKuppuswamy Sathyanarayanan ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, 5595131f072SKuppuswamy Sathyanarayanan ARRAY_SIZE(bxt_wc_dev), NULL, 0, NULL); 56039d047c0SQipeng Zha if (ret) { 56139d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to add devices\n"); 5625131f072SKuppuswamy Sathyanarayanan return ret; 56339d047c0SQipeng Zha } 56439d047c0SQipeng Zha 56539d047c0SQipeng Zha ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group); 56639d047c0SQipeng Zha if (ret) { 56739d047c0SQipeng Zha dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret); 5685131f072SKuppuswamy Sathyanarayanan return ret; 56939d047c0SQipeng Zha } 57039d047c0SQipeng Zha 5719c6235c8SBin Gao /* 5729c6235c8SBin Gao * There is known hw bug. Upon reset BIT 5 of register 5739c6235c8SBin Gao * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, 5749c6235c8SBin Gao * later it's set to 1(masked) automatically by hardware. So we 5759c6235c8SBin Gao * have the software workaround here to unmaksed it in order to let 5769c6235c8SBin Gao * charger interrutp work. 5779c6235c8SBin Gao */ 5789c6235c8SBin Gao regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, 5799c6235c8SBin Gao BXTWC_MIRQLVL1_MCHGR, 0); 5809c6235c8SBin Gao 58139d047c0SQipeng Zha return 0; 58239d047c0SQipeng Zha } 58339d047c0SQipeng Zha 58439d047c0SQipeng Zha static int bxtwc_remove(struct platform_device *pdev) 58539d047c0SQipeng Zha { 58639d047c0SQipeng Zha sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group); 58739d047c0SQipeng Zha 58839d047c0SQipeng Zha return 0; 58939d047c0SQipeng Zha } 59039d047c0SQipeng Zha 59139d047c0SQipeng Zha static void bxtwc_shutdown(struct platform_device *pdev) 59239d047c0SQipeng Zha { 59339d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); 59439d047c0SQipeng Zha 59539d047c0SQipeng Zha disable_irq(pmic->irq); 59639d047c0SQipeng Zha } 59739d047c0SQipeng Zha 59839d047c0SQipeng Zha #ifdef CONFIG_PM_SLEEP 59939d047c0SQipeng Zha static int bxtwc_suspend(struct device *dev) 60039d047c0SQipeng Zha { 60139d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 60239d047c0SQipeng Zha 60339d047c0SQipeng Zha disable_irq(pmic->irq); 60439d047c0SQipeng Zha 60539d047c0SQipeng Zha return 0; 60639d047c0SQipeng Zha } 60739d047c0SQipeng Zha 60839d047c0SQipeng Zha static int bxtwc_resume(struct device *dev) 60939d047c0SQipeng Zha { 61039d047c0SQipeng Zha struct intel_soc_pmic *pmic = dev_get_drvdata(dev); 61139d047c0SQipeng Zha 61239d047c0SQipeng Zha enable_irq(pmic->irq); 61339d047c0SQipeng Zha return 0; 61439d047c0SQipeng Zha } 61539d047c0SQipeng Zha #endif 61639d047c0SQipeng Zha static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); 61739d047c0SQipeng Zha 61839d047c0SQipeng Zha static const struct acpi_device_id bxtwc_acpi_ids[] = { 61939d047c0SQipeng Zha { "INT34D3", }, 62039d047c0SQipeng Zha { } 62139d047c0SQipeng Zha }; 622f57576e7SWei Yongjun MODULE_DEVICE_TABLE(acpi, bxtwc_acpi_ids); 62339d047c0SQipeng Zha 62439d047c0SQipeng Zha static struct platform_driver bxtwc_driver = { 62539d047c0SQipeng Zha .probe = bxtwc_probe, 62639d047c0SQipeng Zha .remove = bxtwc_remove, 62739d047c0SQipeng Zha .shutdown = bxtwc_shutdown, 62839d047c0SQipeng Zha .driver = { 62939d047c0SQipeng Zha .name = "BXTWC PMIC", 63039d047c0SQipeng Zha .pm = &bxtwc_pm_ops, 63139d047c0SQipeng Zha .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids), 63239d047c0SQipeng Zha }, 63339d047c0SQipeng Zha }; 63439d047c0SQipeng Zha 63539d047c0SQipeng Zha module_platform_driver(bxtwc_driver); 63639d047c0SQipeng Zha 63739d047c0SQipeng Zha MODULE_LICENSE("GPL v2"); 63839d047c0SQipeng Zha MODULE_AUTHOR("Qipeng Zha<qipeng.zha@intel.com>"); 639