#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/processor.h>
+#include <acpi/battery.h>
 
 /* Handle ACPI lock mechanism */
 static u32 oxp_mutex;
 };
 
 static enum oxp_board board;
+static struct device *oxp_dev;
 
 /* Fan reading and PWM */
 #define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */
 #define OXP_X1_TURBO_LED_OFF           0x01
 #define OXP_X1_TURBO_LED_ON            0x02
 
+/* Battery extension settings */
+#define EC_CHARGE_CONTROL_BEHAVIOURS   (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |              \
+                                        BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) |    \
+                                        BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE))
+
+#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
+/* 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)
+
 static const struct dmi_system_id dmi_table[] = {
        {
                .matches = {
 
 static DEVICE_ATTR_RW(tt_led);
 
+/* Callbacks for charge behaviour attributes */
+static bool oxp_psy_ext_supported(void)
+{
+       switch (board) {
+       case oxp_x1:
+       case oxp_fly:
+               return true;
+       default:
+               break;
+       }
+       return false;
+}
+
+static int oxp_psy_ext_get_prop(struct power_supply *psy,
+                               const struct power_supply_ext *ext,
+                               void *data,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       long raw_val;
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+               ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
+               if (ret)
+                       return ret;
+               if (raw_val < 0 || raw_val > 100)
+                       return -EINVAL;
+               val->intval = raw_val;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+               ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
+               if (ret)
+                       return ret;
+               if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
+                   OXP_X1_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)
+                       val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
+               else
+                       val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int oxp_psy_ext_set_prop(struct power_supply *psy,
+                               const struct power_supply_ext *ext,
+                               void *data,
+                               enum power_supply_property psp,
+                               const union power_supply_propval *val)
+{
+       long raw_val;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+               if (val->intval > 100)
+                       return -EINVAL;
+               return write_to_ec(OXP_X1_CHARGE_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;
+                       break;
+               case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+                       raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int oxp_psy_prop_is_writeable(struct power_supply *psy,
+                                    const struct power_supply_ext *ext,
+                                    void *data,
+                                    enum power_supply_property psp)
+{
+       return true;
+}
+
+static const enum power_supply_property oxp_psy_ext_props[] = {
+       POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+};
+
+static const struct power_supply_ext oxp_psy_ext = {
+       .name                   = "oxp-charge-control",
+       .properties             = oxp_psy_ext_props,
+       .num_properties         = ARRAY_SIZE(oxp_psy_ext_props),
+       .charge_behaviours      = EC_CHARGE_CONTROL_BEHAVIOURS,
+       .get_property           = oxp_psy_ext_get_prop,
+       .set_property           = oxp_psy_ext_set_prop,
+       .property_is_writeable  = oxp_psy_prop_is_writeable,
+};
+
+static int oxp_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+       return power_supply_register_extension(battery, &oxp_psy_ext, oxp_dev, NULL);
+}
+
+static int oxp_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+       power_supply_unregister_extension(battery, &oxp_psy_ext);
+       return 0;
+}
+
+static struct acpi_battery_hook battery_hook = {
+       .add_battery    = oxp_add_battery,
+       .remove_battery = oxp_remove_battery,
+       .name           = "OneXPlayer Battery",
+};
+
 /* PWM enable/disable functions */
 static int oxp_pwm_enable(void)
 {
 {
        struct device *dev = &pdev->dev;
        struct device *hwdev;
+       int ret;
 
+       oxp_dev = dev;
        hwdev = devm_hwmon_device_register_with_info(dev, "oxp_ec", NULL,
                                                     &oxp_ec_chip_info, NULL);
 
-       return PTR_ERR_OR_ZERO(hwdev);
+       if (IS_ERR(hwdev))
+               return PTR_ERR(hwdev);
+
+       if (oxp_psy_ext_supported()) {
+               ret = devm_battery_hook_register(dev, &battery_hook);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 
 static struct platform_driver oxp_platform_driver = {