uboot/drivers/pwm/rk_pwm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2016 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <clk.h>
   9#include <div64.h>
  10#include <dm.h>
  11#include <log.h>
  12#include <pwm.h>
  13#include <regmap.h>
  14#include <syscon.h>
  15#include <asm/io.h>
  16#include <asm/arch-rockchip/pwm.h>
  17#include <linux/bitops.h>
  18#include <power/regulator.h>
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22struct rockchip_pwm_data {
  23        struct rockchip_pwm_regs regs;
  24        unsigned int prescaler;
  25        bool supports_polarity;
  26        bool supports_lock;
  27        u32 enable_conf;
  28        u32 enable_conf_mask;
  29};
  30
  31struct rk_pwm_priv {
  32        fdt_addr_t base;
  33        ulong freq;
  34        u32 conf_polarity;
  35        const struct rockchip_pwm_data *data;
  36};
  37
  38static int rk_pwm_set_invert(struct udevice *dev, uint channel, bool polarity)
  39{
  40        struct rk_pwm_priv *priv = dev_get_priv(dev);
  41
  42        if (!priv->data->supports_polarity) {
  43                debug("%s: Do not support polarity\n", __func__);
  44                return 0;
  45        }
  46
  47        debug("%s: polarity=%u\n", __func__, polarity);
  48        if (polarity)
  49                priv->conf_polarity = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
  50        else
  51                priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
  52
  53        return 0;
  54}
  55
  56static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns,
  57                             uint duty_ns)
  58{
  59        struct rk_pwm_priv *priv = dev_get_priv(dev);
  60        const struct rockchip_pwm_regs *regs = &priv->data->regs;
  61        unsigned long period, duty;
  62        u32 ctrl;
  63
  64        debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
  65
  66        ctrl = readl(priv->base + regs->ctrl);
  67        /*
  68         * Lock the period and duty of previous configuration, then
  69         * change the duty and period, that would not be effective.
  70         */
  71        if (priv->data->supports_lock) {
  72                ctrl |= PWM_LOCK;
  73                writel(ctrl, priv->base + regs->ctrl);
  74        }
  75
  76        period = lldiv((uint64_t)priv->freq * period_ns,
  77                       priv->data->prescaler * 1000000000);
  78        duty = lldiv((uint64_t)priv->freq * duty_ns,
  79                     priv->data->prescaler * 1000000000);
  80
  81        writel(period, priv->base + regs->period);
  82        writel(duty, priv->base + regs->duty);
  83
  84        if (priv->data->supports_polarity) {
  85                ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);
  86                ctrl |= priv->conf_polarity;
  87        }
  88
  89        /*
  90         * Unlock and set polarity at the same time,
  91         * the configuration of duty, period and polarity
  92         * would be effective together at next period.
  93         */
  94        if (priv->data->supports_lock)
  95                ctrl &= ~PWM_LOCK;
  96        writel(ctrl, priv->base + regs->ctrl);
  97
  98        debug("%s: period=%lu, duty=%lu\n", __func__, period, duty);
  99
 100        return 0;
 101}
 102
 103static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
 104{
 105        struct rk_pwm_priv *priv = dev_get_priv(dev);
 106        const struct rockchip_pwm_regs *regs = &priv->data->regs;
 107        u32 ctrl;
 108
 109        debug("%s: Enable '%s'\n", __func__, dev->name);
 110
 111        ctrl = readl(priv->base + regs->ctrl);
 112        ctrl &= ~priv->data->enable_conf_mask;
 113
 114        if (enable)
 115                ctrl |= priv->data->enable_conf;
 116        else
 117                ctrl &= ~priv->data->enable_conf;
 118
 119        writel(ctrl, priv->base + regs->ctrl);
 120
 121        return 0;
 122}
 123
 124static int rk_pwm_ofdata_to_platdata(struct udevice *dev)
 125{
 126        struct rk_pwm_priv *priv = dev_get_priv(dev);
 127
 128        priv->base = dev_read_addr(dev);
 129
 130        return 0;
 131}
 132
 133static int rk_pwm_probe(struct udevice *dev)
 134{
 135        struct rk_pwm_priv *priv = dev_get_priv(dev);
 136        struct clk clk;
 137        int ret = 0;
 138
 139        ret = clk_get_by_index(dev, 0, &clk);
 140        if (ret < 0) {
 141                debug("%s get clock fail!\n", __func__);
 142                return -EINVAL;
 143        }
 144
 145        priv->freq = clk_get_rate(&clk);
 146        priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev);
 147
 148        if (priv->data->supports_polarity)
 149                priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE;
 150
 151        return 0;
 152}
 153
 154static const struct pwm_ops rk_pwm_ops = {
 155        .set_invert     = rk_pwm_set_invert,
 156        .set_config     = rk_pwm_set_config,
 157        .set_enable     = rk_pwm_set_enable,
 158};
 159
 160static const struct rockchip_pwm_data pwm_data_v1 = {
 161        .regs = {
 162                .duty = 0x04,
 163                .period = 0x08,
 164                .cntr = 0x00,
 165                .ctrl = 0x0c,
 166        },
 167        .prescaler = 2,
 168        .supports_polarity = false,
 169        .supports_lock = false,
 170        .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
 171        .enable_conf_mask = BIT(1) | BIT(3),
 172};
 173
 174static const struct rockchip_pwm_data pwm_data_v2 = {
 175        .regs = {
 176                .duty = 0x08,
 177                .period = 0x04,
 178                .cntr = 0x00,
 179                .ctrl = 0x0c,
 180        },
 181        .prescaler = 1,
 182        .supports_polarity = true,
 183        .supports_lock = false,
 184        .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
 185                       PWM_CONTINUOUS,
 186        .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
 187};
 188
 189static const struct rockchip_pwm_data pwm_data_v3 = {
 190        .regs = {
 191                .duty = 0x08,
 192                .period = 0x04,
 193                .cntr = 0x00,
 194                .ctrl = 0x0c,
 195        },
 196        .prescaler = 1,
 197        .supports_polarity = true,
 198        .supports_lock = true,
 199        .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
 200                       PWM_CONTINUOUS,
 201        .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
 202};
 203
 204static const struct udevice_id rk_pwm_ids[] = {
 205        { .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1},
 206        { .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2},
 207        { .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3},
 208        { }
 209};
 210
 211U_BOOT_DRIVER(rk_pwm) = {
 212        .name   = "rk_pwm",
 213        .id     = UCLASS_PWM,
 214        .of_match = rk_pwm_ids,
 215        .ops    = &rk_pwm_ops,
 216        .ofdata_to_platdata     = rk_pwm_ofdata_to_platdata,
 217        .probe          = rk_pwm_probe,
 218        .priv_auto_alloc_size   = sizeof(struct rk_pwm_priv),
 219};
 220