xref: /linux/drivers/mfd/intel_soc_pmic_bxtwc.c (revision c4949630fe437bc15346abbd1a92dee8e80a85d4)
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