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
  20#define BERLIN_PWM_EN                   0x0
  21#define  BERLIN_PWM_ENABLE              BIT(0)
  22#define BERLIN_PWM_CONTROL              0x4
  23#define  BERLIN_PWM_PRESCALE_MASK       0x7
  24#define  BERLIN_PWM_PRESCALE_MAX        4096
  25#define  BERLIN_PWM_INVERT_POLARITY     BIT(3)
  26#define BERLIN_PWM_DUTY                 0x8
  27#define BERLIN_PWM_TCNT                 0xc
  28#define  BERLIN_PWM_MAX_TCNT            65535
  29
  30struct berlin_pwm_chip {
  31        struct pwm_chip chip;
  32        struct clk *clk;
  33        void __iomem *base;
  34};
  35
  36static inline struct berlin_pwm_chip *to_berlin_pwm_chip(struct pwm_chip *chip)
  37{
  38        return container_of(chip, struct berlin_pwm_chip, chip);
  39}
  40
  41static const u32 prescaler_table[] = {
  42        1, 4, 8, 16, 64, 256, 1024, 4096
  43};
  44
  45static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *chip,
  46                                   unsigned int channel, unsigned long offset)
  47{
  48        return readl_relaxed(chip->base + channel * 0x10 + offset);
  49}
  50
  51static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip,
  52                                     unsigned int channel, u32 value,
  53                                     unsigned long offset)
  54{
  55        writel_relaxed(value, chip->base + channel * 0x10 + offset);
  56}
  57
  58static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
  59                             int duty_ns, int period_ns)
  60{
  61        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
  62        unsigned int prescale;
  63        u32 value, duty, period;
  64        u64 cycles, tmp;
  65
  66        cycles = clk_get_rate(pwm->clk);
  67        cycles *= period_ns;
  68        do_div(cycles, NSEC_PER_SEC);
  69
  70        for (prescale = 0; prescale < ARRAY_SIZE(prescaler_table); prescale++) {
  71                tmp = cycles;
  72                do_div(tmp, prescaler_table[prescale]);
  73
  74                if (tmp <= BERLIN_PWM_MAX_TCNT)
  75                        break;
  76        }
  77
  78        if (tmp > BERLIN_PWM_MAX_TCNT)
  79                return -ERANGE;
  80
  81        period = tmp;
  82        cycles = tmp * duty_ns;
  83        do_div(cycles, period_ns);
  84        duty = cycles;
  85
  86        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
  87        value &= ~BERLIN_PWM_PRESCALE_MASK;
  88        value |= prescale;
  89        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL);
  90
  91        berlin_pwm_writel(pwm, pwm_dev->hwpwm, duty, BERLIN_PWM_DUTY);
  92        berlin_pwm_writel(pwm, pwm_dev->hwpwm, period, BERLIN_PWM_TCNT);
  93
  94        return 0;
  95}
  96
  97static int berlin_pwm_set_polarity(struct pwm_chip *chip,
  98                                   struct pwm_device *pwm_dev,
  99                                   enum pwm_polarity polarity)
 100{
 101        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 102        u32 value;
 103
 104        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
 105
 106        if (polarity == PWM_POLARITY_NORMAL)
 107                value &= ~BERLIN_PWM_INVERT_POLARITY;
 108        else
 109                value |= BERLIN_PWM_INVERT_POLARITY;
 110
 111        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL);
 112
 113        return 0;
 114}
 115
 116static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev)
 117{
 118        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 119        u32 value;
 120
 121        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
 122        value |= BERLIN_PWM_ENABLE;
 123        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN);
 124
 125        return 0;
 126}
 127
 128static void berlin_pwm_disable(struct pwm_chip *chip,
 129                               struct pwm_device *pwm_dev)
 130{
 131        struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
 132        u32 value;
 133
 134        value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
 135        value &= ~BERLIN_PWM_ENABLE;
 136        berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN);
 137}
 138
 139static const struct pwm_ops berlin_pwm_ops = {
 140        .config = berlin_pwm_config,
 141        .set_polarity = berlin_pwm_set_polarity,
 142        .enable = berlin_pwm_enable,
 143        .disable = berlin_pwm_disable,
 144        .owner = THIS_MODULE,
 145};
 146
 147static const struct of_device_id berlin_pwm_match[] = {
 148        { .compatible = "marvell,berlin-pwm" },
 149        { },
 150};
 151MODULE_DEVICE_TABLE(of, berlin_pwm_match);
 152
 153static int berlin_pwm_probe(struct platform_device *pdev)
 154{
 155        struct berlin_pwm_chip *pwm;
 156        struct resource *res;
 157        int ret;
 158
 159        pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
 160        if (!pwm)
 161                return -ENOMEM;
 162
 163        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 164        pwm->base = devm_ioremap_resource(&pdev->dev, res);
 165        if (IS_ERR(pwm->base))
 166                return PTR_ERR(pwm->base);
 167
 168        pwm->clk = devm_clk_get(&pdev->dev, NULL);
 169        if (IS_ERR(pwm->clk))
 170                return PTR_ERR(pwm->clk);
 171
 172        ret = clk_prepare_enable(pwm->clk);
 173        if (ret)
 174                return ret;
 175
 176        pwm->chip.dev = &pdev->dev;
 177        pwm->chip.ops = &berlin_pwm_ops;
 178        pwm->chip.base = -1;
 179        pwm->chip.npwm = 4;
 180        pwm->chip.can_sleep = true;
 181        pwm->chip.of_xlate = of_pwm_xlate_with_flags;
 182        pwm->chip.of_pwm_n_cells = 3;
 183
 184        ret = pwmchip_add(&pwm->chip);
 185        if (ret < 0) {
 186                dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
 187                clk_disable_unprepare(pwm->clk);
 188                return ret;
 189        }
 190
 191        platform_set_drvdata(pdev, pwm);
 192
 193        return 0;
 194}
 195
 196static int berlin_pwm_remove(struct platform_device *pdev)
 197{
 198        struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
 199        int ret;
 200
 201        ret = pwmchip_remove(&pwm->chip);
 202        clk_disable_unprepare(pwm->clk);
 203
 204        return ret;
 205}
 206
 207static struct platform_driver berlin_pwm_driver = {
 208        .probe = berlin_pwm_probe,
 209        .remove = berlin_pwm_remove,
 210        .driver = {
 211                .name = "berlin-pwm",
 212                .of_match_table = berlin_pwm_match,
 213        },
 214};
 215module_platform_driver(berlin_pwm_driver);
 216
 217MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
 218MODULE_DESCRIPTION("Marvell Berlin PWM driver");
 219MODULE_LICENSE("GPL v2");
 220