File Index
Files and directories
Sign in
FILE

oxpec.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/oxpec.c C
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Platform driver for OneXPlayer and AOKZOE devices.
   4 *
   5 * Fan control is provided via pwm interface in the range [0-255].
   6 * Old AMD boards use [0-100] as range in the EC, the written value is
   7 * scaled to accommodate for that. Newer boards like the mini PRO and
   8 * AOKZOE are not scaled but have the same EC layout. Newer models
   9 * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi
  10 * are [1-244] and scaled to 0-255.
  11 *
  12 * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
  13 * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
  14 * Copyright (C) 2025-2026 Antheas Kapenekakis <lkml@antheas.dev>
  15 */
  16
  17#include <linux/acpi.h>
  18#include <linux/dmi.h>
  19#include <linux/hwmon.h>
  20#include <linux/init.h>
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/platform_device.h>
  24#include <linux/processor.h>
  25#include <acpi/battery.h>
  26
  27/* Handle ACPI lock mechanism */
  28static u32 oxp_mutex;
  29
  30#define ACPI_LOCK_DELAY_MS	500
  31
  32static bool lock_global_acpi_lock(void)
  33{
  34	return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
  35}
  36
  37static bool unlock_global_acpi_lock(void)
  38{
  39	return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
  40}
  41
  42enum oxp_board {
  43	aok_zoe_a1 = 1,
  44	orange_pi_neo,
  45	oxp_2,
  46	oxp_fly,
  47	oxp_mini_amd,
  48	oxp_mini_amd_a07,
  49	oxp_mini_amd_pro,
  50	oxp_x1,
  51	oxp_g1_i,
  52	oxp_g1_a,
  53};
  54
  55static enum oxp_board board;
  56static struct device *oxp_dev;
  57
  58/* Fan reading and PWM */
  59#define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */
  60#define OXP_2_SENSOR_FAN_REG		0x58 /* Fan reading is 2 registers long */
  61#define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */
  62#define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register long */
  63#define PWM_MODE_AUTO			0x00
  64#define PWM_MODE_MANUAL			0x01
  65
  66/* OrangePi fan reading and PWM */
  67#define ORANGEPI_SENSOR_FAN_REG		0x78 /* Fan reading is 2 registers long */
  68#define ORANGEPI_SENSOR_PWM_ENABLE_REG	0x40 /* PWM enable is 1 register long */
  69#define ORANGEPI_SENSOR_PWM_REG		0x38 /* PWM reading is 1 register long */
  70
  71/* Turbo button takeover function
  72 * Different boards have different values and EC registers
  73 * for the same function
  74 */
  75#define OXP_TURBO_SWITCH_REG		0xF1 /* Mini Pro, OneXFly, AOKZOE */
  76#define OXP_2_TURBO_SWITCH_REG		0xEB /* OXP2 and X1 */
  77#define OXP_MINI_TURBO_SWITCH_REG	0x1E /* Mini AO7 */
  78
  79#define OXP_MINI_TURBO_TAKE_VAL		0x01 /* Mini AO7 */
  80#define OXP_TURBO_TAKE_VAL		0x40 /* All other models */
  81
  82/* X1 Turbo LED */
  83#define OXP_X1_TURBO_LED_REG		0x57
  84
  85#define OXP_X1_TURBO_LED_OFF		0x01
  86#define OXP_X1_TURBO_LED_ON		0x02
  87
  88/* Battery extension settings */
  89#define EC_CHARGE_CONTROL_BEHAVIOURS	(BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |		\
  90					 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) |	\
  91					 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE))
  92
  93#define OXP_X1_CHARGE_LIMIT_REG		0xA3 /* X1 charge limit (%) */
  94#define OXP_X1_CHARGE_INHIBIT_REG	0xA4 /* X1 bypass charging */
  95
  96#define OXP_X1_CHARGE_INHIBIT_MASK_AWAKE	0x01
  97/* X1 Mask is 0x0A, F1Pro is 0x02 but the extra bit on the X1 does nothing. */
  98#define OXP_X1_CHARGE_INHIBIT_MASK_OFF		0x02
  99#define OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS	(OXP_X1_CHARGE_INHIBIT_MASK_AWAKE | \
 100						 OXP_X1_CHARGE_INHIBIT_MASK_OFF)
 101
 102static const struct dmi_system_id dmi_table[] = {
 103	{
 104		.matches = {
 105			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
 106			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
 107		},
 108		.driver_data = (void *)aok_zoe_a1,
 109	},
 110	{
 111		.matches = {
 112			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
 113			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
 114		},
 115		.driver_data = (void *)aok_zoe_a1,
 116	},
 117	{
 118		.matches = {
 119			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
 120			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A2 Pro"),
 121		},
 122		.driver_data = (void *)aok_zoe_a1,
 123	},
 124	{
 125		.matches = {
 126			DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
 127			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1X"),
 128		},
 129		.driver_data = (void *)oxp_fly,
 130	},
 131	{
 132		.matches = {
 133			DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
 134			DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),
 135		},
 136		.driver_data = (void *)orange_pi_neo,
 137	},
 138	{
 139		.matches = {
 140			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 141			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
 142		},
 143		.driver_data = (void *)oxp_mini_amd,
 144	},
 145	{
 146		.matches = {
 147			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 148			DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),
 149		},
 150		.driver_data = (void *)oxp_2,
 151	},
 152	{
 153		.matches = {
 154			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 155			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER APEX"),
 156		},
 157		.driver_data = (void *)oxp_fly,
 158	},
 159	{
 160		.matches = {
 161			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 162			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),
 163		},
 164		.driver_data = (void *)oxp_fly,
 165	},
 166	{
 167		.matches = {
 168			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 169			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-01"),
 170		},
 171		.driver_data = (void *)oxp_fly,
 172	},
 173	{
 174		.matches = {
 175			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 176			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 OLED"),
 177		},
 178		.driver_data = (void *)oxp_fly,
 179	},
 180	{
 181		.matches = {
 182			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 183			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1L"),
 184		},
 185		.driver_data = (void *)oxp_fly,
 186	},
 187	{
 188		.matches = {
 189			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 190			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1Pro"),
 191		},
 192		.driver_data = (void *)oxp_fly,
 193	},
 194	{
 195		.matches = {
 196			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 197			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-02"),
 198		},
 199		.driver_data = (void *)oxp_fly,
 200	},
 201	{
 202		.matches = {
 203			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 204			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"),
 205		},
 206		.driver_data = (void *)oxp_g1_a,
 207	},
 208	{
 209		.matches = {
 210			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 211			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"),
 212		},
 213		.driver_data = (void *)oxp_g1_i,
 214	},
 215	{
 216		.matches = {
 217			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 218			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
 219		},
 220		.driver_data = (void *)oxp_mini_amd_a07,
 221	},
 222	{
 223		.matches = {
 224			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 225			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
 226		},
 227		.driver_data = (void *)oxp_mini_amd_pro,
 228	},
 229	{
 230		.matches = {
 231			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 232			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1z"),
 233		},
 234		.driver_data = (void *)oxp_x1,
 235	},
 236	{
 237		.matches = {
 238			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 239			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 A"),
 240		},
 241		.driver_data = (void *)oxp_x1,
 242	},
 243	{
 244		.matches = {
 245			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 246			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 i"),
 247		},
 248		.driver_data = (void *)oxp_x1,
 249	},
 250	{
 251		.matches = {
 252			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 253			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Air"),
 254		},
 255		.driver_data = (void *)oxp_x1,
 256	},
 257	{
 258		.matches = {
 259			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 260			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 mini"),
 261		},
 262		.driver_data = (void *)oxp_x1,
 263	},
 264	{
 265		.matches = {
 266			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 267			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Mini Pro"),
 268		},
 269		.driver_data = (void *)oxp_x1,
 270	},
 271	{
 272		.matches = {
 273			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 274			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro"),
 275		},
 276		.driver_data = (void *)oxp_x1,
 277	},
 278	{
 279		.matches = {
 280			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
 281			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro EVA-02"),
 282		},
 283		.driver_data = (void *)oxp_x1,
 284	},
 285	{},
 286};
 287
 288/* Helper functions to handle EC read/write */
 289static int read_from_ec(u8 reg, int size, long *val)
 290{
 291	u8 buffer;
 292	int ret;
 293	int i;
 294
 295	if (!lock_global_acpi_lock())
 296		return -EBUSY;
 297
 298	*val = 0;
 299	for (i = 0; i < size; i++) {
 300		ret = ec_read(reg + i, &buffer);
 301		if (ret)
 302			return ret;
 303		*val <<= i * 8;
 304		*val += buffer;
 305	}
 306
 307	if (!unlock_global_acpi_lock())
 308		return -EBUSY;
 309
 310	return 0;
 311}
 312
 313static int write_to_ec(u8 reg, u8 value)
 314{
 315	int ret;
 316
 317	if (!lock_global_acpi_lock())
 318		return -EBUSY;
 319
 320	ret = ec_write(reg, value);
 321
 322	if (!unlock_global_acpi_lock())
 323		return -EBUSY;
 324
 325	return ret;
 326}
 327
 328/* Callbacks for turbo toggle attribute */
 329static umode_t tt_toggle_is_visible(struct kobject *kobj,
 330				    struct attribute *attr, int n)
 331{
 332	switch (board) {
 333	case aok_zoe_a1:
 334	case oxp_2:
 335	case oxp_fly:
 336	case oxp_mini_amd_a07:
 337	case oxp_mini_amd_pro:
 338	case oxp_x1:
 339	case oxp_g1_i:
 340	case oxp_g1_a:
 341		return attr->mode;
 342	default:
 343		break;
 344	}
 345	return 0;
 346}
 347
 348static ssize_t tt_toggle_store(struct device *dev,
 349			       struct device_attribute *attr, const char *buf,
 350			       size_t count)
 351{
 352	u8 reg, mask, val;
 353	long raw_val;
 354	bool enable;
 355	int ret;
 356
 357	ret = kstrtobool(buf, &enable);
 358	if (ret)
 359		return ret;
 360
 361	switch (board) {
 362	case oxp_mini_amd_a07:
 363		reg = OXP_MINI_TURBO_SWITCH_REG;
 364		mask = OXP_MINI_TURBO_TAKE_VAL;
 365		break;
 366	case aok_zoe_a1:
 367	case oxp_fly:
 368	case oxp_mini_amd_pro:
 369	case oxp_g1_a:
 370		reg = OXP_TURBO_SWITCH_REG;
 371		mask = OXP_TURBO_TAKE_VAL;
 372		break;
 373	case oxp_2:
 374	case oxp_x1:
 375	case oxp_g1_i:
 376		reg = OXP_2_TURBO_SWITCH_REG;
 377		mask = OXP_TURBO_TAKE_VAL;
 378		break;
 379	default:
 380		return -EINVAL;
 381	}
 382
 383	ret = read_from_ec(reg, 1, &raw_val);
 384	if (ret)
 385		return ret;
 386
 387	val = raw_val;
 388	if (enable)
 389		val |= mask;
 390	else
 391		val &= ~mask;
 392
 393	ret = write_to_ec(reg, val);
 394	if (ret)
 395		return ret;
 396
 397	return count;
 398}
 399
 400static ssize_t tt_toggle_show(struct device *dev,
 401			      struct device_attribute *attr, char *buf)
 402{
 403	u8 reg, mask;
 404	int retval;
 405	long val;
 406
 407	switch (board) {
 408	case oxp_mini_amd_a07:
 409		reg = OXP_MINI_TURBO_SWITCH_REG;
 410		mask = OXP_MINI_TURBO_TAKE_VAL;
 411		break;
 412	case aok_zoe_a1:
 413	case oxp_fly:
 414	case oxp_mini_amd_pro:
 415	case oxp_g1_a:
 416		reg = OXP_TURBO_SWITCH_REG;
 417		mask = OXP_TURBO_TAKE_VAL;
 418		break;
 419	case oxp_2:
 420	case oxp_x1:
 421	case oxp_g1_i:
 422		reg = OXP_2_TURBO_SWITCH_REG;
 423		mask = OXP_TURBO_TAKE_VAL;
 424		break;
 425	default:
 426		return -EINVAL;
 427	}
 428
 429	retval = read_from_ec(reg, 1, &val);
 430	if (retval)
 431		return retval;
 432
 433	return sysfs_emit(buf, "%d\n", (val & mask) == mask);
 434}
 435
 436static DEVICE_ATTR_RW(tt_toggle);
 437
 438/* Callbacks for turbo LED attribute */
 439static umode_t tt_led_is_visible(struct kobject *kobj,
 440				 struct attribute *attr, int n)
 441{
 442	switch (board) {
 443	case oxp_x1:
 444		return attr->mode;
 445	default:
 446		break;
 447	}
 448	return 0;
 449}
 450
 451static ssize_t tt_led_store(struct device *dev,
 452			    struct device_attribute *attr, const char *buf,
 453			    size_t count)
 454{
 455	u8 reg, val;
 456	bool value;
 457	int ret;
 458
 459	ret = kstrtobool(buf, &value);
 460	if (ret)
 461		return ret;
 462
 463	switch (board) {
 464	case oxp_x1:
 465		reg = OXP_X1_TURBO_LED_REG;
 466		val = value ? OXP_X1_TURBO_LED_ON : OXP_X1_TURBO_LED_OFF;
 467		break;
 468	default:
 469		return -EINVAL;
 470	}
 471
 472	ret = write_to_ec(reg, val);
 473	if (ret)
 474		return ret;
 475
 476	return count;
 477}
 478
 479static ssize_t tt_led_show(struct device *dev,
 480			   struct device_attribute *attr, char *buf)
 481{
 482	long enval;
 483	long val;
 484	int ret;
 485	u8 reg;
 486
 487	switch (board) {
 488	case oxp_x1:
 489		reg = OXP_X1_TURBO_LED_REG;
 490		enval = OXP_X1_TURBO_LED_ON;
 491		break;
 492	default:
 493		return -EINVAL;
 494	}
 495
 496	ret = read_from_ec(reg, 1, &val);
 497	if (ret)
 498		return ret;
 499
 500	return sysfs_emit(buf, "%d\n", val == enval);
 501}
 502
 503static DEVICE_ATTR_RW(tt_led);
 504
 505/* Callbacks for charge behaviour attributes */
 506static bool oxp_psy_ext_supported(void)
 507{
 508	switch (board) {
 509	case oxp_x1:
 510	case oxp_g1_i:
 511	case oxp_g1_a:
 512	case oxp_fly:
 513		return true;
 514	default:
 515		break;
 516	}
 517	return false;
 518}
 519
 520static int oxp_psy_ext_get_prop(struct power_supply *psy,
 521				const struct power_supply_ext *ext,
 522				void *data,
 523				enum power_supply_property psp,
 524				union power_supply_propval *val)
 525{
 526	long raw_val;
 527	int ret;
 528
 529	switch (psp) {
 530	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
 531		ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
 532		if (ret)
 533			return ret;
 534		if (raw_val < 0 || raw_val > 100)
 535			return -EINVAL;
 536		val->intval = raw_val;
 537		return 0;
 538	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
 539		ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
 540		if (ret)
 541			return ret;
 542		if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
 543		    OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS)
 544			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
 545		else if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_AWAKE) ==
 546			 OXP_X1_CHARGE_INHIBIT_MASK_AWAKE)
 547			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
 548		else
 549			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
 550		return 0;
 551	default:
 552		return -EINVAL;
 553	}
 554}
 555
 556static int oxp_psy_ext_set_prop(struct power_supply *psy,
 557				const struct power_supply_ext *ext,
 558				void *data,
 559				enum power_supply_property psp,
 560				const union power_supply_propval *val)
 561{
 562	long raw_val;
 563
 564	switch (psp) {
 565	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
 566		if (val->intval < 0 || val->intval > 100)
 567			return -EINVAL;
 568		return write_to_ec(OXP_X1_CHARGE_LIMIT_REG, val->intval);
 569	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
 570		switch (val->intval) {
 571		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
 572			raw_val = 0;
 573			break;
 574		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE:
 575			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_AWAKE;
 576			break;
 577		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
 578			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
 579			break;
 580		default:
 581			return -EINVAL;
 582		}
 583
 584		return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
 585	default:
 586		return -EINVAL;
 587	}
 588}
 589
 590static int oxp_psy_prop_is_writeable(struct power_supply *psy,
 591				     const struct power_supply_ext *ext,
 592				     void *data,
 593				     enum power_supply_property psp)
 594{
 595	return true;
 596}
 597
 598static const enum power_supply_property oxp_psy_ext_props[] = {
 599	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
 600	POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
 601};
 602
 603static const struct power_supply_ext oxp_psy_ext = {
 604	.name			= "oxp-charge-control",
 605	.properties		= oxp_psy_ext_props,
 606	.num_properties		= ARRAY_SIZE(oxp_psy_ext_props),
 607	.charge_behaviours	= EC_CHARGE_CONTROL_BEHAVIOURS,
 608	.get_property		= oxp_psy_ext_get_prop,
 609	.set_property		= oxp_psy_ext_set_prop,
 610	.property_is_writeable	= oxp_psy_prop_is_writeable,
 611};
 612
 613static int oxp_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
 614{
 615	return power_supply_register_extension(battery, &oxp_psy_ext, oxp_dev, NULL);
 616}
 617
 618static int oxp_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
 619{
 620	power_supply_unregister_extension(battery, &oxp_psy_ext);
 621	return 0;
 622}
 623
 624static struct acpi_battery_hook battery_hook = {
 625	.add_battery	= oxp_add_battery,
 626	.remove_battery	= oxp_remove_battery,
 627	.name		= "OneXPlayer Battery",
 628};
 629
 630/* PWM enable/disable functions */
 631static int oxp_pwm_enable(void)
 632{
 633	switch (board) {
 634	case orange_pi_neo:
 635		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
 636	case aok_zoe_a1:
 637	case oxp_2:
 638	case oxp_fly:
 639	case oxp_mini_amd:
 640	case oxp_mini_amd_a07:
 641	case oxp_mini_amd_pro:
 642	case oxp_x1:
 643	case oxp_g1_i:
 644	case oxp_g1_a:
 645		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
 646	default:
 647		return -EINVAL;
 648	}
 649}
 650
 651static int oxp_pwm_disable(void)
 652{
 653	switch (board) {
 654	case orange_pi_neo:
 655		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
 656	case aok_zoe_a1:
 657	case oxp_2:
 658	case oxp_fly:
 659	case oxp_mini_amd:
 660	case oxp_mini_amd_a07:
 661	case oxp_mini_amd_pro:
 662	case oxp_x1:
 663	case oxp_g1_i:
 664	case oxp_g1_a:
 665		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
 666	default:
 667		return -EINVAL;
 668	}
 669}
 670
 671static int oxp_pwm_read(long *val)
 672{
 673	switch (board) {
 674	case orange_pi_neo:
 675		return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
 676	case aok_zoe_a1:
 677	case oxp_2:
 678	case oxp_fly:
 679	case oxp_mini_amd:
 680	case oxp_mini_amd_a07:
 681	case oxp_mini_amd_pro:
 682	case oxp_x1:
 683	case oxp_g1_i:
 684	case oxp_g1_a:
 685		return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
 686	default:
 687		return -EOPNOTSUPP;
 688	}
 689}
 690
 691/* Callbacks for hwmon interface */
 692static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
 693				       enum hwmon_sensor_types type, u32 attr, int channel)
 694{
 695	switch (type) {
 696	case hwmon_fan:
 697		return 0444;
 698	case hwmon_pwm:
 699		return 0644;
 700	default:
 701		return 0;
 702	}
 703}
 704
 705/* Fan speed read function */
 706static int oxp_pwm_fan_speed(long *val)
 707{
 708	switch (board) {
 709	case orange_pi_neo:
 710		return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
 711	case oxp_2:
 712	case oxp_x1:
 713	case oxp_g1_i:
 714		return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
 715	case aok_zoe_a1:
 716	case oxp_fly:
 717	case oxp_mini_amd:
 718	case oxp_mini_amd_a07:
 719	case oxp_mini_amd_pro:
 720	case oxp_g1_a:
 721		return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
 722	default:
 723		return -EOPNOTSUPP;
 724	}
 725}
 726
 727/* PWM input read/write functions */
 728static int oxp_pwm_input_write(long val)
 729{
 730	if (val < 0 || val > 255)
 731		return -EINVAL;
 732
 733	switch (board) {
 734	case orange_pi_neo:
 735		/* scale to range [1-244] */
 736		val = ((val - 1) * 243 / 254) + 1;
 737		return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
 738	case oxp_2:
 739	case oxp_x1:
 740	case oxp_g1_i:
 741		/* scale to range [0-184] */
 742		val = (val * 184) / 255;
 743		return write_to_ec(OXP_SENSOR_PWM_REG, val);
 744	case oxp_mini_amd:
 745	case oxp_mini_amd_a07:
 746		/* scale to range [0-100] */
 747		val = (val * 100) / 255;
 748		return write_to_ec(OXP_SENSOR_PWM_REG, val);
 749	case aok_zoe_a1:
 750	case oxp_fly:
 751	case oxp_mini_amd_pro:
 752	case oxp_g1_a:
 753		return write_to_ec(OXP_SENSOR_PWM_REG, val);
 754	default:
 755		return -EOPNOTSUPP;
 756	}
 757}
 758
 759static int oxp_pwm_input_read(long *val)
 760{
 761	int ret;
 762
 763	switch (board) {
 764	case orange_pi_neo:
 765		ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
 766		if (ret)
 767			return ret;
 768		/* scale from range [1-244] */
 769		*val = ((*val - 1) * 254 / 243) + 1;
 770		break;
 771	case oxp_2:
 772	case oxp_x1:
 773	case oxp_g1_i:
 774		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
 775		if (ret)
 776			return ret;
 777		/* scale from range [0-184] */
 778		*val = (*val * 255) / 184;
 779		break;
 780	case oxp_mini_amd:
 781	case oxp_mini_amd_a07:
 782		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
 783		if (ret)
 784			return ret;
 785		/* scale from range [0-100] */
 786		*val = (*val * 255) / 100;
 787		break;
 788	case aok_zoe_a1:
 789	case oxp_fly:
 790	case oxp_mini_amd_pro:
 791	case oxp_g1_a:
 792	default:
 793		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
 794		if (ret)
 795			return ret;
 796		break;
 797	}
 798	return 0;
 799}
 800
 801static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
 802			     u32 attr, int channel, long *val)
 803{
 804	int ret;
 805
 806	switch (type) {
 807	case hwmon_fan:
 808		switch (attr) {
 809		case hwmon_fan_input:
 810			return oxp_pwm_fan_speed(val);
 811		default:
 812			break;
 813		}
 814		break;
 815	case hwmon_pwm:
 816		switch (attr) {
 817		case hwmon_pwm_input:
 818			return oxp_pwm_input_read(val);
 819		case hwmon_pwm_enable:
 820			ret = oxp_pwm_read(val);
 821			if (ret)
 822				return ret;
 823
 824			/* Check for auto and return 2 */
 825			if (!*val) {
 826				*val = 2;
 827				return 0;
 828			}
 829
 830			/* Return 0 if at full fan speed, 1 otherwise */
 831			ret = oxp_pwm_fan_speed(val);
 832			if (ret)
 833				return ret;
 834
 835			if (*val == 255)
 836				*val = 0;
 837			else
 838				*val = 1;
 839
 840			return 0;
 841		default:
 842			break;
 843		}
 844		break;
 845	default:
 846		break;
 847	}
 848	return -EOPNOTSUPP;
 849}
 850
 851static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
 852			      u32 attr, int channel, long val)
 853{
 854	int ret;
 855
 856	switch (type) {
 857	case hwmon_pwm:
 858		switch (attr) {
 859		case hwmon_pwm_enable:
 860			if (val == 1)
 861				return oxp_pwm_enable();
 862			else if (val == 2)
 863				return oxp_pwm_disable();
 864			else if (val != 0)
 865				return -EINVAL;
 866
 867			/* Enable PWM and set to max speed */
 868			ret = oxp_pwm_enable();
 869			if (ret)
 870				return ret;
 871			return oxp_pwm_input_write(255);
 872		case hwmon_pwm_input:
 873			return oxp_pwm_input_write(val);
 874		default:
 875			break;
 876		}
 877		break;
 878	default:
 879		break;
 880	}
 881	return -EOPNOTSUPP;
 882}
 883
 884/* Known sensors in the OXP EC controllers */
 885static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
 886	HWMON_CHANNEL_INFO(fan,
 887			   HWMON_F_INPUT),
 888	HWMON_CHANNEL_INFO(pwm,
 889			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
 890	NULL,
 891};
 892
 893static struct attribute *oxp_tt_toggle_attrs[] = {
 894	&dev_attr_tt_toggle.attr,
 895	NULL
 896};
 897
 898static const struct attribute_group oxp_tt_toggle_attribute_group = {
 899	.is_visible = tt_toggle_is_visible,
 900	.attrs = oxp_tt_toggle_attrs,
 901};
 902
 903static struct attribute *oxp_tt_led_attrs[] = {
 904	&dev_attr_tt_led.attr,
 905	NULL
 906};
 907
 908static const struct attribute_group oxp_tt_led_attribute_group = {
 909	.is_visible = tt_led_is_visible,
 910	.attrs = oxp_tt_led_attrs,
 911};
 912
 913static const struct attribute_group *oxp_ec_groups[] = {
 914	&oxp_tt_toggle_attribute_group,
 915	&oxp_tt_led_attribute_group,
 916	NULL
 917};
 918
 919static const struct hwmon_ops oxp_ec_hwmon_ops = {
 920	.is_visible = oxp_ec_hwmon_is_visible,
 921	.read = oxp_platform_read,
 922	.write = oxp_platform_write,
 923};
 924
 925static const struct hwmon_chip_info oxp_ec_chip_info = {
 926	.ops = &oxp_ec_hwmon_ops,
 927	.info = oxp_platform_sensors,
 928};
 929
 930/* Initialization logic */
 931static int oxp_platform_probe(struct platform_device *pdev)
 932{
 933	struct device *dev = &pdev->dev;
 934	struct device *hwdev;
 935	int ret;
 936
 937	oxp_dev = dev;
 938	hwdev = devm_hwmon_device_register_with_info(dev, "oxp_ec", NULL,
 939						     &oxp_ec_chip_info, NULL);
 940
 941	if (IS_ERR(hwdev))
 942		return PTR_ERR(hwdev);
 943
 944	if (oxp_psy_ext_supported()) {
 945		ret = devm_battery_hook_register(dev, &battery_hook);
 946		if (ret)
 947			return ret;
 948	}
 949
 950	return 0;
 951}
 952
 953static struct platform_driver oxp_platform_driver = {
 954	.driver = {
 955		.name = "oxp-platform",
 956		.dev_groups = oxp_ec_groups,
 957	},
 958	.probe = oxp_platform_probe,
 959};
 960
 961static struct platform_device *oxp_platform_device;
 962
 963static int __init oxp_platform_init(void)
 964{
 965	const struct dmi_system_id *dmi_entry;
 966
 967	dmi_entry = dmi_first_match(dmi_table);
 968	if (!dmi_entry)
 969		return -ENODEV;
 970
 971	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
 972
 973	/*
 974	 * Have to check for AMD processor here because DMI strings are the same
 975	 * between Intel and AMD boards on older OneXPlayer devices, the only way
 976	 * to tell them apart is the CPU. Old Intel boards have an unsupported EC.
 977	 */
 978	if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
 979		return -ENODEV;
 980
 981	oxp_platform_device =
 982		platform_create_bundle(&oxp_platform_driver,
 983				       oxp_platform_probe, NULL, 0, NULL, 0);
 984
 985	return PTR_ERR_OR_ZERO(oxp_platform_device);
 986}
 987
 988static void __exit oxp_platform_exit(void)
 989{
 990	platform_device_unregister(oxp_platform_device);
 991	platform_driver_unregister(&oxp_platform_driver);
 992}
 993
 994MODULE_DEVICE_TABLE(dmi, dmi_table);
 995
 996module_init(oxp_platform_init);
 997module_exit(oxp_platform_exit);
 998
 999MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
1000MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
1001MODULE_LICENSE("GPL");
~/ Read-only file preview.