linux/drivers/pwm/pwm-mediatek.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * MediaTek Pulse Width Modulator driver
   4 *
   5 * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
   6 * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com>
   7 *
   8 */
   9
  10#include <linux/err.h>
  11#include <linux/io.h>
  12#include <linux/ioport.h>
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/clk.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/pwm.h>
  20#include <linux/slab.h>
  21#include <linux/types.h>
  22
  23/* PWM registers and bits definitions */
  24#define PWMCON                  0x00
  25#define PWMHDUR                 0x04
  26#define PWMLDUR                 0x08
  27#define PWMGDUR                 0x0c
  28#define PWMWAVENUM              0x28
  29#define PWMDWIDTH               0x2c
  30#define PWM45DWIDTH_FIXUP       0x30
  31#define PWMTHRES                0x30
  32#define PWM45THRES_FIXUP        0x34
  33#define PWM_CK_26M_SEL          0x210
  34
  35#define PWM_CLK_DIV_MAX         7
  36
  37struct pwm_mediatek_of_data {
  38        unsigned int num_pwms;
  39        bool pwm45_fixup;
  40        bool has_ck_26m_sel;
  41};
  42
  43/**
  44 * struct pwm_mediatek_chip - struct representing PWM chip
  45 * @chip: linux PWM chip representation
  46 * @regs: base address of PWM chip
  47 * @clk_top: the top clock generator
  48 * @clk_main: the clock used by PWM core
  49 * @clk_pwms: the clock used by each PWM channel
  50 * @clk_freq: the fix clock frequency of legacy MIPS SoC
  51 * @soc: pointer to chip's platform data
  52 */
  53struct pwm_mediatek_chip {
  54        struct pwm_chip chip;
  55        void __iomem *regs;
  56        struct clk *clk_top;
  57        struct clk *clk_main;
  58        struct clk **clk_pwms;
  59        const struct pwm_mediatek_of_data *soc;
  60};
  61
  62static const unsigned int pwm_mediatek_reg_offset[] = {
  63        0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
  64};
  65
  66static inline struct pwm_mediatek_chip *
  67to_pwm_mediatek_chip(struct pwm_chip *chip)
  68{
  69        return container_of(chip, struct pwm_mediatek_chip, chip);
  70}
  71
  72static int pwm_mediatek_clk_enable(struct pwm_chip *chip,
  73                                   struct pwm_device *pwm)
  74{
  75        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
  76        int ret;
  77
  78        ret = clk_prepare_enable(pc->clk_top);
  79        if (ret < 0)
  80                return ret;
  81
  82        ret = clk_prepare_enable(pc->clk_main);
  83        if (ret < 0)
  84                goto disable_clk_top;
  85
  86        ret = clk_prepare_enable(pc->clk_pwms[pwm->hwpwm]);
  87        if (ret < 0)
  88                goto disable_clk_main;
  89
  90        return 0;
  91
  92disable_clk_main:
  93        clk_disable_unprepare(pc->clk_main);
  94disable_clk_top:
  95        clk_disable_unprepare(pc->clk_top);
  96
  97        return ret;
  98}
  99
 100static void pwm_mediatek_clk_disable(struct pwm_chip *chip,
 101                                     struct pwm_device *pwm)
 102{
 103        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
 104
 105        clk_disable_unprepare(pc->clk_pwms[pwm->hwpwm]);
 106        clk_disable_unprepare(pc->clk_main);
 107        clk_disable_unprepare(pc->clk_top);
 108}
 109
 110static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
 111                                       unsigned int num, unsigned int offset,
 112                                       u32 value)
 113{
 114        writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset);
 115}
 116
 117static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
 118                               int duty_ns, int period_ns)
 119{
 120        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
 121        u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH,
 122            reg_thres = PWMTHRES;
 123        u64 resolution;
 124        int ret;
 125
 126        ret = pwm_mediatek_clk_enable(chip, pwm);
 127
 128        if (ret < 0)
 129                return ret;
 130
 131        /* Make sure we use the bus clock and not the 26MHz clock */
 132        if (pc->soc->has_ck_26m_sel)
 133                writel(0, pc->regs + PWM_CK_26M_SEL);
 134
 135        /* Using resolution in picosecond gets accuracy higher */
 136        resolution = (u64)NSEC_PER_SEC * 1000;
 137        do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm]));
 138
 139        cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
 140        while (cnt_period > 8191) {
 141                resolution *= 2;
 142                clkdiv++;
 143                cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
 144                                                   resolution);
 145        }
 146
 147        if (clkdiv > PWM_CLK_DIV_MAX) {
 148                pwm_mediatek_clk_disable(chip, pwm);
 149                dev_err(chip->dev, "period %d not supported\n", period_ns);
 150                return -EINVAL;
 151        }
 152
 153        if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
 154                /*
 155                 * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
 156                 * from the other PWMs on MT7623.
 157                 */
 158                reg_width = PWM45DWIDTH_FIXUP;
 159                reg_thres = PWM45THRES_FIXUP;
 160        }
 161
 162        cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
 163        pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
 164        pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
 165        pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
 166
 167        pwm_mediatek_clk_disable(chip, pwm);
 168
 169        return 0;
 170}
 171
 172static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 173{
 174        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
 175        u32 value;
 176        int ret;
 177
 178        ret = pwm_mediatek_clk_enable(chip, pwm);
 179        if (ret < 0)
 180                return ret;
 181
 182        value = readl(pc->regs);
 183        value |= BIT(pwm->hwpwm);
 184        writel(value, pc->regs);
 185
 186        return 0;
 187}
 188
 189static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 190{
 191        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
 192        u32 value;
 193
 194        value = readl(pc->regs);
 195        value &= ~BIT(pwm->hwpwm);
 196        writel(value, pc->regs);
 197
 198        pwm_mediatek_clk_disable(chip, pwm);
 199}
 200
 201static const struct pwm_ops pwm_mediatek_ops = {
 202        .config = pwm_mediatek_config,
 203        .enable = pwm_mediatek_enable,
 204        .disable = pwm_mediatek_disable,
 205        .owner = THIS_MODULE,
 206};
 207
 208static int pwm_mediatek_probe(struct platform_device *pdev)
 209{
 210        struct pwm_mediatek_chip *pc;
 211        unsigned int i;
 212        int ret;
 213
 214        pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
 215        if (!pc)
 216                return -ENOMEM;
 217
 218        pc->soc = of_device_get_match_data(&pdev->dev);
 219
 220        pc->regs = devm_platform_ioremap_resource(pdev, 0);
 221        if (IS_ERR(pc->regs))
 222                return PTR_ERR(pc->regs);
 223
 224        pc->clk_pwms = devm_kcalloc(&pdev->dev, pc->soc->num_pwms,
 225                                    sizeof(*pc->clk_pwms), GFP_KERNEL);
 226        if (!pc->clk_pwms)
 227                return -ENOMEM;
 228
 229        pc->clk_top = devm_clk_get(&pdev->dev, "top");
 230        if (IS_ERR(pc->clk_top)) {
 231                dev_err(&pdev->dev, "clock: top fail: %ld\n",
 232                        PTR_ERR(pc->clk_top));
 233                return PTR_ERR(pc->clk_top);
 234        }
 235
 236        pc->clk_main = devm_clk_get(&pdev->dev, "main");
 237        if (IS_ERR(pc->clk_main)) {
 238                dev_err(&pdev->dev, "clock: main fail: %ld\n",
 239                        PTR_ERR(pc->clk_main));
 240                return PTR_ERR(pc->clk_main);
 241        }
 242
 243        for (i = 0; i < pc->soc->num_pwms; i++) {
 244                char name[8];
 245
 246                snprintf(name, sizeof(name), "pwm%d", i + 1);
 247
 248                pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name);
 249                if (IS_ERR(pc->clk_pwms[i])) {
 250                        dev_err(&pdev->dev, "clock: %s fail: %ld\n",
 251                                name, PTR_ERR(pc->clk_pwms[i]));
 252                        return PTR_ERR(pc->clk_pwms[i]);
 253                }
 254        }
 255
 256        pc->chip.dev = &pdev->dev;
 257        pc->chip.ops = &pwm_mediatek_ops;
 258        pc->chip.npwm = pc->soc->num_pwms;
 259
 260        ret = devm_pwmchip_add(&pdev->dev, &pc->chip);
 261        if (ret < 0) {
 262                dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
 263                return ret;
 264        }
 265
 266        return 0;
 267}
 268
 269static const struct pwm_mediatek_of_data mt2712_pwm_data = {
 270        .num_pwms = 8,
 271        .pwm45_fixup = false,
 272        .has_ck_26m_sel = false,
 273};
 274
 275static const struct pwm_mediatek_of_data mt7622_pwm_data = {
 276        .num_pwms = 6,
 277        .pwm45_fixup = false,
 278        .has_ck_26m_sel = false,
 279};
 280
 281static const struct pwm_mediatek_of_data mt7623_pwm_data = {
 282        .num_pwms = 5,
 283        .pwm45_fixup = true,
 284        .has_ck_26m_sel = false,
 285};
 286
 287static const struct pwm_mediatek_of_data mt7628_pwm_data = {
 288        .num_pwms = 4,
 289        .pwm45_fixup = true,
 290        .has_ck_26m_sel = false,
 291};
 292
 293static const struct pwm_mediatek_of_data mt7629_pwm_data = {
 294        .num_pwms = 1,
 295        .pwm45_fixup = false,
 296        .has_ck_26m_sel = false,
 297};
 298
 299static const struct pwm_mediatek_of_data mt8183_pwm_data = {
 300        .num_pwms = 4,
 301        .pwm45_fixup = false,
 302        .has_ck_26m_sel = true,
 303};
 304
 305static const struct pwm_mediatek_of_data mt8516_pwm_data = {
 306        .num_pwms = 5,
 307        .pwm45_fixup = false,
 308        .has_ck_26m_sel = true,
 309};
 310
 311static const struct of_device_id pwm_mediatek_of_match[] = {
 312        { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },
 313        { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },
 314        { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },
 315        { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
 316        { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data },
 317        { .compatible = "mediatek,mt8183-pwm", .data = &mt8183_pwm_data },
 318        { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data },
 319        { },
 320};
 321MODULE_DEVICE_TABLE(of, pwm_mediatek_of_match);
 322
 323static struct platform_driver pwm_mediatek_driver = {
 324        .driver = {
 325                .name = "pwm-mediatek",
 326                .of_match_table = pwm_mediatek_of_match,
 327        },
 328        .probe = pwm_mediatek_probe,
 329};
 330module_platform_driver(pwm_mediatek_driver);
 331
 332MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
 333MODULE_LICENSE("GPL v2");
 334