All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Antheas Kapenekakis <lkml@antheas.dev>
Cc: platform-driver-x86@vger.kernel.org,
	LKML <linux-kernel@vger.kernel.org>,
	"Derek John Clark" <derekjohn.clark@gmail.com>,
	"Joaquín Ignacio Aramendía" <samsagax@gmail.com>,
	"Hans de Goede" <hansg@kernel.org>,
	Eileen <eileen@one-netbook.com>
Subject: Re: [PATCH v1 1/2] platform/x86: oxpec: Fix Apex charge limit control
Date: Mon, 5 Oct 2026 20:56:47 +0300 (EEST)	[thread overview]
Message-ID: <272b2d3c-243a-cfb8-bf34-c6cffc7e5fec@linux.intel.com> (raw)
In-Reply-To: <20260920190438.3444923-1-lkml@antheas.dev>

On Sun, 20 Sep 2026, Antheas Kapenekakis wrote:

> The OneXPlayer Apex uses E5/E6 for battery bypass / charge limit, making
> it the first device to deviate. This was verified by a user on Windows
> with BIOS version 2.17 and later by running this kernel patch.
> 
> Drop X1_ from the MASK variables as they are common in all devices,
> introduce a board enum for Apex, and make it use the new E5/E6 registers
> for charge limit / bypass. Apply the same quirks as oxp_fly, since APEX
> uses the same registers otherwise.
> 
> Fixes: 3385ea97c14d ("platform/x86: oxpec: Add support for OneXPlayer APEX")
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>  drivers/platform/x86/oxpec.c | 68 ++++++++++++++++++++++++++++--------
>  1 file changed, 53 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c
> index 34bb17fca148..38c272995437 100644
> --- a/drivers/platform/x86/oxpec.c
> +++ b/drivers/platform/x86/oxpec.c
> @@ -50,6 +50,7 @@ enum oxp_board {
>  	oxp_x1,
>  	oxp_g1_i,
>  	oxp_g1_a,
> +	oxp_apex,
>  };
>  
>  static enum oxp_board board;
> @@ -93,11 +94,14 @@ static struct device *oxp_dev;
>  #define OXP_X1_CHARGE_LIMIT_REG		0xA3 /* X1 charge limit (%) */
>  #define OXP_X1_CHARGE_INHIBIT_REG	0xA4 /* X1 bypass charging */
>  
> -#define OXP_X1_CHARGE_INHIBIT_MASK_AWAKE	0x01
> +#define OXP_APEX_CHARGE_LIMIT_REG	0xE5 /* Apex charge limit (%) */
> +#define OXP_APEX_CHARGE_INHIBIT_REG	0xE6 /* Apex bypass charging */
> +
> +#define OXP_CHARGE_INHIBIT_MASK_AWAKE	0x01
>  /* X1 Mask is 0x0A, F1Pro is 0x02 but the extra bit on the X1 does nothing. */
> -#define OXP_X1_CHARGE_INHIBIT_MASK_OFF		0x02
> -#define OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS	(OXP_X1_CHARGE_INHIBIT_MASK_AWAKE | \
> -						 OXP_X1_CHARGE_INHIBIT_MASK_OFF)
> +#define OXP_CHARGE_INHIBIT_MASK_OFF		0x02
> +#define OXP_CHARGE_INHIBIT_MASK_ALWAYS	(OXP_CHARGE_INHIBIT_MASK_AWAKE | \
> +					 OXP_CHARGE_INHIBIT_MASK_OFF)
>  
>  static const struct dmi_system_id dmi_table[] = {
>  	{
> @@ -154,7 +158,7 @@ static const struct dmi_system_id dmi_table[] = {
>  			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
>  			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER APEX"),
>  		},
> -		.driver_data = (void *)oxp_fly,
> +		.driver_data = (void *)oxp_apex,
>  	},
>  	{
>  		.matches = {
> @@ -352,6 +356,7 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj,
>  	case oxp_x1:
>  	case oxp_g1_i:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return attr->mode;
>  	default:
>  		break;
> @@ -381,6 +386,7 @@ static ssize_t tt_toggle_store(struct device *dev,
>  	case oxp_fly:
>  	case oxp_mini_amd_pro:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		reg = OXP_TURBO_SWITCH_REG;
>  		mask = OXP_TURBO_TAKE_VAL;
>  		break;
> @@ -427,6 +433,7 @@ static ssize_t tt_toggle_show(struct device *dev,
>  	case oxp_fly:
>  	case oxp_mini_amd_pro:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		reg = OXP_TURBO_SWITCH_REG;
>  		mask = OXP_TURBO_TAKE_VAL;
>  		break;
> @@ -524,6 +531,7 @@ static bool oxp_psy_ext_supported(void)
>  	case oxp_g1_i:
>  	case oxp_g1_a:
>  	case oxp_fly:
> +	case oxp_apex:
>  		return true;
>  	default:
>  		break;
> @@ -537,12 +545,24 @@ static int oxp_psy_ext_get_prop(struct power_supply *psy,
>  				enum power_supply_property psp,
>  				union power_supply_propval *val)
>  {
> +	u8 limit_reg, inhibit_reg;
>  	long raw_val;
>  	int ret;
>  
> +	switch (board) {
> +	case oxp_apex:
> +		limit_reg = OXP_APEX_CHARGE_LIMIT_REG;
> +		inhibit_reg = OXP_APEX_CHARGE_INHIBIT_REG;
> +		break;
> +	default:
> +		limit_reg = OXP_X1_CHARGE_LIMIT_REG;
> +		inhibit_reg = OXP_X1_CHARGE_INHIBIT_REG;
> +		break;
> +	}
> +
>  	switch (psp) {
>  	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
> -		ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
> +		ret = read_from_ec(limit_reg, 1, &raw_val);
>  		if (ret)
>  			return ret;
>  		if (raw_val < 0 || raw_val > 100)
> @@ -550,14 +570,14 @@ static int oxp_psy_ext_get_prop(struct power_supply *psy,
>  		val->intval = raw_val;
>  		return 0;
>  	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> -		ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
> +		ret = read_from_ec(inhibit_reg, 1, &raw_val);
>  		if (ret)
>  			return ret;
> -		if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
> -		    OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS)
> +		if ((raw_val & OXP_CHARGE_INHIBIT_MASK_ALWAYS) ==
> +		    OXP_CHARGE_INHIBIT_MASK_ALWAYS)
>  			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
> -		else if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_AWAKE) ==
> -			 OXP_X1_CHARGE_INHIBIT_MASK_AWAKE)
> +		else if ((raw_val & OXP_CHARGE_INHIBIT_MASK_AWAKE) ==
> +			 OXP_CHARGE_INHIBIT_MASK_AWAKE)
>  			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
>  		else
>  			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
> @@ -573,29 +593,41 @@ static int oxp_psy_ext_set_prop(struct power_supply *psy,
>  				enum power_supply_property psp,
>  				const union power_supply_propval *val)
>  {
> +	u8 limit_reg, inhibit_reg;
>  	long raw_val;
>  
> +	switch (board) {
> +	case oxp_apex:
> +		limit_reg = OXP_APEX_CHARGE_LIMIT_REG;
> +		inhibit_reg = OXP_APEX_CHARGE_INHIBIT_REG;
> +		break;
> +	default:
> +		limit_reg = OXP_X1_CHARGE_LIMIT_REG;
> +		inhibit_reg = OXP_X1_CHARGE_INHIBIT_REG;

I took this series as is, but in future you should look into implementing 
all these variations through a struct in .driver_data, not using 
ever-growing switch/case blocks.

-- 
 i.

> +		break;
> +	}
> +
>  	switch (psp) {
>  	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
>  		if (val->intval < 0 || val->intval > 100)
>  			return -EINVAL;
> -		return write_to_ec(OXP_X1_CHARGE_LIMIT_REG, val->intval);
> +		return write_to_ec(limit_reg, val->intval);
>  	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
>  		switch (val->intval) {
>  		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
>  			raw_val = 0;
>  			break;
>  		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE:
> -			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_AWAKE;
> +			raw_val = OXP_CHARGE_INHIBIT_MASK_AWAKE;
>  			break;
>  		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
> -			raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
> +			raw_val = OXP_CHARGE_INHIBIT_MASK_ALWAYS;
>  			break;
>  		default:
>  			return -EINVAL;
>  		}
>  
> -		return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
> +		return write_to_ec(inhibit_reg, raw_val);
>  	default:
>  		return -EINVAL;
>  	}
> @@ -656,6 +688,7 @@ static int oxp_pwm_enable(void)
>  	case oxp_x1:
>  	case oxp_g1_i:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
>  	default:
>  		return -EINVAL;
> @@ -676,6 +709,7 @@ static int oxp_pwm_disable(void)
>  	case oxp_x1:
>  	case oxp_g1_i:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
>  	default:
>  		return -EINVAL;
> @@ -696,6 +730,7 @@ static int oxp_pwm_read(long *val)
>  	case oxp_x1:
>  	case oxp_g1_i:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
>  	default:
>  		return -EOPNOTSUPP;
> @@ -732,6 +767,7 @@ static int oxp_pwm_fan_speed(long *val)
>  	case oxp_mini_amd_a07:
>  	case oxp_mini_amd_pro:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
>  	default:
>  		return -EOPNOTSUPP;
> @@ -764,6 +800,7 @@ static int oxp_pwm_input_write(long val)
>  	case oxp_fly:
>  	case oxp_mini_amd_pro:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  		return write_to_ec(OXP_SENSOR_PWM_REG, val);
>  	default:
>  		return -EOPNOTSUPP;
> @@ -803,6 +840,7 @@ static int oxp_pwm_input_read(long *val)
>  	case oxp_fly:
>  	case oxp_mini_amd_pro:
>  	case oxp_g1_a:
> +	case oxp_apex:
>  	default:
>  		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
>  		if (ret)
> 
> base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
> 

      parent reply	other threads:[~2026-10-05 17:56 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-20 19:04 [PATCH v1 1/2] platform/x86: oxpec: Fix Apex charge limit control Antheas Kapenekakis
2026-09-20 19:04 ` [PATCH v1 2/2] platform/x86: oxpec: Add OneXPlayer 3 quirk Antheas Kapenekakis
2026-09-20 22:29   ` Derek J. Clark
2026-09-20 22:28 ` [PATCH v1 1/2] platform/x86: oxpec: Fix Apex charge limit control Derek J. Clark
2026-10-05 17:56 ` Ilpo Järvinen [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=272b2d3c-243a-cfb8-bf34-c6cffc7e5fec@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=derekjohn.clark@gmail.com \
    --cc=eileen@one-netbook.com \
    --cc=hansg@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lkml@antheas.dev \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=samsagax@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.