power: rpi-poe: Add option of being created by MFD or FW
[platform/kernel/linux-rpi.git] / drivers / power / supply / rpi_poe_power.c
index 7cf1014..e96f98c 100644 (file)
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
+#include <linux/regmap.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
-#define RPI_POE_ADC_REG                        0x2
-#define RPI_POE_FLAG_REG               0x4
+#define RPI_POE_FW_BASE_REG            0x2
+
+#define RPI_POE_ADC_REG                        0x0
+#define RPI_POE_FLAG_REG               0x2
 
 #define RPI_POE_FLAG_AT                        BIT(0)
 #define RPI_POE_FLAG_OC                        BIT(1)
 #define DRVNAME "rpi-poe-power-supply"
 
 struct rpi_poe_power_supply_ctx {
-       struct power_supply *supply;
        struct rpi_firmware *fw;
+
+       struct regmap *regmap;
+       u32 offset;
+
+       struct power_supply *supply;
 };
 
 struct fw_tag_data_s {
@@ -36,40 +43,51 @@ struct fw_tag_data_s {
        u32 ret;
 };
 
-static int write_reg(struct rpi_firmware *fw, u32 reg, u32 *val)
+static int write_reg(struct rpi_poe_power_supply_ctx *ctx, u32 reg, u32 *val)
 {
        struct fw_tag_data_s fw_tag_data = {
-               .reg = reg,
+               .reg = reg + RPI_POE_FW_BASE_REG,
                .val = *val
        };
        int ret;
 
-       ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POE_HAT_VAL,
-                                   &fw_tag_data, sizeof(fw_tag_data));
-       if (ret)
-               return ret;
-       else if (fw_tag_data.ret)
-               return -EIO;
-       return 0;
+       if (ctx->fw) {
+               ret = rpi_firmware_property(ctx->fw, RPI_FIRMWARE_SET_POE_HAT_VAL,
+                                           &fw_tag_data, sizeof(fw_tag_data));
+               if (!ret && fw_tag_data.ret)
+                       ret = -EIO;
+       } else {
+               ret = regmap_write(ctx->regmap, ctx->offset + reg, *val);
+       }
+
+       return ret;
 }
 
-static int read_reg(struct rpi_firmware *fw, u32 reg, u32 *val)
+static int read_reg(struct rpi_poe_power_supply_ctx *ctx, u32 reg, u32 *val)
 {
        struct fw_tag_data_s fw_tag_data = {
-               .reg = reg,
+               .reg = reg + RPI_POE_FW_BASE_REG,
                .val = *val
        };
+       u32 value;
        int ret;
 
-       ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_POE_HAT_VAL,
-                                   &fw_tag_data, sizeof(fw_tag_data));
-       if (ret)
-               return ret;
-       else if (fw_tag_data.ret)
-               return -EIO;
+       if (ctx->fw) {
+               ret = rpi_firmware_property(ctx->fw, RPI_FIRMWARE_GET_POE_HAT_VAL,
+                                           &fw_tag_data, sizeof(fw_tag_data));
+               if (!ret && fw_tag_data.ret)
+                       ret = -EIO;
+               *val = fw_tag_data.val;
+       } else {
+               ret = regmap_read(ctx->regmap, ctx->offset + reg, &value);
+               if (!ret) {
+                       *val = value;
+                       ret = regmap_read(ctx->regmap, ctx->offset + reg + 1, &value);
+                       *val |= value << 8;
+               }
+       }
 
