brintos

brintos / linux-shallow public Read only

0
0
Text · 15.7 KiB · 83730d9 Raw
717 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds4 * that expose fan reading and control via hwmon sysfs.5 *6 * Old OXP boards have the same DMI strings and they are told apart by7 * the boot cpu vendor (Intel/AMD). Of these older models only AMD is8 * supported.9 *10 * Fan control is provided via pwm interface in the range [0-255].11 * Old AMD boards use [0-100] as range in the EC, the written value is12 * scaled to accommodate for that. Newer boards like the mini PRO and13 * AOKZOE are not scaled but have the same EC layout. Newer models14 * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi15 * are [1-244] and scaled to 0-255.16 *17 * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>18 * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>19 */20 21#include <linux/acpi.h>22#include <linux/dmi.h>23#include <linux/hwmon.h>24#include <linux/init.h>25#include <linux/kernel.h>26#include <linux/module.h>27#include <linux/platform_device.h>28#include <linux/processor.h>29 30/* Handle ACPI lock mechanism */31static u32 oxp_mutex;32 33#define ACPI_LOCK_DELAY_MS	50034 35static bool lock_global_acpi_lock(void)36{37	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));38}39 40static bool unlock_global_acpi_lock(void)41{42	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));43}44 45enum oxp_board {46	aok_zoe_a1 = 1,47	aya_neo_2,48	aya_neo_air,49	aya_neo_air_1s,50	aya_neo_air_plus_mendo,51	aya_neo_air_pro,52	aya_neo_flip,53	aya_neo_geek,54	aya_neo_kun,55	orange_pi_neo,56	oxp_2,57	oxp_fly,58	oxp_mini_amd,59	oxp_mini_amd_a07,60	oxp_mini_amd_pro,61	oxp_x1,62};63 64static enum oxp_board board;65 66/* Fan reading and PWM */67#define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */68#define OXP_2_SENSOR_FAN_REG           0x58 /* Fan reading is 2 registers long */69#define OXP_SENSOR_PWM_ENABLE_REG      0x4A /* PWM enable is 1 register long */70#define OXP_SENSOR_PWM_REG             0x4B /* PWM reading is 1 register long */71#define PWM_MODE_AUTO                  0x0072#define PWM_MODE_MANUAL                0x0173 74/* OrangePi fan reading and PWM */75#define ORANGEPI_SENSOR_FAN_REG        0x78 /* Fan reading is 2 registers long */76#define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */77#define ORANGEPI_SENSOR_PWM_REG        0x38 /* PWM reading is 1 register long */78 79/* Turbo button takeover function80 * Different boards have different values and EC registers81 * for the same function82 */83#define OXP_TURBO_SWITCH_REG           0xF1 /* Mini Pro, OneXFly, AOKZOE */84#define OXP_2_TURBO_SWITCH_REG         0xEB /* OXP2 and X1 */85#define OXP_MINI_TURBO_SWITCH_REG      0x1E /* Mini AO7 */86 87#define OXP_MINI_TURBO_TAKE_VAL        0x01 /* Mini AO7 */88#define OXP_TURBO_TAKE_VAL             0x40 /* All other models */89 90#define OXP_TURBO_RETURN_VAL           0x00 /* Common return val */91 92static const struct dmi_system_id dmi_table[] = {93	{94		.matches = {95			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),96			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),97		},98		.driver_data = (void *)aok_zoe_a1,99	},100	{101		.matches = {102			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),103			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),104		},105		.driver_data = (void *)aok_zoe_a1,106	},107	{108		.matches = {109			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),110			DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),111		},112		.driver_data = (void *)aya_neo_2,113	},114	{115		.matches = {116			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),117			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),118		},119		.driver_data = (void *)aya_neo_air,120	},121	{122		.matches = {123			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),124			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),125		},126		.driver_data = (void *)aya_neo_air_1s,127	},128	{129		.matches = {130			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),131			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),132		},133		.driver_data = (void *)aya_neo_air_plus_mendo,134	},135	{136		.matches = {137			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),138			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),139		},140		.driver_data = (void *)aya_neo_air_pro,141	},142	{143		.matches = {144			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),145			DMI_MATCH(DMI_BOARD_NAME, "FLIP"),146		},147		.driver_data = (void *)aya_neo_flip,148	},149	{150		.matches = {151			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),152			DMI_MATCH(DMI_BOARD_NAME, "GEEK"),153		},154		.driver_data = (void *)aya_neo_geek,155	},156	{157		.matches = {158			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),159			DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),160		},161		.driver_data = (void *)aya_neo_kun,162	},163	{164		.matches = {165			DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),166			DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),167		},168		.driver_data = (void *)orange_pi_neo,169	},170	{171		.matches = {172			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),173			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),174		},175		.driver_data = (void *)oxp_mini_amd,176	},177	{178		.matches = {179			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),180			DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),181		},182		.driver_data = (void *)oxp_2,183	},184	{185		.matches = {186			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),187			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),188		},189		.driver_data = (void *)oxp_fly,190	},191	{192		.matches = {193			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),194			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),195		},196		.driver_data = (void *)oxp_mini_amd_a07,197	},198	{199		.matches = {200			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),201			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),202		},203		.driver_data = (void *)oxp_mini_amd_pro,204	},205	{206		.matches = {207			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),208			DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"),209		},210		.driver_data = (void *)oxp_x1,211	},212	{},213};214 215/* Helper functions to handle EC read/write */216static int read_from_ec(u8 reg, int size, long *val)217{218	int i;219	int ret;220	u8 buffer;221 222	if (!lock_global_acpi_lock())223		return -EBUSY;224 225	*val = 0;226	for (i = 0; i < size; i++) {227		ret = ec_read(reg + i, &buffer);228		if (ret)229			return ret;230		*val <<= i * 8;231		*val += buffer;232	}233 234	if (!unlock_global_acpi_lock())235		return -EBUSY;236 237	return 0;238}239 240static int write_to_ec(u8 reg, u8 value)241{242	int ret;243 244	if (!lock_global_acpi_lock())245		return -EBUSY;246 247	ret = ec_write(reg, value);248 249	if (!unlock_global_acpi_lock())250		return -EBUSY;251 252	return ret;253}254 255/* Turbo button toggle functions */256static int tt_toggle_enable(void)257{258	u8 reg;259	u8 val;260 261	switch (board) {262	case oxp_mini_amd_a07:263		reg = OXP_MINI_TURBO_SWITCH_REG;264		val = OXP_MINI_TURBO_TAKE_VAL;265		break;266	case aok_zoe_a1:267	case oxp_fly:268	case oxp_mini_amd_pro:269		reg = OXP_TURBO_SWITCH_REG;270		val = OXP_TURBO_TAKE_VAL;271		break;272	case oxp_2:273	case oxp_x1:274		reg = OXP_2_TURBO_SWITCH_REG;275		val = OXP_TURBO_TAKE_VAL;276		break;277	default:278		return -EINVAL;279	}280	return write_to_ec(reg, val);281}282 283static int tt_toggle_disable(void)284{285	u8 reg;286	u8 val;287 288	switch (board) {289	case oxp_mini_amd_a07:290		reg = OXP_MINI_TURBO_SWITCH_REG;291		val = OXP_TURBO_RETURN_VAL;292		break;293	case aok_zoe_a1:294	case oxp_fly:295	case oxp_mini_amd_pro:296		reg = OXP_TURBO_SWITCH_REG;297		val = OXP_TURBO_RETURN_VAL;298		break;299	case oxp_2:300	case oxp_x1:301		reg = OXP_2_TURBO_SWITCH_REG;302		val = OXP_TURBO_RETURN_VAL;303		break;304	default:305		return -EINVAL;306	}307	return write_to_ec(reg, val);308}309 310/* Callbacks for turbo toggle attribute */311static umode_t tt_toggle_is_visible(struct kobject *kobj,312				    struct attribute *attr, int n)313{314	switch (board) {315	case aok_zoe_a1:316	case oxp_2:317	case oxp_fly:318	case oxp_mini_amd_a07:319	case oxp_mini_amd_pro:320	case oxp_x1:321		return attr->mode;322	default:323		break;324	}325	return 0;326}327 328static ssize_t tt_toggle_store(struct device *dev,329			       struct device_attribute *attr, const char *buf,330			       size_t count)331{332	int rval;333	bool value;334 335	rval = kstrtobool(buf, &value);336	if (rval)337		return rval;338 339	if (value) {340		rval = tt_toggle_enable();341	} else {342		rval = tt_toggle_disable();343	}344	if (rval)345		return rval;346 347	return count;348}349 350static ssize_t tt_toggle_show(struct device *dev,351			      struct device_attribute *attr, char *buf)352{353	int retval;354	u8 reg;355	long val;356 357	switch (board) {358	case oxp_mini_amd_a07:359		reg = OXP_MINI_TURBO_SWITCH_REG;360		break;361	case aok_zoe_a1:362	case oxp_fly:363	case oxp_mini_amd_pro:364		reg = OXP_TURBO_SWITCH_REG;365		break;366	case oxp_2:367	case oxp_x1:368		reg = OXP_2_TURBO_SWITCH_REG;369		break;370	default:371		return -EINVAL;372	}373 374	retval = read_from_ec(reg, 1, &val);375	if (retval)376		return retval;377 378	return sysfs_emit(buf, "%d\n", !!val);379}380 381static DEVICE_ATTR_RW(tt_toggle);382 383/* PWM enable/disable functions */384static int oxp_pwm_enable(void)385{386	switch (board) {387	case orange_pi_neo:388		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);389	case aok_zoe_a1:390	case aya_neo_2:391	case aya_neo_air:392	case aya_neo_air_plus_mendo:393	case aya_neo_air_pro:394	case aya_neo_flip:395	case aya_neo_geek:396	case aya_neo_kun:397	case oxp_2:398	case oxp_fly:399	case oxp_mini_amd:400	case oxp_mini_amd_a07:401	case oxp_mini_amd_pro:402	case oxp_x1:403		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);404	default:405		return -EINVAL;406	}407}408 409static int oxp_pwm_disable(void)410{411	switch (board) {412	case orange_pi_neo:413		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);414	case aok_zoe_a1:415	case aya_neo_2:416	case aya_neo_air:417	case aya_neo_air_1s:418	case aya_neo_air_plus_mendo:419	case aya_neo_air_pro:420	case aya_neo_flip:421	case aya_neo_geek:422	case aya_neo_kun:423	case oxp_2:424	case oxp_fly:425	case oxp_mini_amd:426	case oxp_mini_amd_a07:427	case oxp_mini_amd_pro:428	case oxp_x1:429		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);430	default:431		return -EINVAL;432	}433}434 435/* Callbacks for hwmon interface */436static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,437				       enum hwmon_sensor_types type, u32 attr, int channel)438{439	switch (type) {440	case hwmon_fan:441		return 0444;442	case hwmon_pwm:443		return 0644;444	default:445		return 0;446	}447}448 449static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,450			     u32 attr, int channel, long *val)451{452	int ret;453 454	switch (type) {455	case hwmon_fan:456		switch (attr) {457		case hwmon_fan_input:458			switch (board) {459			case orange_pi_neo:460				return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);461			case oxp_2:462			case oxp_x1:463				return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);464			case aok_zoe_a1:465			case aya_neo_2:466			case aya_neo_air:467			case aya_neo_air_1s:468			case aya_neo_air_plus_mendo:469			case aya_neo_air_pro:470			case aya_neo_flip:471			case aya_neo_geek:472			case aya_neo_kun:473			case oxp_fly:474			case oxp_mini_amd:475			case oxp_mini_amd_a07:476			case oxp_mini_amd_pro:477				return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);478			default:479				break;480			}481			break;482		default:483			break;484		}485		break;486	case hwmon_pwm:487		switch (attr) {488		case hwmon_pwm_input:489			switch (board) {490			case orange_pi_neo:491				ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);492				if (ret)493					return ret;494				/* scale from range [1-244] */495				*val = ((*val - 1) * 254 / 243) + 1;496				break;497			case oxp_2:498			case oxp_x1:499				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);500				if (ret)501					return ret;502				/* scale from range [0-184] */503				*val = (*val * 255) / 184;504				break;505			case aya_neo_2:506			case aya_neo_air:507			case aya_neo_air_1s:508			case aya_neo_air_plus_mendo:509			case aya_neo_air_pro:510			case aya_neo_flip:511			case aya_neo_geek:512			case aya_neo_kun:513			case oxp_mini_amd:514			case oxp_mini_amd_a07:515				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);516				if (ret)517					return ret;518				/* scale from range [0-100] */519				*val = (*val * 255) / 100;520				break;521			case aok_zoe_a1:522			case oxp_fly:523			case oxp_mini_amd_pro:524			default:525				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);526				if (ret)527					return ret;528				break;529			}530			return 0;531		case hwmon_pwm_enable:532			switch (board) {533			case orange_pi_neo:534				return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);535			case aok_zoe_a1:536			case aya_neo_2:537			case aya_neo_air:538			case aya_neo_air_1s:539			case aya_neo_air_plus_mendo:540			case aya_neo_air_pro:541			case aya_neo_flip:542			case aya_neo_geek:543			case aya_neo_kun:544			case oxp_2:545			case oxp_fly:546			case oxp_mini_amd:547			case oxp_mini_amd_a07:548			case oxp_mini_amd_pro:549			case oxp_x1:550				return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);551			default:552				break;553			}554			break;555		default:556			break;557		}558		break;559	default:560		break;561	}562	return -EOPNOTSUPP;563}564 565static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,566			      u32 attr, int channel, long val)567{568	switch (type) {569	case hwmon_pwm:570		switch (attr) {571		case hwmon_pwm_enable:572			if (val == 1)573				return oxp_pwm_enable();574			else if (val == 0)575				return oxp_pwm_disable();576			return -EINVAL;577		case hwmon_pwm_input:578			if (val < 0 || val > 255)579				return -EINVAL;580			switch (board) {581			case orange_pi_neo:582				/* scale to range [1-244] */583				val = ((val - 1) * 243 / 254) + 1;584				return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);585			case oxp_2:586			case oxp_x1:587				/* scale to range [0-184] */588				val = (val * 184) / 255;589				return write_to_ec(OXP_SENSOR_PWM_REG, val);590			case aya_neo_2:591			case aya_neo_air:592			case aya_neo_air_1s:593			case aya_neo_air_plus_mendo:594			case aya_neo_air_pro:595			case aya_neo_flip:596			case aya_neo_geek:597			case aya_neo_kun:598			case oxp_mini_amd:599			case oxp_mini_amd_a07:600				/* scale to range [0-100] */601				val = (val * 100) / 255;602				return write_to_ec(OXP_SENSOR_PWM_REG, val);603			case aok_zoe_a1:604			case oxp_fly:605			case oxp_mini_amd_pro:606				return write_to_ec(OXP_SENSOR_PWM_REG, val);607			default:608				break;609			}610			break;611		default:612			break;613		}614		break;615	default:616		break;617	}618	return -EOPNOTSUPP;619}620 621/* Known sensors in the OXP EC controllers */622static const struct hwmon_channel_info * const oxp_platform_sensors[] = {623	HWMON_CHANNEL_INFO(fan,624			   HWMON_F_INPUT),625	HWMON_CHANNEL_INFO(pwm,626			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),627	NULL,628};629 630static struct attribute *oxp_ec_attrs[] = {631	&dev_attr_tt_toggle.attr,632	NULL633};634 635static struct attribute_group oxp_ec_attribute_group = {636	.is_visible = tt_toggle_is_visible,637	.attrs = oxp_ec_attrs,638};639 640static const struct attribute_group *oxp_ec_groups[] = {641	&oxp_ec_attribute_group,642	NULL643};644 645static const struct hwmon_ops oxp_ec_hwmon_ops = {646	.is_visible = oxp_ec_hwmon_is_visible,647	.read = oxp_platform_read,648	.write = oxp_platform_write,649};650 651static const struct hwmon_chip_info oxp_ec_chip_info = {652	.ops = &oxp_ec_hwmon_ops,653	.info = oxp_platform_sensors,654};655 656/* Initialization logic */657static int oxp_platform_probe(struct platform_device *pdev)658{659	struct device *dev = &pdev->dev;660	struct device *hwdev;661 662	hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,663						     &oxp_ec_chip_info, NULL);664 665	return PTR_ERR_OR_ZERO(hwdev);666}667 668static struct platform_driver oxp_platform_driver = {669	.driver = {670		.name = "oxp-platform",671		.dev_groups = oxp_ec_groups,672	},673	.probe = oxp_platform_probe,674};675 676static struct platform_device *oxp_platform_device;677 678static int __init oxp_platform_init(void)679{680	const struct dmi_system_id *dmi_entry;681 682	dmi_entry = dmi_first_match(dmi_table);683	if (!dmi_entry)684		return -ENODEV;685 686	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;687 688	/*689	 * Have to check for AMD processor here because DMI strings are the same690	 * between Intel and AMD boards on older OneXPlayer devices, the only way691	 * to tell them apart is the CPU. Old Intel boards have an unsupported EC.692	 */693	if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)694		return -ENODEV;695 696	oxp_platform_device =697		platform_create_bundle(&oxp_platform_driver,698				       oxp_platform_probe, NULL, 0, NULL, 0);699 700	return PTR_ERR_OR_ZERO(oxp_platform_device);701}702 703static void __exit oxp_platform_exit(void)704{705	platform_device_unregister(oxp_platform_device);706	platform_driver_unregister(&oxp_platform_driver);707}708 709MODULE_DEVICE_TABLE(dmi, dmi_table);710 711module_init(oxp_platform_init);712module_exit(oxp_platform_exit);713 714MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");715MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");716MODULE_LICENSE("GPL");717