Linux Audio

Check our new training course

Loading...
  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);