-       *val = fw_tag_data.val;
-       return 0;
+       return ret;
 }
 
 static int rpi_poe_power_supply_get_property(struct power_supply *psy,
@@ -82,14 +100,14 @@ static int rpi_poe_power_supply_get_property(struct power_supply *psy,
 
        switch (psp) {
        case POWER_SUPPLY_PROP_HEALTH:
-               ret = read_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+               ret = read_reg(ctx, RPI_POE_FLAG_REG, &val);
                if (ret)
                        return ret;
 
                if (val & RPI_POE_FLAG_OC) {
                        r_val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
                        val = RPI_POE_FLAG_OC;
-                       ret = write_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+                       ret = write_reg(ctx, RPI_POE_FLAG_REG, &val);
                        if (ret)
                                return ret;
                        return 0;
@@ -99,7 +117,7 @@ static int rpi_poe_power_supply_get_property(struct power_supply *psy,
                return 0;
 
        case POWER_SUPPLY_PROP_ONLINE:
-               ret = read_reg(ctx->fw, RPI_POE_ADC_REG, &val);
+               ret = read_reg(ctx, RPI_POE_ADC_REG, &val);
                if (ret)
                        return ret;
 
@@ -107,7 +125,7 @@ static int rpi_poe_power_supply_get_property(struct power_supply *psy,
                return 0;
 
        case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = read_reg(ctx->fw, RPI_POE_ADC_REG, &val);
+               ret = read_reg(ctx, RPI_POE_ADC_REG, &val);
                if (ret)
                        return ret;
                val = (val * 3300)/9821;
@@ -115,15 +133,14 @@ static int rpi_poe_power_supply_get_property(struct power_supply *psy,
                return 0;
 
        case POWER_SUPPLY_PROP_CURRENT_MAX:
-               ret = read_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+               ret = read_reg(ctx, RPI_POE_FLAG_REG, &val);
                if (ret)
                        return ret;
 
-               if (val & RPI_POE_FLAG_AT) {
+               if (val & RPI_POE_FLAG_AT)
                        r_val->intval = RPI_POE_CURRENT_AT_MAX;
-                       return 0;
-               }
-               r_val->intval = RPI_POE_CURRENT_AF_MAX;
+               else
+                       r_val->intval = RPI_POE_CURRENT_AF_MAX;
                return 0;
 
        default:
@@ -158,29 +175,38 @@ static int rpi_poe_power_supply_probe(struct platform_device *pdev)
        if (!of_device_is_available(pdev->dev.of_node))
                return -ENODEV;
 
-       fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
-       if (!fw_node) {
-               dev_err(&pdev->dev, "Missing firmware node\n");
-               return -ENOENT;
-       }
-
        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
-       ctx->fw = rpi_firmware_get(fw_node);
-       if (!ctx->fw)
-               return -EPROBE_DEFER;
-       if (rpi_firmware_property(ctx->fw,
-                       RPI_FIRMWARE_GET_FIRMWARE_REVISION,
-                       &revision, sizeof(revision))) {
-               dev_err(&pdev->dev, "Failed to get firmware revision\n");
-               return -ENOENT;
-       }
-       if (revision < 0x60af72e8) {
-               dev_err(&pdev->dev, "Unsupported firmware\n");
-               return -ENOENT;
+       if (pdev->dev.parent)
+               ctx->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+
+       if (ctx->regmap) {
+               if (device_property_read_u32(&pdev->dev, "reg", &ctx->offset))
+                       return -EINVAL;
+       } else {
+               fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
+               if (!fw_node) {
+                       dev_err(&pdev->dev, "Missing firmware node\n");
+                       return -ENOENT;
+               }
+
+               ctx->fw = rpi_firmware_get(fw_node);
+               if (!ctx->fw)
+                       return -EPROBE_DEFER;
+               if (rpi_firmware_property(ctx->fw,
+                                         RPI_FIRMWARE_GET_FIRMWARE_REVISION,
+                                         &revision, sizeof(revision))) {
+                       dev_err(&pdev->dev, "Failed to get firmware revision\n");
+                       return -ENOENT;
+               }
+               if (revision < 0x60af72e8) {
+                       dev_err(&pdev->dev, "Unsupported firmware\n");
+                       return -ENOENT;
+               }
        }
+
        platform_set_drvdata(pdev, ctx);
 
        psy_cfg.of_node = pdev->dev.of_node;