linux/drivers/pwm/pwm-berlin.c
<<
>>
Prefs
   1/*
   2 * Marvell Berlin PWM driver
   3 *
   4 * Copyright (C) 2015 Marvell Technology Group Ltd.
   5 *
   6 * Author: Antoine Tenart <antoine.tenart@free-electrons.com>
   7 *
   8 * This file is licensed under the terms of the GNU General Public
   9 * License version 2. This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/io.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18#include <linux/pwm.h>
  19#include <linux/slab.h>
  20
  21#define BERLIN_PWM_EN                   0x0
  22#define  BERLIN_PWM_ENABLE              BIT(0)
  23#define BERLIN_PWM_CONTROL              0x4
  24#define  BERLIN_PWM_PRESCALE_MASK       0x7
  25#define  BERLIN_PWM_PRESCALE_MAX        4096
  26#define  BERLIN_PWM_INVERT_POLARITY     BIT(3)
  27#define BERLIN_PWM_DUTY                 0x8
  28#define BERLIN_PWM_TCNT                 0xc
  29#define  BERLIN_PWM_MAX_TCNT            65535
  30
  31struct berlin_pwm_channel {
  32        u32 enable;
  33        u32 ctrl;
  34        u32 duty;
  35        u32 tcnt;
  36};
  37
  38struct berlin_pwm_chip {
  39        struct pwm_chip chip;
  40        struct clk *clk;
  41        void __iomem *base;
  42};
  43
  44static inline struct berlin_pwm_chip *to_berlin_pwm_chip(struct pwm_chip *chip)
  45{
  46        return container_of(chip, struct berlin_pwm_chip, chip);
  47}
  48
  49static const u32 prescaler_table[] = {
  50        1, 4, 8, 16, 64, 256, 1024, 4096
  51};
  52
  53static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *chip,
  54                                   unsigned int channel, unsigned long offset)
  55{
  56        return readl_relaxed(chip->base + channel * 0x10 + offset);
  57}
  58
  59static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip,
  60                                     unsigned int channel, u32 value,
  61                                     unsigned long offset)
  62{
  63        writel_relaxed(value, chip->base + channel * 0x10 + offset);
  64}
  65
  66static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  67{
  68        struct berlin_pwm_channel *channel;
  69
  70        channel = kzalloc(sizeof(*channel), GFP_KERNEL);
  71        if (!channel)
  72                return -ENOMEM;
  73
  74        return pwm_set_chip_data(pwm, channel);
  75}
  76
  77static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
  78{
  79        struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm);
  80
  81        pwm_set_chip_data(pwm, NULL);
  82        kfree(channel);
  83}
  84
  85static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
  86                             int duty_ns, int period_ns)
  87{
  88        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
  89        unsigned int prescale;
  90        u32 value, duty, period;
  91        u64 cycles, tmp;
  92
  93        cycles = clk_get_rate(pwm->clk);
  94        cycles *= period_ns;
  95        do_div(cycles, NSEC_PER_SEC);
  96
  97        for (prescale = 0; prescale < ARRAY_SIZE(prescaler_table); prescale++) {
  98                tmp = cycles;
  99                do_div(tmp, prescaler_table[prescale]);
 100
 101                if (tmp <= BERLIN_PWM_MAX_TCNT)
 102                        break;
 103        }
 104
 105        if (tmp > BERLIN_PWM_MAX_TCNT)
 106                return -ERANGE;
 107
 108        period = tmp;
 109        cycles = tmp * duty_ns;
 110        do_div(cycles, period_ns);
 111        duty = cycles;
 112
 113        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
 114        value &= ~BERLIN_PWM_PRESCALE_MASK;
 115        value |= prescale;
 116        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL);
 117
 118        berlin_pwm_writel(pwm, pwm_dev->hwpwm, duty, BERLIN_PWM_DUTY);
 119        berlin_pwm_writel(pwm, pwm_dev->hwpwm, period, BERLIN_PWM_TCNT);
 120
 121        return 0;
 122}
 123
 124static int berlin_pwm_set_polarity(struct pwm_chip *chip,
 125                                   struct pwm_device *pwm_dev,
 126                                   enum pwm_polarity polarity)
 127{
 128        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 129        u32 value;
 130
 131        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
 132
 133        if (polarity == PWM_POLARITY_NORMAL)
 134                value &= ~BERLIN_PWM_INVERT_POLARITY;
 135        else
 136                value |= BERLIN_PWM_INVERT_POLARITY;
 137
 138        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL);
 139
 140        return 0;
 141}
 142
 143static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev)
 144{
 145        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 146        u32 value;
 147
 148        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
 149        value |= BERLIN_PWM_ENABLE;
 150        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN);
 151
 152        return 0;
 153}
 154
 155static void berlin_pwm_disable(struct pwm_chip *chip,
 156                               struct pwm_device *pwm_dev)
 157{
 158        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 159        u32 value;
 160
 161        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
 162        value &= ~BERLIN_PWM_ENABLE;
 163        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN);
 164}
 165
 166static const struct pwm_ops berlin_pwm_ops = {
 167        .request = berlin_pwm_request,
 168        .free = berlin_pwm_free,
 169        .config = berlin_pwm_config,
 170        .set_polarity = berlin_pwm_set_polarity,
 171        .enable = berlin_pwm_enable,
 172        .disable = berlin_pwm_disable,
 173        .owner = THIS_MODULE,
 174};
 175
 176static const struct of_device_id berlin_pwm_match[] = {
 177        { .compatible = "marvell,berlin-pwm" },
 178        { },
 179};
 180MODULE_DEVICE_TABLE(of, berlin_pwm_match);
 181
 182static int berlin_pwm_probe(struct platform_device *pdev)
 183{
 184        struct berlin_pwm_chip *pwm;
 185        struct resource *res;
 186        int ret;
 187
 188        pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
 189        if (!pwm)
 190                return -ENOMEM;
 191
 192        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 193        pwm->base = devm_ioremap_resource(&pdev->dev, res);
 194        if (IS_ERR(pwm->base))
 195                return PTR_ERR(pwm->base);
 196
 197        pwm->clk = devm_clk_get(&pdev->dev, NULL);
 198        if (IS_ERR(pwm->clk))
 199                return PTR_ERR(pwm->clk);
 200
 201        ret = clk_prepare_enable(pwm->clk);
 202        if (ret)
 203                return ret;
 204
 205        pwm->chip.dev = &pdev->dev;
 206        pwm->chip.ops = &berlin_pwm_ops;
 207        pwm->chip.base = -1;
 208        pwm->chip.npwm = 4;
 209        pwm->chip.of_xlate = of_pwm_xlate_with_flags;
 210        pwm->chip.of_pwm_n_cells = 3;
 211
 212        ret = pwmchip_add(&pwm->chip);
 213        if (ret < 0) {
 214                dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
 215                clk_disable_unprepare(pwm->clk);
 216                return ret;
 217        }
 218
 219        platform_set_drvdata(pdev, pwm);
 220
 221        return 0;
 222}
 223
 224static int berlin_pwm_remove(struct platform_device *pdev)
 225{
 226        struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
 227        int ret;
 228
 229        ret = pwmchip_remove(&pwm->chip);
 230        clk_disable_unprepare(pwm->clk);
 231
 232        return ret;
 233}
 234
 235#ifdef CONFIG_PM_SLEEP
 236static int berlin_pwm_suspend(struct device *dev)
 237{
 238        struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
 239        unsigned int i;
 240
 241        for (i = 0; i < pwm->chip.npwm; i++) {
 242                struct berlin_pwm_channel *channel;
 243
 244                channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
 245                if (!channel)
 246                        continue;
 247
 248                channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE);
 249                channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL);
 250                channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY);
 251                channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT);
 252        }
 253
 254        clk_disable_unprepare(pwm->clk);
 255
 256        return 0;
 257}
 258
 259static int berlin_pwm_resume(struct device *dev)
 260{
 261        struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
 262        unsigned int i;
 263        int ret;
 264
 265        ret = clk_prepare_enable(pwm->clk);
 266        if (ret)
 267                return ret;
 268
 269        for (i = 0; i < pwm->chip.npwm; i++) {
 270                struct berlin_pwm_channel *channel;
 271
 272                channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
 273                if (!channel)
 274                        continue;
 275
 276                berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL);
 277                berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY);
 278                berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT);
 279                berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE);
 280        }
 281
 282        return 0;
 283}
 284#endif
 285
 286static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend,
 287                         berlin_pwm_resume);
 288
 289static struct platform_driver berlin_pwm_driver = {
 290        .probe = berlin_pwm_probe,
 291        .remove = berlin_pwm_remove,
 292        .driver = {
 293                .name = "berlin-pwm",
 294                .of_match_table = berlin_pwm_match,
 295                .pm = &berlin_pwm_pm_ops,
 296        },
 297};
 298module_platform_driver(berlin_pwm_driver);
 299
 300MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
 301MODULE_DESCRIPTION("Marvell Berlin PWM driver");
 302MODULE_LICENSE("GPL v2");
 303