xref: /openbmc/linux/drivers/hwmon/oxp-sensors.c (revision 4dbbaf8fbdbd13adc80731b2452257857e4c2d8b)
1ed264e8aSJoaquín Ignacio Aramendía // SPDX-License-Identifier: GPL-2.0+
2ed264e8aSJoaquín Ignacio Aramendía /*
3ebd4bfeeSDerek J. Clark  * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose
4ebd4bfeeSDerek J. Clark  * fan reading and control via hwmon sysfs.
5ed264e8aSJoaquín Ignacio Aramendía  *
6ebd4bfeeSDerek J. Clark  * Old OXP boards have the same DMI strings and they are told apart by
7ebd4bfeeSDerek J. Clark  * the boot cpu vendor (Intel/AMD). Currently only AMD boards are
8ebd4bfeeSDerek J. Clark  * supported but the code is made to be simple to add other handheld
9ebd4bfeeSDerek J. Clark  * boards in the future.
103ca0f12aSJoaquín Ignacio Aramendía  * Fan control is provided via pwm interface in the range [0-255].
113ca0f12aSJoaquín Ignacio Aramendía  * Old AMD boards use [0-100] as range in the EC, the written value is
123ca0f12aSJoaquín Ignacio Aramendía  * scaled to accommodate for that. Newer boards like the mini PRO and
133ca0f12aSJoaquín Ignacio Aramendía  * AOK ZOE are not scaled but have the same EC layout.
14ed264e8aSJoaquín Ignacio Aramendía  *
15ed264e8aSJoaquín Ignacio Aramendía  * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
16ed264e8aSJoaquín Ignacio Aramendía  */
17ed264e8aSJoaquín Ignacio Aramendía 
18ed264e8aSJoaquín Ignacio Aramendía #include <linux/acpi.h>
19ed264e8aSJoaquín Ignacio Aramendía #include <linux/dmi.h>
20ed264e8aSJoaquín Ignacio Aramendía #include <linux/hwmon.h>
21ed264e8aSJoaquín Ignacio Aramendía #include <linux/init.h>
22ed264e8aSJoaquín Ignacio Aramendía #include <linux/kernel.h>
23ed264e8aSJoaquín Ignacio Aramendía #include <linux/module.h>
24ed264e8aSJoaquín Ignacio Aramendía #include <linux/platform_device.h>
25ed264e8aSJoaquín Ignacio Aramendía #include <linux/processor.h>
26ed264e8aSJoaquín Ignacio Aramendía 
27ed264e8aSJoaquín Ignacio Aramendía /* Handle ACPI lock mechanism */
28ed264e8aSJoaquín Ignacio Aramendía static u32 oxp_mutex;
29ed264e8aSJoaquín Ignacio Aramendía 
30ed264e8aSJoaquín Ignacio Aramendía #define ACPI_LOCK_DELAY_MS	500
31ed264e8aSJoaquín Ignacio Aramendía 
32ed264e8aSJoaquín Ignacio Aramendía static bool lock_global_acpi_lock(void)
33ed264e8aSJoaquín Ignacio Aramendía {
34ed264e8aSJoaquín Ignacio Aramendía 	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
35ed264e8aSJoaquín Ignacio Aramendía }
36ed264e8aSJoaquín Ignacio Aramendía 
37ed264e8aSJoaquín Ignacio Aramendía static bool unlock_global_acpi_lock(void)
38ed264e8aSJoaquín Ignacio Aramendía {
39ed264e8aSJoaquín Ignacio Aramendía 	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
40ed264e8aSJoaquín Ignacio Aramendía }
41ed264e8aSJoaquín Ignacio Aramendía 
423ca0f12aSJoaquín Ignacio Aramendía enum oxp_board {
433ca0f12aSJoaquín Ignacio Aramendía 	aok_zoe_a1 = 1,
44f415cb6cSJoaquín Ignacio Aramendía 	aya_neo_2,
45ebd4bfeeSDerek J. Clark 	aya_neo_air,
46ebd4bfeeSDerek J. Clark 	aya_neo_air_pro,
47f415cb6cSJoaquín Ignacio Aramendía 	aya_neo_geek,
483ca0f12aSJoaquín Ignacio Aramendía 	oxp_mini_amd,
49be144ee4SJoaquín Ignacio Aramendía 	oxp_mini_amd_a07,
503ca0f12aSJoaquín Ignacio Aramendía 	oxp_mini_amd_pro,
513ca0f12aSJoaquín Ignacio Aramendía };
523ca0f12aSJoaquín Ignacio Aramendía 
533ca0f12aSJoaquín Ignacio Aramendía static enum oxp_board board;
543ca0f12aSJoaquín Ignacio Aramendía 
55be144ee4SJoaquín Ignacio Aramendía /* Fan reading and PWM */
56ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */
57ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */
58ed264e8aSJoaquín Ignacio Aramendía #define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register long */
59ed264e8aSJoaquín Ignacio Aramendía 
60be144ee4SJoaquín Ignacio Aramendía /* Turbo button takeover function
61be144ee4SJoaquín Ignacio Aramendía  * Older boards have different values and EC registers
62be144ee4SJoaquín Ignacio Aramendía  * for the same function
63be144ee4SJoaquín Ignacio Aramendía  */
64be144ee4SJoaquín Ignacio Aramendía #define OXP_OLD_TURBO_SWITCH_REG	0x1E
65be144ee4SJoaquín Ignacio Aramendía #define OXP_OLD_TURBO_TAKE_VAL		0x01
66be144ee4SJoaquín Ignacio Aramendía #define OXP_OLD_TURBO_RETURN_VAL	0x00
67be144ee4SJoaquín Ignacio Aramendía 
68be144ee4SJoaquín Ignacio Aramendía #define OXP_TURBO_SWITCH_REG		0xF1
69be144ee4SJoaquín Ignacio Aramendía #define OXP_TURBO_TAKE_VAL		0x40
70be144ee4SJoaquín Ignacio Aramendía #define OXP_TURBO_RETURN_VAL		0x00
71be144ee4SJoaquín Ignacio Aramendía 
72ed264e8aSJoaquín Ignacio Aramendía static const struct dmi_system_id dmi_table[] = {
73ed264e8aSJoaquín Ignacio Aramendía 	{
74ed264e8aSJoaquín Ignacio Aramendía 		.matches = {
753ca0f12aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
763ca0f12aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
773ca0f12aSJoaquín Ignacio Aramendía 		},
785d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)aok_zoe_a1,
793ca0f12aSJoaquín Ignacio Aramendía 	},
803ca0f12aSJoaquín Ignacio Aramendía 	{
813ca0f12aSJoaquín Ignacio Aramendía 		.matches = {
82*4dbbaf8fSJerrod Frost 			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
83*4dbbaf8fSJerrod Frost 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
84*4dbbaf8fSJerrod Frost 		},
85*4dbbaf8fSJerrod Frost 		.driver_data = (void *)aok_zoe_a1,
86*4dbbaf8fSJerrod Frost 	},
87*4dbbaf8fSJerrod Frost 	{
88*4dbbaf8fSJerrod Frost 		.matches = {
89ebd4bfeeSDerek J. Clark 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
90f415cb6cSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
91f415cb6cSJoaquín Ignacio Aramendía 		},
925d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)aya_neo_2,
93f415cb6cSJoaquín Ignacio Aramendía 	},
94f415cb6cSJoaquín Ignacio Aramendía 	{
95f415cb6cSJoaquín Ignacio Aramendía 		.matches = {
96f415cb6cSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
97ebd4bfeeSDerek J. Clark 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
98ebd4bfeeSDerek J. Clark 		},
995d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)aya_neo_air,
100ebd4bfeeSDerek J. Clark 	},
101ebd4bfeeSDerek J. Clark 	{
102ebd4bfeeSDerek J. Clark 		.matches = {
103ebd4bfeeSDerek J. Clark 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
104ebd4bfeeSDerek J. Clark 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
105ebd4bfeeSDerek J. Clark 		},
1065d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)aya_neo_air_pro,
107ebd4bfeeSDerek J. Clark 	},
108ebd4bfeeSDerek J. Clark 	{
109ebd4bfeeSDerek J. Clark 		.matches = {
110f415cb6cSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
111f415cb6cSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"),
112f415cb6cSJoaquín Ignacio Aramendía 		},
1135d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)aya_neo_geek,
114f415cb6cSJoaquín Ignacio Aramendía 	},
115f415cb6cSJoaquín Ignacio Aramendía 	{
116f415cb6cSJoaquín Ignacio Aramendía 		.matches = {
117ed264e8aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
118ed264e8aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
119ed264e8aSJoaquín Ignacio Aramendía 		},
1205d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)oxp_mini_amd,
1213ca0f12aSJoaquín Ignacio Aramendía 	},
1223ca0f12aSJoaquín Ignacio Aramendía 	{
1233ca0f12aSJoaquín Ignacio Aramendía 		.matches = {
1243ca0f12aSJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
1257d0c2c61SJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
1267d0c2c61SJoaquín Ignacio Aramendía 		},
127be144ee4SJoaquín Ignacio Aramendía 		.driver_data = (void *)oxp_mini_amd_a07,
1287d0c2c61SJoaquín Ignacio Aramendía 	},
1297d0c2c61SJoaquín Ignacio Aramendía 	{
1307d0c2c61SJoaquín Ignacio Aramendía 		.matches = {
1317d0c2c61SJoaquín Ignacio Aramendía 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
1323ca0f12aSJoaquín Ignacio Aramendía 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
1333ca0f12aSJoaquín Ignacio Aramendía 		},
1345d06ec42SJoaquín Ignacio Aramendía 		.driver_data = (void *)oxp_mini_amd_pro,
135ed264e8aSJoaquín Ignacio Aramendía 	},
136ed264e8aSJoaquín Ignacio Aramendía 	{},
137ed264e8aSJoaquín Ignacio Aramendía };
138ed264e8aSJoaquín Ignacio Aramendía 
139ed264e8aSJoaquín Ignacio Aramendía /* Helper functions to handle EC read/write */
140ed264e8aSJoaquín Ignacio Aramendía static int read_from_ec(u8 reg, int size, long *val)
141ed264e8aSJoaquín Ignacio Aramendía {
142ed264e8aSJoaquín Ignacio Aramendía 	int i;
143ed264e8aSJoaquín Ignacio Aramendía 	int ret;
144ed264e8aSJoaquín Ignacio Aramendía 	u8 buffer;
145ed264e8aSJoaquín Ignacio Aramendía 
146ed264e8aSJoaquín Ignacio Aramendía 	if (!lock_global_acpi_lock())
147ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
148ed264e8aSJoaquín Ignacio Aramendía 
149ed264e8aSJoaquín Ignacio Aramendía 	*val = 0;
150ed264e8aSJoaquín Ignacio Aramendía 	for (i = 0; i < size; i++) {
151ed264e8aSJoaquín Ignacio Aramendía 		ret = ec_read(reg + i, &buffer);
152ed264e8aSJoaquín Ignacio Aramendía 		if (ret)
153ed264e8aSJoaquín Ignacio Aramendía 			return ret;
154ed264e8aSJoaquín Ignacio Aramendía 		*val <<= i * 8;
155ed264e8aSJoaquín Ignacio Aramendía 		*val += buffer;
156ed264e8aSJoaquín Ignacio Aramendía 	}
157ed264e8aSJoaquín Ignacio Aramendía 
158ed264e8aSJoaquín Ignacio Aramendía 	if (!unlock_global_acpi_lock())
159ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
160ed264e8aSJoaquín Ignacio Aramendía 
161ed264e8aSJoaquín Ignacio Aramendía 	return 0;
162ed264e8aSJoaquín Ignacio Aramendía }
163ed264e8aSJoaquín Ignacio Aramendía 
1647590e659SJoaquín Ignacio Aramendía static int write_to_ec(u8 reg, u8 value)
165ed264e8aSJoaquín Ignacio Aramendía {
166ed264e8aSJoaquín Ignacio Aramendía 	int ret;
167ed264e8aSJoaquín Ignacio Aramendía 
168ed264e8aSJoaquín Ignacio Aramendía 	if (!lock_global_acpi_lock())
169ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
170ed264e8aSJoaquín Ignacio Aramendía 
171ed264e8aSJoaquín Ignacio Aramendía 	ret = ec_write(reg, value);
172ed264e8aSJoaquín Ignacio Aramendía 
173ed264e8aSJoaquín Ignacio Aramendía 	if (!unlock_global_acpi_lock())
174ed264e8aSJoaquín Ignacio Aramendía 		return -EBUSY;
175ed264e8aSJoaquín Ignacio Aramendía 
176ed264e8aSJoaquín Ignacio Aramendía 	return ret;
177ed264e8aSJoaquín Ignacio Aramendía }
178ed264e8aSJoaquín Ignacio Aramendía 
179be144ee4SJoaquín Ignacio Aramendía /* Turbo button toggle functions */
180be144ee4SJoaquín Ignacio Aramendía static int tt_toggle_enable(void)
181be144ee4SJoaquín Ignacio Aramendía {
182be144ee4SJoaquín Ignacio Aramendía 	u8 reg;
183be144ee4SJoaquín Ignacio Aramendía 	u8 val;
184be144ee4SJoaquín Ignacio Aramendía 
185be144ee4SJoaquín Ignacio Aramendía 	switch (board) {
186be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_a07:
187be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_OLD_TURBO_SWITCH_REG;
188be144ee4SJoaquín Ignacio Aramendía 		val = OXP_OLD_TURBO_TAKE_VAL;
189be144ee4SJoaquín Ignacio Aramendía 		break;
190be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_pro:
191be144ee4SJoaquín Ignacio Aramendía 	case aok_zoe_a1:
192be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_TURBO_SWITCH_REG;
193be144ee4SJoaquín Ignacio Aramendía 		val = OXP_TURBO_TAKE_VAL;
194be144ee4SJoaquín Ignacio Aramendía 		break;
195be144ee4SJoaquín Ignacio Aramendía 	default:
196be144ee4SJoaquín Ignacio Aramendía 		return -EINVAL;
197be144ee4SJoaquín Ignacio Aramendía 	}
198be144ee4SJoaquín Ignacio Aramendía 	return write_to_ec(reg, val);
199be144ee4SJoaquín Ignacio Aramendía }
200be144ee4SJoaquín Ignacio Aramendía 
201be144ee4SJoaquín Ignacio Aramendía static int tt_toggle_disable(void)
202be144ee4SJoaquín Ignacio Aramendía {
203be144ee4SJoaquín Ignacio Aramendía 	u8 reg;
204be144ee4SJoaquín Ignacio Aramendía 	u8 val;
205be144ee4SJoaquín Ignacio Aramendía 
206be144ee4SJoaquín Ignacio Aramendía 	switch (board) {
207be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_a07:
208be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_OLD_TURBO_SWITCH_REG;
209be144ee4SJoaquín Ignacio Aramendía 		val = OXP_OLD_TURBO_RETURN_VAL;
210be144ee4SJoaquín Ignacio Aramendía 		break;
211be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_pro:
212be144ee4SJoaquín Ignacio Aramendía 	case aok_zoe_a1:
213be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_TURBO_SWITCH_REG;
214be144ee4SJoaquín Ignacio Aramendía 		val = OXP_TURBO_RETURN_VAL;
215be144ee4SJoaquín Ignacio Aramendía 		break;
216be144ee4SJoaquín Ignacio Aramendía 	default:
217be144ee4SJoaquín Ignacio Aramendía 		return -EINVAL;
218be144ee4SJoaquín Ignacio Aramendía 	}
219be144ee4SJoaquín Ignacio Aramendía 	return write_to_ec(reg, val);
220be144ee4SJoaquín Ignacio Aramendía }
221be144ee4SJoaquín Ignacio Aramendía 
222be144ee4SJoaquín Ignacio Aramendía /* Callbacks for turbo toggle attribute */
223be144ee4SJoaquín Ignacio Aramendía static ssize_t tt_toggle_store(struct device *dev,
224be144ee4SJoaquín Ignacio Aramendía 			       struct device_attribute *attr, const char *buf,
225be144ee4SJoaquín Ignacio Aramendía 			       size_t count)
226be144ee4SJoaquín Ignacio Aramendía {
227be144ee4SJoaquín Ignacio Aramendía 	int rval;
228be144ee4SJoaquín Ignacio Aramendía 	bool value;
229be144ee4SJoaquín Ignacio Aramendía 
230be144ee4SJoaquín Ignacio Aramendía 	rval = kstrtobool(buf, &value);
231be144ee4SJoaquín Ignacio Aramendía 	if (rval)
232be144ee4SJoaquín Ignacio Aramendía 		return rval;
233be144ee4SJoaquín Ignacio Aramendía 
234be144ee4SJoaquín Ignacio Aramendía 	if (value) {
235be144ee4SJoaquín Ignacio Aramendía 		rval = tt_toggle_enable();
236be144ee4SJoaquín Ignacio Aramendía 	} else {
237be144ee4SJoaquín Ignacio Aramendía 		rval = tt_toggle_disable();
23837f665ffSJoaquín Ignacio Aramendía 	}
239be144ee4SJoaquín Ignacio Aramendía 	if (rval)
240be144ee4SJoaquín Ignacio Aramendía 		return rval;
24137f665ffSJoaquín Ignacio Aramendía 
242be144ee4SJoaquín Ignacio Aramendía 	return count;
243be144ee4SJoaquín Ignacio Aramendía }
244be144ee4SJoaquín Ignacio Aramendía 
245be144ee4SJoaquín Ignacio Aramendía static ssize_t tt_toggle_show(struct device *dev,
246be144ee4SJoaquín Ignacio Aramendía 			      struct device_attribute *attr, char *buf)
247be144ee4SJoaquín Ignacio Aramendía {
248be144ee4SJoaquín Ignacio Aramendía 	int retval;
249be144ee4SJoaquín Ignacio Aramendía 	u8 reg;
250be144ee4SJoaquín Ignacio Aramendía 	long val;
251be144ee4SJoaquín Ignacio Aramendía 
252be144ee4SJoaquín Ignacio Aramendía 	switch (board) {
253be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_a07:
254be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_OLD_TURBO_SWITCH_REG;
255be144ee4SJoaquín Ignacio Aramendía 		break;
256be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_pro:
257be144ee4SJoaquín Ignacio Aramendía 	case aok_zoe_a1:
258be144ee4SJoaquín Ignacio Aramendía 		reg = OXP_TURBO_SWITCH_REG;
259be144ee4SJoaquín Ignacio Aramendía 		break;
260be144ee4SJoaquín Ignacio Aramendía 	default:
261be144ee4SJoaquín Ignacio Aramendía 		return -EINVAL;
262be144ee4SJoaquín Ignacio Aramendía 	}
263be144ee4SJoaquín Ignacio Aramendía 
264be144ee4SJoaquín Ignacio Aramendía 	retval = read_from_ec(reg, 1, &val);
265be144ee4SJoaquín Ignacio Aramendía 	if (retval)
266be144ee4SJoaquín Ignacio Aramendía 		return retval;
267be144ee4SJoaquín Ignacio Aramendía 
268be144ee4SJoaquín Ignacio Aramendía 	return sysfs_emit(buf, "%d\n", !!val);
269be144ee4SJoaquín Ignacio Aramendía }
270be144ee4SJoaquín Ignacio Aramendía 
271be144ee4SJoaquín Ignacio Aramendía static DEVICE_ATTR_RW(tt_toggle);
272be144ee4SJoaquín Ignacio Aramendía 
273be144ee4SJoaquín Ignacio Aramendía /* PWM enable/disable functions */
2747590e659SJoaquín Ignacio Aramendía static int oxp_pwm_enable(void)
275ed264e8aSJoaquín Ignacio Aramendía {
2767590e659SJoaquín Ignacio Aramendía 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01);
277ed264e8aSJoaquín Ignacio Aramendía }
278ed264e8aSJoaquín Ignacio Aramendía 
2797590e659SJoaquín Ignacio Aramendía static int oxp_pwm_disable(void)
280ed264e8aSJoaquín Ignacio Aramendía {
2817590e659SJoaquín Ignacio Aramendía 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00);
282ed264e8aSJoaquín Ignacio Aramendía }
283ed264e8aSJoaquín Ignacio Aramendía 
284ed264e8aSJoaquín Ignacio Aramendía /* Callbacks for hwmon interface */
285ed264e8aSJoaquín Ignacio Aramendía static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
286ed264e8aSJoaquín Ignacio Aramendía 				       enum hwmon_sensor_types type, u32 attr, int channel)
287ed264e8aSJoaquín Ignacio Aramendía {
288ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
289ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_fan:
290ed264e8aSJoaquín Ignacio Aramendía 		return 0444;
291ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
292ed264e8aSJoaquín Ignacio Aramendía 		return 0644;
293ed264e8aSJoaquín Ignacio Aramendía 	default:
294ed264e8aSJoaquín Ignacio Aramendía 		return 0;
295ed264e8aSJoaquín Ignacio Aramendía 	}
296ed264e8aSJoaquín Ignacio Aramendía }
297ed264e8aSJoaquín Ignacio Aramendía 
298ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
299ed264e8aSJoaquín Ignacio Aramendía 			     u32 attr, int channel, long *val)
300ed264e8aSJoaquín Ignacio Aramendía {
301ed264e8aSJoaquín Ignacio Aramendía 	int ret;
302ed264e8aSJoaquín Ignacio Aramendía 
303ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
304ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_fan:
305ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
306ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_fan_input:
307ed264e8aSJoaquín Ignacio Aramendía 			return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
308ed264e8aSJoaquín Ignacio Aramendía 		default:
309ed264e8aSJoaquín Ignacio Aramendía 			break;
310ed264e8aSJoaquín Ignacio Aramendía 		}
311ed264e8aSJoaquín Ignacio Aramendía 		break;
312ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
313ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
314ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_input:
3150cd3ba68SJoaquín Ignacio Aramendía 			ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
316ed264e8aSJoaquín Ignacio Aramendía 			if (ret)
317ed264e8aSJoaquín Ignacio Aramendía 				return ret;
318ebd4bfeeSDerek J. Clark 			switch (board) {
319f415cb6cSJoaquín Ignacio Aramendía 			case aya_neo_2:
320ebd4bfeeSDerek J. Clark 			case aya_neo_air:
321ebd4bfeeSDerek J. Clark 			case aya_neo_air_pro:
322f415cb6cSJoaquín Ignacio Aramendía 			case aya_neo_geek:
323ebd4bfeeSDerek J. Clark 			case oxp_mini_amd:
324be144ee4SJoaquín Ignacio Aramendía 			case oxp_mini_amd_a07:
325ed264e8aSJoaquín Ignacio Aramendía 				*val = (*val * 255) / 100;
326ebd4bfeeSDerek J. Clark 				break;
327ebd4bfeeSDerek J. Clark 			case oxp_mini_amd_pro:
328ebd4bfeeSDerek J. Clark 			case aok_zoe_a1:
329ebd4bfeeSDerek J. Clark 			default:
330ebd4bfeeSDerek J. Clark 				break;
331ebd4bfeeSDerek J. Clark 			}
332ed264e8aSJoaquín Ignacio Aramendía 			return 0;
333ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_enable:
334ed264e8aSJoaquín Ignacio Aramendía 			return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
335ed264e8aSJoaquín Ignacio Aramendía 		default:
336ed264e8aSJoaquín Ignacio Aramendía 			break;
337ed264e8aSJoaquín Ignacio Aramendía 		}
338ed264e8aSJoaquín Ignacio Aramendía 		break;
339ed264e8aSJoaquín Ignacio Aramendía 	default:
340ed264e8aSJoaquín Ignacio Aramendía 		break;
341ed264e8aSJoaquín Ignacio Aramendía 	}
342ed264e8aSJoaquín Ignacio Aramendía 	return -EOPNOTSUPP;
343ed264e8aSJoaquín Ignacio Aramendía }
344ed264e8aSJoaquín Ignacio Aramendía 
345ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
346ed264e8aSJoaquín Ignacio Aramendía 			      u32 attr, int channel, long val)
347ed264e8aSJoaquín Ignacio Aramendía {
348ed264e8aSJoaquín Ignacio Aramendía 	switch (type) {
349ed264e8aSJoaquín Ignacio Aramendía 	case hwmon_pwm:
350ed264e8aSJoaquín Ignacio Aramendía 		switch (attr) {
351ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_enable:
352ed264e8aSJoaquín Ignacio Aramendía 			if (val == 1)
3537590e659SJoaquín Ignacio Aramendía 				return oxp_pwm_enable();
354ed264e8aSJoaquín Ignacio Aramendía 			else if (val == 0)
3557590e659SJoaquín Ignacio Aramendía 				return oxp_pwm_disable();
356ed264e8aSJoaquín Ignacio Aramendía 			return -EINVAL;
357ed264e8aSJoaquín Ignacio Aramendía 		case hwmon_pwm_input:
358ed264e8aSJoaquín Ignacio Aramendía 			if (val < 0 || val > 255)
359ed264e8aSJoaquín Ignacio Aramendía 				return -EINVAL;
360ebd4bfeeSDerek J. Clark 			switch (board) {
361f415cb6cSJoaquín Ignacio Aramendía 			case aya_neo_2:
362ebd4bfeeSDerek J. Clark 			case aya_neo_air:
363ebd4bfeeSDerek J. Clark 			case aya_neo_air_pro:
364f415cb6cSJoaquín Ignacio Aramendía 			case aya_neo_geek:
365ebd4bfeeSDerek J. Clark 			case oxp_mini_amd:
366be144ee4SJoaquín Ignacio Aramendía 			case oxp_mini_amd_a07:
367ed264e8aSJoaquín Ignacio Aramendía 				val = (val * 100) / 255;
368ebd4bfeeSDerek J. Clark 				break;
369ebd4bfeeSDerek J. Clark 			case aok_zoe_a1:
370ebd4bfeeSDerek J. Clark 			case oxp_mini_amd_pro:
371ebd4bfeeSDerek J. Clark 			default:
372ebd4bfeeSDerek J. Clark 				break;
373ebd4bfeeSDerek J. Clark 			}
3747590e659SJoaquín Ignacio Aramendía 			return write_to_ec(OXP_SENSOR_PWM_REG, val);
375ed264e8aSJoaquín Ignacio Aramendía 		default:
376ed264e8aSJoaquín Ignacio Aramendía 			break;
377ed264e8aSJoaquín Ignacio Aramendía 		}
378ed264e8aSJoaquín Ignacio Aramendía 		break;
379ed264e8aSJoaquín Ignacio Aramendía 	default:
380ed264e8aSJoaquín Ignacio Aramendía 		break;
381ed264e8aSJoaquín Ignacio Aramendía 	}
382ed264e8aSJoaquín Ignacio Aramendía 	return -EOPNOTSUPP;
383ed264e8aSJoaquín Ignacio Aramendía }
384ed264e8aSJoaquín Ignacio Aramendía 
385ed264e8aSJoaquín Ignacio Aramendía /* Known sensors in the OXP EC controllers */
386195030d3SKrzysztof Kozlowski static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
387ed264e8aSJoaquín Ignacio Aramendía 	HWMON_CHANNEL_INFO(fan,
388ed264e8aSJoaquín Ignacio Aramendía 			   HWMON_F_INPUT),
389ed264e8aSJoaquín Ignacio Aramendía 	HWMON_CHANNEL_INFO(pwm,
390ed264e8aSJoaquín Ignacio Aramendía 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
391ed264e8aSJoaquín Ignacio Aramendía 	NULL,
392ed264e8aSJoaquín Ignacio Aramendía };
393ed264e8aSJoaquín Ignacio Aramendía 
394be144ee4SJoaquín Ignacio Aramendía static struct attribute *oxp_ec_attrs[] = {
395be144ee4SJoaquín Ignacio Aramendía 	&dev_attr_tt_toggle.attr,
396be144ee4SJoaquín Ignacio Aramendía 	NULL
397be144ee4SJoaquín Ignacio Aramendía };
398be144ee4SJoaquín Ignacio Aramendía 
399be144ee4SJoaquín Ignacio Aramendía ATTRIBUTE_GROUPS(oxp_ec);
400be144ee4SJoaquín Ignacio Aramendía 
401ed264e8aSJoaquín Ignacio Aramendía static const struct hwmon_ops oxp_ec_hwmon_ops = {
402ed264e8aSJoaquín Ignacio Aramendía 	.is_visible = oxp_ec_hwmon_is_visible,
403ed264e8aSJoaquín Ignacio Aramendía 	.read = oxp_platform_read,
404ed264e8aSJoaquín Ignacio Aramendía 	.write = oxp_platform_write,
405ed264e8aSJoaquín Ignacio Aramendía };
406ed264e8aSJoaquín Ignacio Aramendía 
407ed264e8aSJoaquín Ignacio Aramendía static const struct hwmon_chip_info oxp_ec_chip_info = {
408ed264e8aSJoaquín Ignacio Aramendía 	.ops = &oxp_ec_hwmon_ops,
409ed264e8aSJoaquín Ignacio Aramendía 	.info = oxp_platform_sensors,
410ed264e8aSJoaquín Ignacio Aramendía };
411ed264e8aSJoaquín Ignacio Aramendía 
412ed264e8aSJoaquín Ignacio Aramendía /* Initialization logic */
413ed264e8aSJoaquín Ignacio Aramendía static int oxp_platform_probe(struct platform_device *pdev)
414ed264e8aSJoaquín Ignacio Aramendía {
415ed264e8aSJoaquín Ignacio Aramendía 	const struct dmi_system_id *dmi_entry;
416ed264e8aSJoaquín Ignacio Aramendía 	struct device *dev = &pdev->dev;
417ed264e8aSJoaquín Ignacio Aramendía 	struct device *hwdev;
418be144ee4SJoaquín Ignacio Aramendía 	int ret;
419ed264e8aSJoaquín Ignacio Aramendía 
420ed264e8aSJoaquín Ignacio Aramendía 	/*
421ed264e8aSJoaquín Ignacio Aramendía 	 * Have to check for AMD processor here because DMI strings are the
422ebd4bfeeSDerek J. Clark 	 * same between Intel and AMD boards, the only way to tell them apart
423ed264e8aSJoaquín Ignacio Aramendía 	 * is the CPU.
424ed264e8aSJoaquín Ignacio Aramendía 	 * Intel boards seem to have different EC registers and values to
425ed264e8aSJoaquín Ignacio Aramendía 	 * read/write.
426ed264e8aSJoaquín Ignacio Aramendía 	 */
427ed264e8aSJoaquín Ignacio Aramendía 	dmi_entry = dmi_first_match(dmi_table);
428ed264e8aSJoaquín Ignacio Aramendía 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
429ed264e8aSJoaquín Ignacio Aramendía 		return -ENODEV;
430ed264e8aSJoaquín Ignacio Aramendía 
4315d06ec42SJoaquín Ignacio Aramendía 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
4323ca0f12aSJoaquín Ignacio Aramendía 
433be144ee4SJoaquín Ignacio Aramendía 	switch (board) {
434be144ee4SJoaquín Ignacio Aramendía 	case aok_zoe_a1:
435be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_a07:
436be144ee4SJoaquín Ignacio Aramendía 	case oxp_mini_amd_pro:
437be144ee4SJoaquín Ignacio Aramendía 		ret = devm_device_add_groups(dev, oxp_ec_groups);
438be144ee4SJoaquín Ignacio Aramendía 		if (ret)
439be144ee4SJoaquín Ignacio Aramendía 			return ret;
440be144ee4SJoaquín Ignacio Aramendía 		break;
441be144ee4SJoaquín Ignacio Aramendía 	default:
442be144ee4SJoaquín Ignacio Aramendía 		break;
443be144ee4SJoaquín Ignacio Aramendía 	}
444be144ee4SJoaquín Ignacio Aramendía 
445ed264e8aSJoaquín Ignacio Aramendía 	hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
446ed264e8aSJoaquín Ignacio Aramendía 						     &oxp_ec_chip_info, NULL);
447ed264e8aSJoaquín Ignacio Aramendía 
448ed264e8aSJoaquín Ignacio Aramendía 	return PTR_ERR_OR_ZERO(hwdev);
449ed264e8aSJoaquín Ignacio Aramendía }
450ed264e8aSJoaquín Ignacio Aramendía 
451ed264e8aSJoaquín Ignacio Aramendía static struct platform_driver oxp_platform_driver = {
452ed264e8aSJoaquín Ignacio Aramendía 	.driver = {
453ed264e8aSJoaquín Ignacio Aramendía 		.name = "oxp-platform",
454ed264e8aSJoaquín Ignacio Aramendía 	},
455ed264e8aSJoaquín Ignacio Aramendía 	.probe = oxp_platform_probe,
456ed264e8aSJoaquín Ignacio Aramendía };
457ed264e8aSJoaquín Ignacio Aramendía 
458ed264e8aSJoaquín Ignacio Aramendía static struct platform_device *oxp_platform_device;
459ed264e8aSJoaquín Ignacio Aramendía 
460ed264e8aSJoaquín Ignacio Aramendía static int __init oxp_platform_init(void)
461ed264e8aSJoaquín Ignacio Aramendía {
462ed264e8aSJoaquín Ignacio Aramendía 	oxp_platform_device =
463ed264e8aSJoaquín Ignacio Aramendía 		platform_create_bundle(&oxp_platform_driver,
464ed264e8aSJoaquín Ignacio Aramendía 				       oxp_platform_probe, NULL, 0, NULL, 0);
465ed264e8aSJoaquín Ignacio Aramendía 
466ed264e8aSJoaquín Ignacio Aramendía 	return PTR_ERR_OR_ZERO(oxp_platform_device);
467ed264e8aSJoaquín Ignacio Aramendía }
468ed264e8aSJoaquín Ignacio Aramendía 
469ed264e8aSJoaquín Ignacio Aramendía static void __exit oxp_platform_exit(void)
470ed264e8aSJoaquín Ignacio Aramendía {
471ed264e8aSJoaquín Ignacio Aramendía 	platform_device_unregister(oxp_platform_device);
472ed264e8aSJoaquín Ignacio Aramendía 	platform_driver_unregister(&oxp_platform_driver);
473ed264e8aSJoaquín Ignacio Aramendía }
474ed264e8aSJoaquín Ignacio Aramendía 
475ed264e8aSJoaquín Ignacio Aramendía MODULE_DEVICE_TABLE(dmi, dmi_table);
476ed264e8aSJoaquín Ignacio Aramendía 
477ed264e8aSJoaquín Ignacio Aramendía module_init(oxp_platform_init);
478ed264e8aSJoaquín Ignacio Aramendía module_exit(oxp_platform_exit);
479ed264e8aSJoaquín Ignacio Aramendía 
480ed264e8aSJoaquín Ignacio Aramendía MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
481ed264e8aSJoaquín Ignacio Aramendía MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
482ed264e8aSJoaquín Ignacio Aramendía MODULE_LICENSE("GPL");
483