1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Intel CHT Whiskey Cove PMIC operation region driver
4 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
5 *
6 * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
7 * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
8 */
9
10#include <linux/acpi.h>
11#include <linux/init.h>
12#include <linux/mfd/intel_soc_pmic.h>
13#include <linux/platform_device.h>
14#include <linux/regmap.h>
15#include "intel_pmic.h"
16
17#define CHT_WC_V1P05A_CTRL		0x6e3b
18#define CHT_WC_V1P15_CTRL		0x6e3c
19#define CHT_WC_V1P05A_VSEL		0x6e3d
20#define CHT_WC_V1P15_VSEL		0x6e3e
21#define CHT_WC_V1P8A_CTRL		0x6e56
22#define CHT_WC_V1P8SX_CTRL		0x6e57
23#define CHT_WC_VDDQ_CTRL		0x6e58
24#define CHT_WC_V1P2A_CTRL		0x6e59
25#define CHT_WC_V1P2SX_CTRL		0x6e5a
26#define CHT_WC_V1P8A_VSEL		0x6e5b
27#define CHT_WC_VDDQ_VSEL		0x6e5c
28#define CHT_WC_V2P8SX_CTRL		0x6e5d
29#define CHT_WC_V3P3A_CTRL		0x6e5e
30#define CHT_WC_V3P3SD_CTRL		0x6e5f
31#define CHT_WC_VSDIO_CTRL		0x6e67
32#define CHT_WC_V3P3A_VSEL		0x6e68
33#define CHT_WC_VPROG1A_CTRL		0x6e90
34#define CHT_WC_VPROG1B_CTRL		0x6e91
35#define CHT_WC_VPROG1F_CTRL		0x6e95
36#define CHT_WC_VPROG2D_CTRL		0x6e99
37#define CHT_WC_VPROG3A_CTRL		0x6e9a
38#define CHT_WC_VPROG3B_CTRL		0x6e9b
39#define CHT_WC_VPROG4A_CTRL		0x6e9c
40#define CHT_WC_VPROG4B_CTRL		0x6e9d
41#define CHT_WC_VPROG4C_CTRL		0x6e9e
42#define CHT_WC_VPROG4D_CTRL		0x6e9f
43#define CHT_WC_VPROG5A_CTRL		0x6ea0
44#define CHT_WC_VPROG5B_CTRL		0x6ea1
45#define CHT_WC_VPROG6A_CTRL		0x6ea2
46#define CHT_WC_VPROG6B_CTRL		0x6ea3
47#define CHT_WC_VPROG1A_VSEL		0x6ec0
48#define CHT_WC_VPROG1B_VSEL		0x6ec1
49#define CHT_WC_V1P8SX_VSEL		0x6ec2
50#define CHT_WC_V1P2SX_VSEL		0x6ec3
51#define CHT_WC_V1P2A_VSEL		0x6ec4
52#define CHT_WC_VPROG1F_VSEL		0x6ec5
53#define CHT_WC_VSDIO_VSEL		0x6ec6
54#define CHT_WC_V2P8SX_VSEL		0x6ec7
55#define CHT_WC_V3P3SD_VSEL		0x6ec8
56#define CHT_WC_VPROG2D_VSEL		0x6ec9
57#define CHT_WC_VPROG3A_VSEL		0x6eca
58#define CHT_WC_VPROG3B_VSEL		0x6ecb
59#define CHT_WC_VPROG4A_VSEL		0x6ecc
60#define CHT_WC_VPROG4B_VSEL		0x6ecd
61#define CHT_WC_VPROG4C_VSEL		0x6ece
62#define CHT_WC_VPROG4D_VSEL		0x6ecf
63#define CHT_WC_VPROG5A_VSEL		0x6ed0
64#define CHT_WC_VPROG5B_VSEL		0x6ed1
65#define CHT_WC_VPROG6A_VSEL		0x6ed2
66#define CHT_WC_VPROG6B_VSEL		0x6ed3
67
68/*
69 * Regulator support is based on the non upstream patch:
70 * "regulator: whiskey_cove: implements Whiskey Cove pmic VRF support"
71 * https://github.com/intel-aero/meta-intel-aero/blob/master/recipes-kernel/linux/linux-yocto/0019-regulator-whiskey_cove-implements-WhiskeyCove-pmic-V.patch
72 */
73static struct pmic_table power_table[] = {
74	{
75		.address = 0x0,
76		.reg = CHT_WC_V1P8A_CTRL,
77		.bit = 0x01,
78	}, /* V18A */
79	{
80		.address = 0x04,
81		.reg = CHT_WC_V1P8SX_CTRL,
82		.bit = 0x07,
83	}, /* V18X */
84	{
85		.address = 0x08,
86		.reg = CHT_WC_VDDQ_CTRL,
87		.bit = 0x01,
88	}, /* VDDQ */
89	{
90		.address = 0x0c,
91		.reg = CHT_WC_V1P2A_CTRL,
92		.bit = 0x07,
93	}, /* V12A */
94	{
95		.address = 0x10,
96		.reg = CHT_WC_V1P2SX_CTRL,
97		.bit = 0x07,
98	}, /* V12X */
99	{
100		.address = 0x14,
101		.reg = CHT_WC_V2P8SX_CTRL,
102		.bit = 0x07,
103	}, /* V28X */
104	{
105		.address = 0x18,
106		.reg = CHT_WC_V3P3A_CTRL,
107		.bit = 0x01,
108	}, /* V33A */
109	{
110		.address = 0x1c,
111		.reg = CHT_WC_V3P3SD_CTRL,
112		.bit = 0x07,
113	}, /* V3SD */
114	{
115		.address = 0x20,
116		.reg = CHT_WC_VSDIO_CTRL,
117		.bit = 0x07,
118	}, /* VSD */
119/*	{
120		.address = 0x24,
121		.reg = ??,
122		.bit = ??,
123	}, ** VSW2 */
124/*	{
125		.address = 0x28,
126		.reg = ??,
127		.bit = ??,
128	}, ** VSW1 */
129/*	{
130		.address = 0x2c,
131		.reg = ??,
132		.bit = ??,
133	}, ** VUPY */
134/*	{
135		.address = 0x30,
136		.reg = ??,
137		.bit = ??,
138	}, ** VRSO */
139	{
140		.address = 0x34,
141		.reg = CHT_WC_VPROG1A_CTRL,
142		.bit = 0x07,
143	}, /* VP1A */
144	{
145		.address = 0x38,
146		.reg = CHT_WC_VPROG1B_CTRL,
147		.bit = 0x07,
148	}, /* VP1B */
149	{
150		.address = 0x3c,
151		.reg = CHT_WC_VPROG1F_CTRL,
152		.bit = 0x07,
153	}, /* VP1F */
154	{
155		.address = 0x40,
156		.reg = CHT_WC_VPROG2D_CTRL,
157		.bit = 0x07,
158	}, /* VP2D */
159	{
160		.address = 0x44,
161		.reg = CHT_WC_VPROG3A_CTRL,
162		.bit = 0x07,
163	}, /* VP3A */
164	{
165		.address = 0x48,
166		.reg = CHT_WC_VPROG3B_CTRL,
167		.bit = 0x07,
168	}, /* VP3B */
169	{
170		.address = 0x4c,
171		.reg = CHT_WC_VPROG4A_CTRL,
172		.bit = 0x07,
173	}, /* VP4A */
174	{
175		.address = 0x50,
176		.reg = CHT_WC_VPROG4B_CTRL,
177		.bit = 0x07,
178	}, /* VP4B */
179	{
180		.address = 0x54,
181		.reg = CHT_WC_VPROG4C_CTRL,
182		.bit = 0x07,
183	}, /* VP4C */
184	{
185		.address = 0x58,
186		.reg = CHT_WC_VPROG4D_CTRL,
187		.bit = 0x07,
188	}, /* VP4D */
189	{
190		.address = 0x5c,
191		.reg = CHT_WC_VPROG5A_CTRL,
192		.bit = 0x07,
193	}, /* VP5A */
194	{
195		.address = 0x60,
196		.reg = CHT_WC_VPROG5B_CTRL,
197		.bit = 0x07,
198	}, /* VP5B */
199	{
200		.address = 0x64,
201		.reg = CHT_WC_VPROG6A_CTRL,
202		.bit = 0x07,
203	}, /* VP6A */
204	{
205		.address = 0x68,
206		.reg = CHT_WC_VPROG6B_CTRL,
207		.bit = 0x07,
208	}, /* VP6B */
209/*	{
210		.address = 0x6c,
211		.reg = ??,
212		.bit = ??,
213	}  ** VP7A */
214};
215
216static int intel_cht_wc_pmic_get_power(struct regmap *regmap, int reg,
217		int bit, u64 *value)
218{
219	int data;
220
221	if (regmap_read(regmap, reg, &data))
222		return -EIO;
223
224	*value = (data & bit) ? 1 : 0;
225	return 0;
226}
227
228static int intel_cht_wc_pmic_update_power(struct regmap *regmap, int reg,
229		int bitmask, bool on)
230{
231	return regmap_update_bits(regmap, reg, bitmask, on ? 1 : 0);
232}
233
234static int intel_cht_wc_exec_mipi_pmic_seq_element(struct regmap *regmap,
235						   u16 i2c_client_address,
236						   u32 reg_address,
237						   u32 value, u32 mask)
238{
239	u32 address;
240
241	if (i2c_client_address > 0xff || reg_address > 0xff) {
242		pr_warn("%s warning addresses too big client 0x%x reg 0x%x\n",
243			__func__, i2c_client_address, reg_address);
244		return -ERANGE;
245	}
246
247	address = (i2c_client_address << 8) | reg_address;
248
249	return regmap_update_bits(regmap, address, mask, value);
250}
251
252/*
253 * The thermal table and ops are empty, we do not support the Thermal opregion
254 * (DPTF) due to lacking documentation.
255 */
256static const struct intel_pmic_opregion_data intel_cht_wc_pmic_opregion_data = {
257	.get_power		= intel_cht_wc_pmic_get_power,
258	.update_power		= intel_cht_wc_pmic_update_power,
259	.exec_mipi_pmic_seq_element = intel_cht_wc_exec_mipi_pmic_seq_element,
260	.lpat_raw_to_temp	= acpi_lpat_raw_to_temp,
261	.power_table		= power_table,
262	.power_table_count	= ARRAY_SIZE(power_table),
263};
264
265static int intel_cht_wc_pmic_opregion_probe(struct platform_device *pdev)
266{
267	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
268
269	return intel_pmic_install_opregion_handler(&pdev->dev,
270			ACPI_HANDLE(pdev->dev.parent),
271			pmic->regmap,
272			&intel_cht_wc_pmic_opregion_data);
273}
274
275static const struct platform_device_id cht_wc_opregion_id_table[] = {
276	{ .name = "cht_wcove_region" },
277	{},
278};
279
280static struct platform_driver intel_cht_wc_pmic_opregion_driver = {
281	.probe = intel_cht_wc_pmic_opregion_probe,
282	.driver = {
283		.name = "cht_whiskey_cove_pmic",
284	},
285	.id_table = cht_wc_opregion_id_table,
286};
287builtin_platform_driver(intel_cht_wc_pmic_opregion_driver);
288