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