| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (C) 2016 Rockchip Electronics Co., Ltd |
| * |
| * Based on kernel drivers/regulator/pwm-regulator.c |
| * Copyright (C) 2014 - STMicroelectronics Inc. |
| * Author: Lee Jones <lee.jones@linaro.org> |
| */ |
| |
| #include <dm.h> |
| #include <errno.h> |
| #include <log.h> |
| #include <pwm.h> |
| #include <asm/global_data.h> |
| #include <dm/device_compat.h> |
| #include <power/regulator.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| struct pwm_regulator_info { |
| /* pwm id corresponding to the PWM driver */ |
| int pwm_id; |
| /* the period of one PWM cycle */ |
| int period_ns; |
| /* |
| * the polarity of one PWM |
| * 0: normal polarity |
| * 1: inverted polarity |
| */ |
| bool polarity; |
| struct udevice *pwm; |
| /* initialize voltage of regulator */ |
| int init_voltage; |
| /* the maximum voltage of regulator */ |
| int max_voltage; |
| /* the minimum voltage of regulator */ |
| int min_voltage; |
| /* the current voltage of regulator */ |
| int volt_uV; |
| }; |
| |
| static int pwm_regulator_enable(struct udevice *dev, bool enable) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| |
| return pwm_set_enable(priv->pwm, priv->pwm_id, enable); |
| } |
| |
| static int pwm_voltage_to_duty_cycle_percentage(struct udevice *dev, int req_uV) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| int min_uV = priv->min_voltage; |
| int max_uV = priv->max_voltage; |
| int diff = max_uV - min_uV; |
| |
| return ((req_uV * 100) - (min_uV * 100)) / diff; |
| } |
| |
| static int pwm_regulator_get_voltage(struct udevice *dev) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| |
| return priv->volt_uV; |
| } |
| |
| static int pwm_regulator_set_voltage(struct udevice *dev, int uvolt) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| int duty_cycle; |
| int ret = 0; |
| |
| duty_cycle = pwm_voltage_to_duty_cycle_percentage(dev, uvolt); |
| |
| ret = pwm_set_invert(priv->pwm, priv->pwm_id, priv->polarity); |
| if (ret) { |
| dev_err(dev, "Failed to init PWM\n"); |
| return ret; |
| } |
| |
| ret = pwm_set_config(priv->pwm, priv->pwm_id, |
| priv->period_ns, (priv->period_ns / 100) * duty_cycle); |
| if (ret) { |
| dev_err(dev, "Failed to configure PWM\n"); |
| return ret; |
| } |
| |
| priv->volt_uV = uvolt; |
| |
| return ret; |
| } |
| |
| static int pwm_regulator_of_to_plat(struct udevice *dev) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| struct ofnode_phandle_args args; |
| int ret; |
| |
| ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, &args); |
| if (ret) { |
| debug("%s: Cannot get PWM phandle: ret=%d\n", __func__, ret); |
| return ret; |
| } |
| |
| priv->period_ns = args.args[1]; |
| priv->polarity = args.args[2]; |
| |
| priv->init_voltage = dev_read_u32_default(dev, "regulator-init-microvolt", -1); |
| if (priv->init_voltage < 0) { |
| printf("Cannot find regulator pwm init_voltage\n"); |
| return -EINVAL; |
| } |
| |
| ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm); |
| if (ret) { |
| debug("%s: Cannot get PWM: ret=%d\n", __func__, ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int pwm_regulator_probe(struct udevice *dev) |
| { |
| struct pwm_regulator_info *priv = dev_get_priv(dev); |
| struct dm_regulator_uclass_plat *uc_pdata; |
| |
| uc_pdata = dev_get_uclass_plat(dev); |
| |
| uc_pdata->type = REGULATOR_TYPE_BUCK; |
| uc_pdata->mode_count = 0; |
| priv->max_voltage = uc_pdata->max_uV; |
| priv->min_voltage = uc_pdata->min_uV; |
| |
| if (priv->init_voltage) |
| pwm_regulator_set_voltage(dev, priv->init_voltage); |
| |
| return 0; |
| } |
| |
| static const struct dm_regulator_ops pwm_regulator_ops = { |
| .get_value = pwm_regulator_get_voltage, |
| .set_value = pwm_regulator_set_voltage, |
| .set_enable = pwm_regulator_enable, |
| }; |
| |
| static const struct udevice_id pwm_regulator_ids[] = { |
| { .compatible = "pwm-regulator" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(pwm_regulator) = { |
| .name = "pwm_regulator", |
| .id = UCLASS_REGULATOR, |
| .ops = &pwm_regulator_ops, |
| .probe = pwm_regulator_probe, |
| .of_match = pwm_regulator_ids, |
| .of_to_plat = pwm_regulator_of_to_plat, |
| .priv_auto = sizeof(struct pwm_regulator_info), |
| }; |