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