From: Muhammed Efe Cetin <efectn@protonmail.com>
Fan control on the Khadas Edge 2 is controlled with the 0x8A register,
using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards.
Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver.
The original behavior can still be used when the cooling-levels property is not specified,
ensuring that the new functionality does not break old boards.
Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com>
---
drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++--
1 file changed, 72 insertions(+), 4 deletions(-)
diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c
index d35e5313b..504e7d254 100644
--- a/drivers/thermal/khadas_mcu_fan.c
+++ b/drivers/thermal/khadas_mcu_fan.c
@@ -15,10 +15,16 @@
#include <linux/thermal.h>
#define MAX_LEVEL 3
+#define MAX_SPEED 0x64
struct khadas_mcu_fan_ctx {
struct khadas_mcu *mcu;
unsigned int level;
+
+ unsigned int fan_max_level;
+ unsigned int fan_register;
+ unsigned int *fan_cooling_levels;
+
struct thermal_cooling_device *cdev;
};
@@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
unsigned int level)
{
int ret;
+ unsigned int write_level = level;
+
+ if (level > ctx->fan_max_level)
+ return -EINVAL;
+
+ if (ctx->fan_cooling_levels != NULL) {
+ write_level = ctx->fan_cooling_levels[level];
+
+ if (write_level > MAX_SPEED)
+ return -EINVAL;
+ }
+
+ ret = regmap_write(ctx->mcu->regmap, ctx->fan_register,
+ write_level);
- ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
- level);
if (ret)
return ret;
@@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
- *state = MAX_LEVEL;
+ struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->fan_max_level;
return 0;
}
@@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
{
struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
- if (state > MAX_LEVEL)
+ if (state > ctx->fan_max_level)
return -EINVAL;
if (state == ctx->level)
@@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
.set_cur_state = khadas_mcu_fan_set_cur_state,
};
+static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev)
+{
+ struct device_node *np = ctx->mcu->dev->of_node;
+ int num, i, ret;
+
+ if (!of_property_present(np, "cooling-levels"))
+ return 0;
+
+ ret = of_property_count_u32_elems(np, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? : -EINVAL;
+ }
+
+ num = ret;
+ ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->fan_cooling_levels[i] > MAX_SPEED) {
+ dev_err(dev, "MCU fan state[%d]:%d > %d\n", i,
+ ctx->fan_cooling_levels[i], MAX_SPEED);
+ return -EINVAL;
+ }
+ }
+
+ ctx->fan_max_level = num - 1;
+ ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2;
+
+ return 0;
+}
+
static int khadas_mcu_fan_probe(struct platform_device *pdev)
{
struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
@@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev)
ctx->mcu = mcu;
platform_set_drvdata(pdev, ctx);
+ ctx->fan_max_level = MAX_LEVEL;
+ ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG;
+
+ ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev);
+ if (ret)
+ return ret;
+
cdev = devm_thermal_of_cooling_device_register(dev->parent,
dev->parent->of_node, "khadas-mcu-fan", ctx,
&khadas_mcu_fan_cooling_ops);
--
2.49.0
On 26/06/2025 16:04, muhammed.efecetin.67@gmail.com wrote: > From: Muhammed Efe Cetin <efectn@protonmail.com> > > Fan control on the Khadas Edge 2 is controlled with the 0x8A register, > using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards. > Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver. > The original behavior can still be used when the cooling-levels property is not specified, > ensuring that the new functionality does not break old boards. Thanks for the explanation, but would would you like to change that ? The MCU can accept any value between 0 and 99, so why change the levels from DT ? Neil > > Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com> > --- > drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++-- > 1 file changed, 72 insertions(+), 4 deletions(-) > > diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c > index d35e5313b..504e7d254 100644 > --- a/drivers/thermal/khadas_mcu_fan.c > +++ b/drivers/thermal/khadas_mcu_fan.c > @@ -15,10 +15,16 @@ > #include <linux/thermal.h> > > #define MAX_LEVEL 3 > +#define MAX_SPEED 0x64 > > struct khadas_mcu_fan_ctx { > struct khadas_mcu *mcu; > unsigned int level; > + > + unsigned int fan_max_level; > + unsigned int fan_register; > + unsigned int *fan_cooling_levels; > + > struct thermal_cooling_device *cdev; > }; > > @@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, > unsigned int level) > { > int ret; > + unsigned int write_level = level; > + > + if (level > ctx->fan_max_level) > + return -EINVAL; > + > + if (ctx->fan_cooling_levels != NULL) { > + write_level = ctx->fan_cooling_levels[level]; > + > + if (write_level > MAX_SPEED) > + return -EINVAL; > + } > + > + ret = regmap_write(ctx->mcu->regmap, ctx->fan_register, > + write_level); > > - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, > - level); > if (ret) > return ret; > > @@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, > static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, > unsigned long *state) > { > - *state = MAX_LEVEL; > + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; > + > + *state = ctx->fan_max_level; > > return 0; > } > @@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, > { > struct khadas_mcu_fan_ctx *ctx = cdev->devdata; > > - if (state > MAX_LEVEL) > + if (state > ctx->fan_max_level) > return -EINVAL; > > if (state == ctx->level) > @@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { > .set_cur_state = khadas_mcu_fan_set_cur_state, > }; > > +static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev) > +{ > + struct device_node *np = ctx->mcu->dev->of_node; > + int num, i, ret; > + > + if (!of_property_present(np, "cooling-levels")) > + return 0; > + > + ret = of_property_count_u32_elems(np, "cooling-levels"); > + if (ret <= 0) { > + dev_err(dev, "Wrong data!\n"); > + return ret ? : -EINVAL; > + } > + > + num = ret; > + ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), > + GFP_KERNEL); > + if (!ctx->fan_cooling_levels) > + return -ENOMEM; > + > + ret = of_property_read_u32_array(np, "cooling-levels", > + ctx->fan_cooling_levels, num); > + if (ret) { > + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); > + return ret; > + } > + > + for (i = 0; i < num; i++) { > + if (ctx->fan_cooling_levels[i] > MAX_SPEED) { > + dev_err(dev, "MCU fan state[%d]:%d > %d\n", i, > + ctx->fan_cooling_levels[i], MAX_SPEED); > + return -EINVAL; > + } > + } > + > + ctx->fan_max_level = num - 1; > + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2; > + > + return 0; > +} > + > static int khadas_mcu_fan_probe(struct platform_device *pdev) > { > struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); > @@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) > ctx->mcu = mcu; > platform_set_drvdata(pdev, ctx); > > + ctx->fan_max_level = MAX_LEVEL; > + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; > + > + ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev); > + if (ret) > + return ret; > + > cdev = devm_thermal_of_cooling_device_register(dev->parent, > dev->parent->of_node, "khadas-mcu-fan", ctx, > &khadas_mcu_fan_cooling_ops);
On 6/26/25 17:11, neil.armstrong@linaro.org wrote: > On 26/06/2025 16:04, muhammed.efecetin.67@gmail.com wrote: >> From: Muhammed Efe Cetin <efectn@protonmail.com> >> >> Fan control on the Khadas Edge 2 is controlled with the 0x8A register, >> using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards. >> Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver. >> The original behavior can still be used when the cooling-levels property is not specified, >> ensuring that the new functionality does not break old boards. > > Thanks for the explanation, but would would you like to change that ? The MCU can accept > any value between 0 and 99, so why change the levels from DT ? > > Neil Thanks for the review. Therefore, you say just add values between 0-100 to cooling-device instead of remapping them using cooling-levels property? What would be the best practise of detecting whether the board is Khadas Edge 2? Adding new bool property, reading model propety from devicetree etc. Best regards. > >> >> Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com> >> --- >> drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++-- >> 1 file changed, 72 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c >> index d35e5313b..504e7d254 100644 >> --- a/drivers/thermal/khadas_mcu_fan.c >> +++ b/drivers/thermal/khadas_mcu_fan.c >> @@ -15,10 +15,16 @@ >> #include <linux/thermal.h> >> #define MAX_LEVEL 3 >> +#define MAX_SPEED 0x64 >> struct khadas_mcu_fan_ctx { >> struct khadas_mcu *mcu; >> unsigned int level; >> + >> + unsigned int fan_max_level; >> + unsigned int fan_register; >> + unsigned int *fan_cooling_levels; >> + >> struct thermal_cooling_device *cdev; >> }; >> @@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >> unsigned int level) >> { >> int ret; >> + unsigned int write_level = level; >> + >> + if (level > ctx->fan_max_level) >> + return -EINVAL; >> + >> + if (ctx->fan_cooling_levels != NULL) { >> + write_level = ctx->fan_cooling_levels[level]; >> + >> + if (write_level > MAX_SPEED) >> + return -EINVAL; >> + } >> + >> + ret = regmap_write(ctx->mcu->regmap, ctx->fan_register, >> + write_level); >> - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, >> - level); >> if (ret) >> return ret; >> @@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >> static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, >> unsigned long *state) >> { >> - *state = MAX_LEVEL; >> + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >> + >> + *state = ctx->fan_max_level; >> return 0; >> } >> @@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, >> { >> struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >> - if (state > MAX_LEVEL) >> + if (state > ctx->fan_max_level) >> return -EINVAL; >> if (state == ctx->level) >> @@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { >> .set_cur_state = khadas_mcu_fan_set_cur_state, >> }; >> +static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev) >> +{ >> + struct device_node *np = ctx->mcu->dev->of_node; >> + int num, i, ret; >> + >> + if (!of_property_present(np, "cooling-levels")) >> + return 0; >> + >> + ret = of_property_count_u32_elems(np, "cooling-levels"); >> + if (ret <= 0) { >> + dev_err(dev, "Wrong data!\n"); >> + return ret ? : -EINVAL; >> + } >> + >> + num = ret; >> + ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), >> + GFP_KERNEL); >> + if (!ctx->fan_cooling_levels) >> + return -ENOMEM; >> + >> + ret = of_property_read_u32_array(np, "cooling-levels", >> + ctx->fan_cooling_levels, num); >> + if (ret) { >> + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); >> + return ret; >> + } >> + >> + for (i = 0; i < num; i++) { >> + if (ctx->fan_cooling_levels[i] > MAX_SPEED) { >> + dev_err(dev, "MCU fan state[%d]:%d > %d\n", i, >> + ctx->fan_cooling_levels[i], MAX_SPEED); >> + return -EINVAL; >> + } >> + } >> + >> + ctx->fan_max_level = num - 1; >> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2; >> + >> + return 0; >> +} >> + >> static int khadas_mcu_fan_probe(struct platform_device *pdev) >> { >> struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); >> @@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) >> ctx->mcu = mcu; >> platform_set_drvdata(pdev, ctx); >> + ctx->fan_max_level = MAX_LEVEL; >> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; >> + >> + ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev); >> + if (ret) >> + return ret; >> + >> cdev = devm_thermal_of_cooling_device_register(dev->parent, >> dev->parent->of_node, "khadas-mcu-fan", ctx, >> &khadas_mcu_fan_cooling_ops); >
On 26/06/2025 16:36, muhammed.efecetin.67@gmail.com wrote: > On 6/26/25 17:11, neil.armstrong@linaro.org wrote: >> On 26/06/2025 16:04, muhammed.efecetin.67@gmail.com wrote: >>> From: Muhammed Efe Cetin <efectn@protonmail.com> >>> >>> Fan control on the Khadas Edge 2 is controlled with the 0x8A register, >>> using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards. >>> Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver. >>> The original behavior can still be used when the cooling-levels property is not specified, >>> ensuring that the new functionality does not break old boards. >> >> Thanks for the explanation, but would would you like to change that ? The MCU can accept >> any value between 0 and 99, so why change the levels from DT ? >> >> Neil > > Thanks for the review. Therefore, you say just add values between 0-100 to cooling-device instead of remapping them using cooling-levels property? > > What would be the best practise of detecting whether the board is Khadas Edge 2? Adding new bool property, reading model propety from devicetree etc. The register DEVICE_NO should be set at 0x12 for Edge V, 0x11 for Edge 1. I don't have the number for Edge 2, perhaps you can read it ? Neil > > Best regards. > >> >>> >>> Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com> >>> --- >>> drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++-- >>> 1 file changed, 72 insertions(+), 4 deletions(-) >>> >>> diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c >>> index d35e5313b..504e7d254 100644 >>> --- a/drivers/thermal/khadas_mcu_fan.c >>> +++ b/drivers/thermal/khadas_mcu_fan.c >>> @@ -15,10 +15,16 @@ >>> #include <linux/thermal.h> >>> #define MAX_LEVEL 3 >>> +#define MAX_SPEED 0x64 >>> struct khadas_mcu_fan_ctx { >>> struct khadas_mcu *mcu; >>> unsigned int level; >>> + >>> + unsigned int fan_max_level; >>> + unsigned int fan_register; >>> + unsigned int *fan_cooling_levels; >>> + >>> struct thermal_cooling_device *cdev; >>> }; >>> @@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>> unsigned int level) >>> { >>> int ret; >>> + unsigned int write_level = level; >>> + >>> + if (level > ctx->fan_max_level) >>> + return -EINVAL; >>> + >>> + if (ctx->fan_cooling_levels != NULL) { >>> + write_level = ctx->fan_cooling_levels[level]; >>> + >>> + if (write_level > MAX_SPEED) >>> + return -EINVAL; >>> + } >>> + >>> + ret = regmap_write(ctx->mcu->regmap, ctx->fan_register, >>> + write_level); >>> - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, >>> - level); >>> if (ret) >>> return ret; >>> @@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>> static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, >>> unsigned long *state) >>> { >>> - *state = MAX_LEVEL; >>> + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>> + >>> + *state = ctx->fan_max_level; >>> return 0; >>> } >>> @@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, >>> { >>> struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>> - if (state > MAX_LEVEL) >>> + if (state > ctx->fan_max_level) >>> return -EINVAL; >>> if (state == ctx->level) >>> @@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { >>> .set_cur_state = khadas_mcu_fan_set_cur_state, >>> }; >>> +static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev) >>> +{ >>> + struct device_node *np = ctx->mcu->dev->of_node; >>> + int num, i, ret; >>> + >>> + if (!of_property_present(np, "cooling-levels")) >>> + return 0; >>> + >>> + ret = of_property_count_u32_elems(np, "cooling-levels"); >>> + if (ret <= 0) { >>> + dev_err(dev, "Wrong data!\n"); >>> + return ret ? : -EINVAL; >>> + } >>> + >>> + num = ret; >>> + ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), >>> + GFP_KERNEL); >>> + if (!ctx->fan_cooling_levels) >>> + return -ENOMEM; >>> + >>> + ret = of_property_read_u32_array(np, "cooling-levels", >>> + ctx->fan_cooling_levels, num); >>> + if (ret) { >>> + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); >>> + return ret; >>> + } >>> + >>> + for (i = 0; i < num; i++) { >>> + if (ctx->fan_cooling_levels[i] > MAX_SPEED) { >>> + dev_err(dev, "MCU fan state[%d]:%d > %d\n", i, >>> + ctx->fan_cooling_levels[i], MAX_SPEED); >>> + return -EINVAL; >>> + } >>> + } >>> + >>> + ctx->fan_max_level = num - 1; >>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2; >>> + >>> + return 0; >>> +} >>> + >>> static int khadas_mcu_fan_probe(struct platform_device *pdev) >>> { >>> struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); >>> @@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) >>> ctx->mcu = mcu; >>> platform_set_drvdata(pdev, ctx); >>> + ctx->fan_max_level = MAX_LEVEL; >>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; >>> + >>> + ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev); >>> + if (ret) >>> + return ret; >>> + >>> cdev = devm_thermal_of_cooling_device_register(dev->parent, >>> dev->parent->of_node, "khadas-mcu-fan", ctx, >>> &khadas_mcu_fan_cooling_ops); >> >
On 6/26/25 17:46, neil.armstrong@linaro.org wrote: > On 26/06/2025 16:36, muhammed.efecetin.67@gmail.com wrote: >> On 6/26/25 17:11, neil.armstrong@linaro.org wrote: >>> On 26/06/2025 16:04, muhammed.efecetin.67@gmail.com wrote: >>>> From: Muhammed Efe Cetin <efectn@protonmail.com> >>>> >>>> Fan control on the Khadas Edge 2 is controlled with the 0x8A register, >>>> using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards. >>>> Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver. >>>> The original behavior can still be used when the cooling-levels property is not specified, >>>> ensuring that the new functionality does not break old boards. >>> >>> Thanks for the explanation, but would would you like to change that ? The MCU can accept >>> any value between 0 and 99, so why change the levels from DT ? >>> >>> Neil >> >> Thanks for the review. Therefore, you say just add values between 0-100 to cooling-device instead of remapping them using cooling-levels property? >> >> What would be the best practise of detecting whether the board is Khadas Edge 2? Adding new bool property, reading model propety from devicetree etc. > > The register DEVICE_NO should be set at 0x12 for Edge V, 0x11 for Edge 1. I don't have the number for Edge 2, perhaps you can read it ? > > Neil The DEVICE_NO register is not available for Khadas Edge 2. Here are the registers [0]. [0] https://dl.khadas.com/products/edge2/tools/mcu/edge2-mcu-register-description-en-v1.0-230520.pdf > >> >> Best regards. >> >>> >>>> >>>> Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com> >>>> --- >>>> drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++-- >>>> 1 file changed, 72 insertions(+), 4 deletions(-) >>>> >>>> diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c >>>> index d35e5313b..504e7d254 100644 >>>> --- a/drivers/thermal/khadas_mcu_fan.c >>>> +++ b/drivers/thermal/khadas_mcu_fan.c >>>> @@ -15,10 +15,16 @@ >>>> #include <linux/thermal.h> >>>> #define MAX_LEVEL 3 >>>> +#define MAX_SPEED 0x64 >>>> struct khadas_mcu_fan_ctx { >>>> struct khadas_mcu *mcu; >>>> unsigned int level; >>>> + >>>> + unsigned int fan_max_level; >>>> + unsigned int fan_register; >>>> + unsigned int *fan_cooling_levels; >>>> + >>>> struct thermal_cooling_device *cdev; >>>> }; >>>> @@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>>> unsigned int level) >>>> { >>>> int ret; >>>> + unsigned int write_level = level; >>>> + >>>> + if (level > ctx->fan_max_level) >>>> + return -EINVAL; >>>> + >>>> + if (ctx->fan_cooling_levels != NULL) { >>>> + write_level = ctx->fan_cooling_levels[level]; >>>> + >>>> + if (write_level > MAX_SPEED) >>>> + return -EINVAL; >>>> + } >>>> + >>>> + ret = regmap_write(ctx->mcu->regmap, ctx->fan_register, >>>> + write_level); >>>> - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, >>>> - level); >>>> if (ret) >>>> return ret; >>>> @@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>>> static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, >>>> unsigned long *state) >>>> { >>>> - *state = MAX_LEVEL; >>>> + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>>> + >>>> + *state = ctx->fan_max_level; >>>> return 0; >>>> } >>>> @@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, >>>> { >>>> struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>>> - if (state > MAX_LEVEL) >>>> + if (state > ctx->fan_max_level) >>>> return -EINVAL; >>>> if (state == ctx->level) >>>> @@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { >>>> .set_cur_state = khadas_mcu_fan_set_cur_state, >>>> }; >>>> +static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev) >>>> +{ >>>> + struct device_node *np = ctx->mcu->dev->of_node; >>>> + int num, i, ret; >>>> + >>>> + if (!of_property_present(np, "cooling-levels")) >>>> + return 0; >>>> + >>>> + ret = of_property_count_u32_elems(np, "cooling-levels"); >>>> + if (ret <= 0) { >>>> + dev_err(dev, "Wrong data!\n"); >>>> + return ret ? : -EINVAL; >>>> + } >>>> + >>>> + num = ret; >>>> + ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), >>>> + GFP_KERNEL); >>>> + if (!ctx->fan_cooling_levels) >>>> + return -ENOMEM; >>>> + >>>> + ret = of_property_read_u32_array(np, "cooling-levels", >>>> + ctx->fan_cooling_levels, num); >>>> + if (ret) { >>>> + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); >>>> + return ret; >>>> + } >>>> + >>>> + for (i = 0; i < num; i++) { >>>> + if (ctx->fan_cooling_levels[i] > MAX_SPEED) { >>>> + dev_err(dev, "MCU fan state[%d]:%d > %d\n", i, >>>> + ctx->fan_cooling_levels[i], MAX_SPEED); >>>> + return -EINVAL; >>>> + } >>>> + } >>>> + >>>> + ctx->fan_max_level = num - 1; >>>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2; >>>> + >>>> + return 0; >>>> +} >>>> + >>>> static int khadas_mcu_fan_probe(struct platform_device *pdev) >>>> { >>>> struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); >>>> @@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) >>>> ctx->mcu = mcu; >>>> platform_set_drvdata(pdev, ctx); >>>> + ctx->fan_max_level = MAX_LEVEL; >>>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; >>>> + >>>> + ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev); >>>> + if (ret) >>>> + return ret; >>>> + >>>> cdev = devm_thermal_of_cooling_device_register(dev->parent, >>>> dev->parent->of_node, "khadas-mcu-fan", ctx, >>>> &khadas_mcu_fan_cooling_ops); >>> >> >
On 26/06/2025 17:16, Muhammed Efe Cetin wrote: > On 6/26/25 17:46, neil.armstrong@linaro.org wrote: >> On 26/06/2025 16:36, muhammed.efecetin.67@gmail.com wrote: >>> On 6/26/25 17:11, neil.armstrong@linaro.org wrote: >>>> On 26/06/2025 16:04, muhammed.efecetin.67@gmail.com wrote: >>>>> From: Muhammed Efe Cetin <efectn@protonmail.com> >>>>> >>>>> Fan control on the Khadas Edge 2 is controlled with the 0x8A register, >>>>> using percentage values from 0 to 100, whereas there are only 3 constant steps in previous Khadas boards. >>>>> Therefore, i added a new cooling-levels property, similar to the one used in the pwm-fan driver. >>>>> The original behavior can still be used when the cooling-levels property is not specified, >>>>> ensuring that the new functionality does not break old boards. >>>> >>>> Thanks for the explanation, but would would you like to change that ? The MCU can accept >>>> any value between 0 and 99, so why change the levels from DT ? >>>> >>>> Neil >>> >>> Thanks for the review. Therefore, you say just add values between 0-100 to cooling-device instead of remapping them using cooling-levels property? >>> >>> What would be the best practise of detecting whether the board is Khadas Edge 2? Adding new bool property, reading model propety from devicetree etc. >> >> The register DEVICE_NO should be set at 0x12 for Edge V, 0x11 for Edge 1. I don't have the number for Edge 2, perhaps you can read it ? >> >> Neil > > The DEVICE_NO register is not available for Khadas Edge 2. Here are the registers [0]. > > [0] https://dl.khadas.com/products/edge2/tools/mcu/edge2-mcu-register-description-en-v1.0-230520.pdf OK so it's basically completely different. In this case introduce a separate compatible since we can't detect it dynamically, and just add the registers with EDGE2 prefix. But keep the khadas-mcu-fan-ctrl, it will work if you pass the device type detected via the new compatible. Neil > >> >>> >>> Best regards. >>> >>>> >>>>> >>>>> Signed-off-by: Muhammed Efe Cetin <efectn@protonmail.com> >>>>> --- >>>>> drivers/thermal/khadas_mcu_fan.c | 76 ++++++++++++++++++++++++++++++-- >>>>> 1 file changed, 72 insertions(+), 4 deletions(-) >>>>> >>>>> diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c >>>>> index d35e5313b..504e7d254 100644 >>>>> --- a/drivers/thermal/khadas_mcu_fan.c >>>>> +++ b/drivers/thermal/khadas_mcu_fan.c >>>>> @@ -15,10 +15,16 @@ >>>>> #include <linux/thermal.h> >>>>> #define MAX_LEVEL 3 >>>>> +#define MAX_SPEED 0x64 >>>>> struct khadas_mcu_fan_ctx { >>>>> struct khadas_mcu *mcu; >>>>> unsigned int level; >>>>> + >>>>> + unsigned int fan_max_level; >>>>> + unsigned int fan_register; >>>>> + unsigned int *fan_cooling_levels; >>>>> + >>>>> struct thermal_cooling_device *cdev; >>>>> }; >>>>> @@ -26,9 +32,21 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>>>> unsigned int level) >>>>> { >>>>> int ret; >>>>> + unsigned int write_level = level; >>>>> + >>>>> + if (level > ctx->fan_max_level) >>>>> + return -EINVAL; >>>>> + >>>>> + if (ctx->fan_cooling_levels != NULL) { >>>>> + write_level = ctx->fan_cooling_levels[level]; >>>>> + >>>>> + if (write_level > MAX_SPEED) >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + ret = regmap_write(ctx->mcu->regmap, ctx->fan_register, >>>>> + write_level); >>>>> - ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, >>>>> - level); >>>>> if (ret) >>>>> return ret; >>>>> @@ -40,7 +58,9 @@ static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, >>>>> static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, >>>>> unsigned long *state) >>>>> { >>>>> - *state = MAX_LEVEL; >>>>> + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>>>> + >>>>> + *state = ctx->fan_max_level; >>>>> return 0; >>>>> } >>>>> @@ -61,7 +81,7 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, >>>>> { >>>>> struct khadas_mcu_fan_ctx *ctx = cdev->devdata; >>>>> - if (state > MAX_LEVEL) >>>>> + if (state > ctx->fan_max_level) >>>>> return -EINVAL; >>>>> if (state == ctx->level) >>>>> @@ -76,6 +96,47 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { >>>>> .set_cur_state = khadas_mcu_fan_set_cur_state, >>>>> }; >>>>> +static int khadas_mcu_fan_get_cooling_data_edge2(struct khadas_mcu_fan_ctx *ctx, struct device *dev) >>>>> +{ >>>>> + struct device_node *np = ctx->mcu->dev->of_node; >>>>> + int num, i, ret; >>>>> + >>>>> + if (!of_property_present(np, "cooling-levels")) >>>>> + return 0; >>>>> + >>>>> + ret = of_property_count_u32_elems(np, "cooling-levels"); >>>>> + if (ret <= 0) { >>>>> + dev_err(dev, "Wrong data!\n"); >>>>> + return ret ? : -EINVAL; >>>>> + } >>>>> + >>>>> + num = ret; >>>>> + ctx->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), >>>>> + GFP_KERNEL); >>>>> + if (!ctx->fan_cooling_levels) >>>>> + return -ENOMEM; >>>>> + >>>>> + ret = of_property_read_u32_array(np, "cooling-levels", >>>>> + ctx->fan_cooling_levels, num); >>>>> + if (ret) { >>>>> + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + for (i = 0; i < num; i++) { >>>>> + if (ctx->fan_cooling_levels[i] > MAX_SPEED) { >>>>> + dev_err(dev, "MCU fan state[%d]:%d > %d\n", i, >>>>> + ctx->fan_cooling_levels[i], MAX_SPEED); >>>>> + return -EINVAL; >>>>> + } >>>>> + } >>>>> + >>>>> + ctx->fan_max_level = num - 1; >>>>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG_V2; >>>>> + >>>>> + return 0; >>>>> +} >>>>> + >>>>> static int khadas_mcu_fan_probe(struct platform_device *pdev) >>>>> { >>>>> struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); >>>>> @@ -90,6 +151,13 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) >>>>> ctx->mcu = mcu; >>>>> platform_set_drvdata(pdev, ctx); >>>>> + ctx->fan_max_level = MAX_LEVEL; >>>>> + ctx->fan_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; >>>>> + >>>>> + ret = khadas_mcu_fan_get_cooling_data_edge2(ctx, dev); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> cdev = devm_thermal_of_cooling_device_register(dev->parent, >>>>> dev->parent->of_node, "khadas-mcu-fan", ctx, >>>>> &khadas_mcu_fan_cooling_ops); >>>> >>> >> >
© 2016 - 2025 Red Hat, Inc.