1 /*
2  * Dollar Cove TI PMIC operation region driver
3  * Copyright (C) 2014 Intel Corporation. All rights reserved.
4  *
5  * Rewritten and cleaned up
6  * Copyright (C) 2017 Takashi Iwai <tiwai@suse.de>
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/init.h>
11 #include <linux/mfd/intel_soc_pmic.h>
12 #include <linux/platform_device.h>
13 #include "intel_pmic.h"
14 
15 /* registers stored in 16bit BE (high:low, total 10bit) */
16 #define CHTDC_TI_VBAT		0x54
17 #define CHTDC_TI_DIETEMP	0x56
18 #define CHTDC_TI_BPTHERM	0x58
19 #define CHTDC_TI_GPADC		0x5a
20 
21 static struct pmic_table chtdc_ti_power_table[] = {
22 	{ .address = 0x00, .reg = 0x41 },
23 	{ .address = 0x04, .reg = 0x42 },
24 	{ .address = 0x08, .reg = 0x43 },
25 	{ .address = 0x0c, .reg = 0x45 },
26 	{ .address = 0x10, .reg = 0x46 },
27 	{ .address = 0x14, .reg = 0x47 },
28 	{ .address = 0x18, .reg = 0x48 },
29 	{ .address = 0x1c, .reg = 0x49 },
30 	{ .address = 0x20, .reg = 0x4a },
31 	{ .address = 0x24, .reg = 0x4b },
32 	{ .address = 0x28, .reg = 0x4c },
33 	{ .address = 0x2c, .reg = 0x4d },
34 	{ .address = 0x30, .reg = 0x4e },
35 };
36 
37 static struct pmic_table chtdc_ti_thermal_table[] = {
38 	{
39 		.address = 0x00,
40 		.reg = CHTDC_TI_GPADC
41 	},
42 	{
43 		.address = 0x0c,
44 		.reg = CHTDC_TI_GPADC
45 	},
46 	/* TMP2 -> SYSTEMP */
47 	{
48 		.address = 0x18,
49 		.reg = CHTDC_TI_GPADC
50 	},
51 	/* TMP3 -> BPTHERM */
52 	{
53 		.address = 0x24,
54 		.reg = CHTDC_TI_BPTHERM
55 	},
56 	{
57 		.address = 0x30,
58 		.reg = CHTDC_TI_GPADC
59 	},
60 	/* TMP5 -> DIETEMP */
61 	{
62 		.address = 0x3c,
63 		.reg = CHTDC_TI_DIETEMP
64 	},
65 };
66 
67 static int chtdc_ti_pmic_get_power(struct regmap *regmap, int reg, int bit,
68 				   u64 *value)
69 {
70 	int data;
71 
72 	if (regmap_read(regmap, reg, &data))
73 		return -EIO;
74 
75 	*value = data & 1;
76 	return 0;
77 }
78 
79 static int chtdc_ti_pmic_update_power(struct regmap *regmap, int reg, int bit,
80 				      bool on)
81 {
82 	return regmap_update_bits(regmap, reg, 1, on);
83 }
84 
85 static int chtdc_ti_pmic_get_raw_temp(struct regmap *regmap, int reg)
86 {
87 	u8 buf[2];
88 
89 	if (regmap_bulk_read(regmap, reg, buf, 2))
90 		return -EIO;
91 
92 	/* stored in big-endian */
93 	return ((buf[0] & 0x03) << 8) | buf[1];
94 }
95 
96 static struct intel_pmic_opregion_data chtdc_ti_pmic_opregion_data = {
97 	.get_power = chtdc_ti_pmic_get_power,
98 	.update_power = chtdc_ti_pmic_update_power,
99 	.get_raw_temp = chtdc_ti_pmic_get_raw_temp,
100 	.power_table = chtdc_ti_power_table,
101 	.power_table_count = ARRAY_SIZE(chtdc_ti_power_table),
102 	.thermal_table = chtdc_ti_thermal_table,
103 	.thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table),
104 };
105 
106 static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev)
107 {
108 	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
109 	int err;
110 
111 	err = intel_pmic_install_opregion_handler(&pdev->dev,
112 			ACPI_HANDLE(pdev->dev.parent), pmic->regmap,
113 			&chtdc_ti_pmic_opregion_data);
114 	if (err < 0)
115 		return err;
116 
117 	/* Re-enumerate devices depending on PMIC */
118 	acpi_walk_dep_device_list(ACPI_HANDLE(pdev->dev.parent));
119 	return 0;
120 }
121 
122 static const struct platform_device_id chtdc_ti_pmic_opregion_id_table[] = {
123 	{ .name = "chtdc_ti_region" },
124 	{},
125 };
126 
127 static struct platform_driver chtdc_ti_pmic_opregion_driver = {
128 	.probe = chtdc_ti_pmic_opregion_probe,
129 	.driver = {
130 		.name = "cht_dollar_cove_ti_pmic",
131 	},
132 	.id_table = chtdc_ti_pmic_opregion_id_table,
133 };
134 builtin_platform_driver(chtdc_ti_pmic_opregion_driver);
135