uboot/drivers/pwm/pwm-ti-ehrpwm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * EHRPWM PWM driver
   4 *
   5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
   6 *
   7 * Based on Linux kernel drivers/pwm/pwm-tiehrpwm.c
   8 */
   9
  10#include <common.h>
  11#include <clk.h>
  12#include <div64.h>
  13#include <dm.h>
  14#include <dm/device_compat.h>
  15#include <pwm.h>
  16#include <asm/io.h>
  17
  18#define NSEC_PER_SEC                            1000000000L
  19
  20/* Time base module registers */
  21#define TI_EHRPWM_TBCTL                         0x00
  22#define TI_EHRPWM_TBPRD                         0x0A
  23
  24#define TI_EHRPWM_TBCTL_PRDLD_MASK              BIT(3)
  25#define TI_EHRPWM_TBCTL_PRDLD_SHDW              0
  26#define TI_EHRPWM_TBCTL_PRDLD_IMDT              BIT(3)
  27#define TI_EHRPWM_TBCTL_CLKDIV_MASK             GENMASK(12, 7)
  28#define TI_EHRPWM_TBCTL_CTRMODE_MASK            GENMASK(1, 0)
  29#define TI_EHRPWM_TBCTL_CTRMODE_UP              0
  30#define TI_EHRPWM_TBCTL_CTRMODE_DOWN            BIT(0)
  31#define TI_EHRPWM_TBCTL_CTRMODE_UPDOWN          BIT(1)
  32#define TI_EHRPWM_TBCTL_CTRMODE_FREEZE          GENMASK(1, 0)
  33
  34#define TI_EHRPWM_TBCTL_HSPCLKDIV_SHIFT         7
  35#define TI_EHRPWM_TBCTL_CLKDIV_SHIFT            10
  36
  37#define TI_EHRPWM_CLKDIV_MAX                    7
  38#define TI_EHRPWM_HSPCLKDIV_MAX                 7
  39#define TI_EHRPWM_PERIOD_MAX                    0xFFFF
  40
  41/* Counter compare module registers */
  42#define TI_EHRPWM_CMPA                          0x12
  43#define TI_EHRPWM_CMPB                          0x14
  44
  45/* Action qualifier module registers */
  46#define TI_EHRPWM_AQCTLA                        0x16
  47#define TI_EHRPWM_AQCTLB                        0x18
  48#define TI_EHRPWM_AQSFRC                        0x1A
  49#define TI_EHRPWM_AQCSFRC                       0x1C
  50
  51#define TI_EHRPWM_AQCTL_CBU_MASK                GENMASK(9, 8)
  52#define TI_EHRPWM_AQCTL_CBU_FRCLOW              BIT(8)
  53#define TI_EHRPWM_AQCTL_CBU_FRCHIGH             BIT(9)
  54#define TI_EHRPWM_AQCTL_CBU_FRCTOGGLE           GENMASK(9, 8)
  55#define TI_EHRPWM_AQCTL_CAU_MASK                GENMASK(5, 4)
  56#define TI_EHRPWM_AQCTL_CAU_FRCLOW              BIT(4)
  57#define TI_EHRPWM_AQCTL_CAU_FRCHIGH             BIT(5)
  58#define TI_EHRPWM_AQCTL_CAU_FRCTOGGLE           GENMASK(5, 4)
  59#define TI_EHRPWM_AQCTL_PRD_MASK                GENMASK(3, 2)
  60#define TI_EHRPWM_AQCTL_PRD_FRCLOW              BIT(2)
  61#define TI_EHRPWM_AQCTL_PRD_FRCHIGH             BIT(3)
  62#define TI_EHRPWM_AQCTL_PRD_FRCTOGGLE           GENMASK(3, 2)
  63#define TI_EHRPWM_AQCTL_ZRO_MASK                GENMASK(1, 0)
  64#define TI_EHRPWM_AQCTL_ZRO_FRCLOW              BIT(0)
  65#define TI_EHRPWM_AQCTL_ZRO_FRCHIGH             BIT(1)
  66#define TI_EHRPWM_AQCTL_ZRO_FRCTOGGLE           GENMASK(1, 0)
  67
  68#define TI_EHRPWM_AQCTL_CHANA_POLNORMAL         (TI_EHRPWM_AQCTL_CAU_FRCLOW | \
  69                                                 TI_EHRPWM_AQCTL_PRD_FRCHIGH | \
  70                                                 TI_EHRPWM_AQCTL_ZRO_FRCHIGH)
  71#define TI_EHRPWM_AQCTL_CHANA_POLINVERSED       (TI_EHRPWM_AQCTL_CAU_FRCHIGH | \
  72                                                 TI_EHRPWM_AQCTL_PRD_FRCLOW | \
  73                                                 TI_EHRPWM_AQCTL_ZRO_FRCLOW)
  74#define TI_EHRPWM_AQCTL_CHANB_POLNORMAL         (TI_EHRPWM_AQCTL_CBU_FRCLOW | \
  75                                                 TI_EHRPWM_AQCTL_PRD_FRCHIGH | \
  76                                                 TI_EHRPWM_AQCTL_ZRO_FRCHIGH)
  77#define TI_EHRPWM_AQCTL_CHANB_POLINVERSED       (TI_EHRPWM_AQCTL_CBU_FRCHIGH | \
  78                                                 TI_EHRPWM_AQCTL_PRD_FRCLOW | \
  79                                                 TI_EHRPWM_AQCTL_ZRO_FRCLOW)
  80
  81#define TI_EHRPWM_AQSFRC_RLDCSF_MASK            GENMASK(7, 6)
  82#define TI_EHRPWM_AQSFRC_RLDCSF_ZRO             0
  83#define TI_EHRPWM_AQSFRC_RLDCSF_PRD             BIT(6)
  84#define TI_EHRPWM_AQSFRC_RLDCSF_ZROPRD          BIT(7)
  85#define TI_EHRPWM_AQSFRC_RLDCSF_IMDT            GENMASK(7, 6)
  86
  87#define TI_EHRPWM_AQCSFRC_CSFB_MASK             GENMASK(3, 2)
  88#define TI_EHRPWM_AQCSFRC_CSFB_FRCDIS           0
  89#define TI_EHRPWM_AQCSFRC_CSFB_FRCLOW           BIT(2)
  90#define TI_EHRPWM_AQCSFRC_CSFB_FRCHIGH          BIT(3)
  91#define TI_EHRPWM_AQCSFRC_CSFB_DISSWFRC         GENMASK(3, 2)
  92#define TI_EHRPWM_AQCSFRC_CSFA_MASK             GENMASK(1, 0)
  93#define TI_EHRPWM_AQCSFRC_CSFA_FRCDIS           0
  94#define TI_EHRPWM_AQCSFRC_CSFA_FRCLOW           BIT(0)
  95#define TI_EHRPWM_AQCSFRC_CSFA_FRCHIGH          BIT(1)
  96#define TI_EHRPWM_AQCSFRC_CSFA_DISSWFRC         GENMASK(1, 0)
  97
  98#define TI_EHRPWM_NUM_CHANNELS                  2
  99
 100struct ti_ehrpwm_priv {
 101        fdt_addr_t regs;
 102        u32 clk_rate;
 103        struct clk tbclk;
 104        unsigned long period_cycles[TI_EHRPWM_NUM_CHANNELS];
 105        bool polarity_reversed[TI_EHRPWM_NUM_CHANNELS];
 106};
 107
 108static void ti_ehrpwm_modify(u16 val, u16 mask, fdt_addr_t reg)
 109{
 110        unsigned short v;
 111
 112        v = readw(reg);
 113        v &= ~mask;
 114        v |= val & mask;
 115        writew(v, reg);
 116}
 117
 118static int ti_ehrpwm_set_invert(struct udevice *dev, uint channel,
 119                                bool polarity)
 120{
 121        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 122
 123        if (channel >= TI_EHRPWM_NUM_CHANNELS)
 124                return -ENOSPC;
 125
 126        /* Configuration of polarity in hardware delayed, do at enable */
 127        priv->polarity_reversed[channel] = polarity;
 128        return 0;
 129}
 130
 131/**
 132 * set_prescale_div -   Set up the prescaler divider function
 133 * @rqst_prescaler:     prescaler value min
 134 * @prescale_div:       prescaler value set
 135 * @tb_clk_div:         Time Base Control prescaler bits
 136 */
 137static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div,
 138                            u16 *tb_clk_div)
 139{
 140        unsigned int clkdiv, hspclkdiv;
 141
 142        for (clkdiv = 0; clkdiv <= TI_EHRPWM_CLKDIV_MAX; clkdiv++) {
 143                for (hspclkdiv = 0; hspclkdiv <= TI_EHRPWM_HSPCLKDIV_MAX;
 144                     hspclkdiv++) {
 145                        /*
 146                         * calculations for prescaler value :
 147                         * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
 148                         * HSPCLKDIVIDER =  2 ** hspclkdiv
 149                         * CLKDIVIDER = (1),            if clkdiv == 0 *OR*
 150                         *              (2 * clkdiv),   if clkdiv != 0
 151                         *
 152                         * Configure prescale_div value such that period
 153                         * register value is less than 65535.
 154                         */
 155
 156                        *prescale_div = (1 << clkdiv) *
 157                                (hspclkdiv ? (hspclkdiv * 2) : 1);
 158                        if (*prescale_div > rqst_prescaler) {
 159                                *tb_clk_div =
 160                                    (clkdiv << TI_EHRPWM_TBCTL_CLKDIV_SHIFT) |
 161                                    (hspclkdiv <<
 162                                     TI_EHRPWM_TBCTL_HSPCLKDIV_SHIFT);
 163                                return 0;
 164                        }
 165                }
 166        }
 167
 168        return 1;
 169}
 170
 171static void ti_ehrpwm_configure_polarity(struct udevice *dev, uint channel)
 172{
 173        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 174        u16 aqctl_val, aqctl_mask;
 175        unsigned int aqctl_reg;
 176
 177        /*
 178         * Configure PWM output to HIGH/LOW level on counter
 179         * reaches compare register value and LOW/HIGH level
 180         * on counter value reaches period register value and
 181         * zero value on counter
 182         */
 183        if (channel == 1) {
 184                aqctl_reg = TI_EHRPWM_AQCTLB;
 185                aqctl_mask = TI_EHRPWM_AQCTL_CBU_MASK;
 186
 187                if (priv->polarity_reversed[channel])
 188                        aqctl_val = TI_EHRPWM_AQCTL_CHANB_POLINVERSED;
 189                else
 190                        aqctl_val = TI_EHRPWM_AQCTL_CHANB_POLNORMAL;
 191        } else {
 192                aqctl_reg = TI_EHRPWM_AQCTLA;
 193                aqctl_mask = TI_EHRPWM_AQCTL_CAU_MASK;
 194
 195                if (priv->polarity_reversed[channel])
 196                        aqctl_val = TI_EHRPWM_AQCTL_CHANA_POLINVERSED;
 197                else
 198                        aqctl_val = TI_EHRPWM_AQCTL_CHANA_POLNORMAL;
 199        }
 200
 201        aqctl_mask |= TI_EHRPWM_AQCTL_PRD_MASK | TI_EHRPWM_AQCTL_ZRO_MASK;
 202        ti_ehrpwm_modify(aqctl_val, aqctl_mask, priv->regs + aqctl_reg);
 203}
 204
 205/*
 206 * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
 207 * duty_ns   = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
 208 */
 209static int ti_ehrpwm_set_config(struct udevice *dev, uint channel,
 210                                uint period_ns, uint duty_ns)
 211{
 212        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 213        u32 period_cycles, duty_cycles;
 214        u16 ps_divval, tb_divval;
 215        unsigned int i, cmp_reg;
 216        unsigned long long c;
 217
 218        if (channel >= TI_EHRPWM_NUM_CHANNELS)
 219                return -ENOSPC;
 220
 221        if (period_ns > NSEC_PER_SEC)
 222                return -ERANGE;
 223
 224        c = priv->clk_rate;
 225        c = c * period_ns;
 226        do_div(c, NSEC_PER_SEC);
 227        period_cycles = (unsigned long)c;
 228
 229        if (period_cycles < 1) {
 230                period_cycles = 1;
 231                duty_cycles = 1;
 232        } else {
 233                c = priv->clk_rate;
 234                c = c * duty_ns;
 235                do_div(c, NSEC_PER_SEC);
 236                duty_cycles = (unsigned long)c;
 237        }
 238
 239        dev_dbg(dev, "channel=%d, period_ns=%d, duty_ns=%d\n",
 240                channel, period_ns, duty_ns);
 241
 242        /*
 243         * Period values should be same for multiple PWM channels as IP uses
 244         * same period register for multiple channels.
 245         */
 246        for (i = 0; i < TI_EHRPWM_NUM_CHANNELS; i++) {
 247                if (priv->period_cycles[i] &&
 248                    priv->period_cycles[i] != period_cycles) {
 249                        /*
 250                         * Allow channel to reconfigure period if no other
 251                         * channels being configured.
 252                         */
 253                        if (i == channel)
 254                                continue;
 255
 256                        dev_err(dev, "period value conflicts with channel %u\n",
 257                                i);
 258                        return -EINVAL;
 259                }
 260        }
 261
 262        priv->period_cycles[channel] = period_cycles;
 263
 264        /* Configure clock prescaler to support Low frequency PWM wave */
 265        if (set_prescale_div(period_cycles / TI_EHRPWM_PERIOD_MAX, &ps_divval,
 266                             &tb_divval)) {
 267                dev_err(dev, "unsupported values\n");
 268                return -EINVAL;
 269        }
 270
 271        /* Update clock prescaler values */
 272        ti_ehrpwm_modify(tb_divval, TI_EHRPWM_TBCTL_CLKDIV_MASK,
 273                         priv->regs + TI_EHRPWM_TBCTL);
 274
 275        /* Update period & duty cycle with presacler division */
 276        period_cycles = period_cycles / ps_divval;
 277        duty_cycles = duty_cycles / ps_divval;
 278
 279        /* Configure shadow loading on Period register */
 280        ti_ehrpwm_modify(TI_EHRPWM_TBCTL_PRDLD_SHDW, TI_EHRPWM_TBCTL_PRDLD_MASK,
 281                         priv->regs + TI_EHRPWM_TBCTL);
 282
 283        writew(period_cycles, priv->regs + TI_EHRPWM_TBPRD);
 284
 285        /* Configure ehrpwm counter for up-count mode */
 286        ti_ehrpwm_modify(TI_EHRPWM_TBCTL_CTRMODE_UP,
 287                         TI_EHRPWM_TBCTL_CTRMODE_MASK,
 288                         priv->regs + TI_EHRPWM_TBCTL);
 289
 290        if (channel == 1)
 291                /* Channel 1 configured with compare B register */
 292                cmp_reg = TI_EHRPWM_CMPB;
 293        else
 294                /* Channel 0 configured with compare A register */
 295                cmp_reg = TI_EHRPWM_CMPA;
 296
 297        writew(duty_cycles, priv->regs + cmp_reg);
 298        return 0;
 299}
 300
 301static int ti_ehrpwm_disable(struct udevice *dev, uint channel)
 302{
 303        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 304        u16 aqcsfrc_val, aqcsfrc_mask;
 305        int err;
 306
 307        if (channel >= TI_EHRPWM_NUM_CHANNELS)
 308                return -ENOSPC;
 309
 310        /* Action Qualifier puts PWM output low forcefully */
 311        if (channel) {
 312                aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFB_FRCLOW;
 313                aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFB_MASK;
 314        } else {
 315                aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFA_FRCLOW;
 316                aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFA_MASK;
 317        }
 318
 319        /* Update shadow register first before modifying active register */
 320        ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_ZRO,
 321                         TI_EHRPWM_AQSFRC_RLDCSF_MASK,
 322                         priv->regs + TI_EHRPWM_AQSFRC);
 323
 324        ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
 325                         priv->regs + TI_EHRPWM_AQCSFRC);
 326
 327        /*
 328         * Changes to immediate action on Action Qualifier. This puts
 329         * Action Qualifier control on PWM output from next TBCLK
 330         */
 331        ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_IMDT,
 332                         TI_EHRPWM_AQSFRC_RLDCSF_MASK,
 333                         priv->regs + TI_EHRPWM_AQSFRC);
 334
 335        ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
 336                         priv->regs + TI_EHRPWM_AQCSFRC);
 337
 338        /* Disabling TBCLK on PWM disable */
 339        err = clk_disable(&priv->tbclk);
 340        if (err) {
 341                dev_err(dev, "failed to disable tbclk\n");
 342                return err;
 343        }
 344
 345        return 0;
 346}
 347
 348static int ti_ehrpwm_enable(struct udevice *dev, uint channel)
 349{
 350        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 351        u16 aqcsfrc_val, aqcsfrc_mask;
 352        int err;
 353
 354        if (channel >= TI_EHRPWM_NUM_CHANNELS)
 355                return -ENOSPC;
 356
 357        /* Disabling Action Qualifier on PWM output */
 358        if (channel) {
 359                aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFB_FRCDIS;
 360                aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFB_MASK;
 361        } else {
 362                aqcsfrc_val = TI_EHRPWM_AQCSFRC_CSFA_FRCDIS;
 363                aqcsfrc_mask = TI_EHRPWM_AQCSFRC_CSFA_MASK;
 364        }
 365
 366        /* Changes to shadow mode */
 367        ti_ehrpwm_modify(TI_EHRPWM_AQSFRC_RLDCSF_ZRO,
 368                         TI_EHRPWM_AQSFRC_RLDCSF_MASK,
 369                         priv->regs + TI_EHRPWM_AQSFRC);
 370
 371        ti_ehrpwm_modify(aqcsfrc_val, aqcsfrc_mask,
 372                         priv->regs + TI_EHRPWM_AQCSFRC);
 373
 374        /* Channels polarity can be configured from action qualifier module */
 375        ti_ehrpwm_configure_polarity(dev, channel);
 376
 377        err = clk_enable(&priv->tbclk);
 378        if (err) {
 379                dev_err(dev, "failed to enable tbclk\n");
 380                return err;
 381        }
 382
 383        return 0;
 384}
 385
 386static int ti_ehrpwm_set_enable(struct udevice *dev, uint channel, bool enable)
 387{
 388        if (enable)
 389                return ti_ehrpwm_enable(dev, channel);
 390
 391        return ti_ehrpwm_disable(dev, channel);
 392}
 393
 394static int ti_ehrpwm_of_to_plat(struct udevice *dev)
 395{
 396        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 397
 398        priv->regs = dev_read_addr(dev);
 399        if (priv->regs == FDT_ADDR_T_NONE) {
 400                dev_err(dev, "invalid address\n");
 401                return -EINVAL;
 402        }
 403
 404        dev_dbg(dev, "regs=0x%08lx\n", priv->regs);
 405        return 0;
 406}
 407
 408static int ti_ehrpwm_remove(struct udevice *dev)
 409{
 410        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 411
 412        clk_release_all(&priv->tbclk, 1);
 413        return 0;
 414}
 415
 416static int ti_ehrpwm_probe(struct udevice *dev)
 417{
 418        struct ti_ehrpwm_priv *priv = dev_get_priv(dev);
 419        struct clk clk;
 420        int err;
 421
 422        err = clk_get_by_name(dev, "fck", &clk);
 423        if (err) {
 424                dev_err(dev, "failed to get clock\n");
 425                return err;
 426        }
 427
 428        priv->clk_rate = clk_get_rate(&clk);
 429        if (IS_ERR_VALUE(priv->clk_rate) || !priv->clk_rate) {
 430                dev_err(dev, "failed to get clock rate\n");
 431                if (IS_ERR_VALUE(priv->clk_rate))
 432                        return priv->clk_rate;
 433
 434                return -EINVAL;
 435        }
 436
 437        /* Acquire tbclk for Time Base EHRPWM submodule */
 438        err = clk_get_by_name(dev, "tbclk", &priv->tbclk);
 439        if (err) {
 440                dev_err(dev, "failed to get tbclk clock\n");
 441                return err;
 442        }
 443
 444        return 0;
 445}
 446
 447static const struct pwm_ops ti_ehrpwm_ops = {
 448        .set_config = ti_ehrpwm_set_config,
 449        .set_enable = ti_ehrpwm_set_enable,
 450        .set_invert = ti_ehrpwm_set_invert,
 451};
 452
 453static const struct udevice_id ti_ehrpwm_ids[] = {
 454        {.compatible = "ti,am3352-ehrpwm"},
 455        {.compatible = "ti,am33xx-ehrpwm"},
 456        {}
 457};
 458
 459U_BOOT_DRIVER(ti_ehrpwm) = {
 460        .name = "ti_ehrpwm",
 461        .id = UCLASS_PWM,
 462        .of_match = ti_ehrpwm_ids,
 463        .ops = &ti_ehrpwm_ops,
 464        .of_to_plat = ti_ehrpwm_of_to_plat,
 465        .probe = ti_ehrpwm_probe,
 466        .remove = ti_ehrpwm_remove,
 467        .priv_auto = sizeof(struct ti_ehrpwm_priv),
 468};
 469