Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Intel Keem Bay PWM driver
0004  *
0005  * Copyright (C) 2020 Intel Corporation
0006  * Authors: Lai Poey Seng <poey.seng.lai@intel.com>
0007  *          Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
0008  *
0009  * Limitations:
0010  * - Upon disabling a channel, the currently running
0011  *   period will not be completed. However, upon
0012  *   reconfiguration of the duty cycle/period, the
0013  *   currently running period will be completed first.
0014  */
0015 
0016 #include <linux/bitfield.h>
0017 #include <linux/clk.h>
0018 #include <linux/io.h>
0019 #include <linux/mod_devicetable.h>
0020 #include <linux/module.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/pwm.h>
0023 #include <linux/regmap.h>
0024 
0025 #define KMB_TOTAL_PWM_CHANNELS      6
0026 #define KMB_PWM_COUNT_MAX       U16_MAX
0027 #define KMB_PWM_EN_BIT          BIT(31)
0028 
0029 /* Mask */
0030 #define KMB_PWM_HIGH_MASK       GENMASK(31, 16)
0031 #define KMB_PWM_LOW_MASK        GENMASK(15, 0)
0032 #define KMB_PWM_LEADIN_MASK     GENMASK(30, 0)
0033 
0034 /* PWM Register offset */
0035 #define KMB_PWM_LEADIN_OFFSET(ch)   (0x00 + 4 * (ch))
0036 #define KMB_PWM_HIGHLOW_OFFSET(ch)  (0x20 + 4 * (ch))
0037 
0038 struct keembay_pwm {
0039     struct pwm_chip chip;
0040     struct device *dev;
0041     struct clk *clk;
0042     void __iomem *base;
0043 };
0044 
0045 static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip)
0046 {
0047     return container_of(chip, struct keembay_pwm, chip);
0048 }
0049 
0050 static void keembay_clk_unprepare(void *data)
0051 {
0052     clk_disable_unprepare(data);
0053 }
0054 
0055 static int keembay_clk_enable(struct device *dev, struct clk *clk)
0056 {
0057     int ret;
0058 
0059     ret = clk_prepare_enable(clk);
0060     if (ret)
0061         return ret;
0062 
0063     return devm_add_action_or_reset(dev, keembay_clk_unprepare, clk);
0064 }
0065 
0066 /*
0067  * With gcc 10, CONFIG_CC_OPTIMIZE_FOR_SIZE and only "inline" instead of
0068  * "__always_inline" this fails to compile because the compiler doesn't notice
0069  * for all valid masks (e.g. KMB_PWM_LEADIN_MASK) that they are ok.
0070  */
0071 static __always_inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32 mask,
0072                        u32 val, u32 offset)
0073 {
0074     u32 buff = readl(priv->base + offset);
0075 
0076     buff = u32_replace_bits(buff, val, mask);
0077     writel(buff, priv->base + offset);
0078 }
0079 
0080 static void keembay_pwm_enable(struct keembay_pwm *priv, int ch)
0081 {
0082     keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 1,
0083                 KMB_PWM_LEADIN_OFFSET(ch));
0084 }
0085 
0086 static void keembay_pwm_disable(struct keembay_pwm *priv, int ch)
0087 {
0088     keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 0,
0089                 KMB_PWM_LEADIN_OFFSET(ch));
0090 }
0091 
0092 static void keembay_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
0093                   struct pwm_state *state)
0094 {
0095     struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
0096     unsigned long long high, low;
0097     unsigned long clk_rate;
0098     u32 highlow;
0099 
0100     clk_rate = clk_get_rate(priv->clk);
0101 
0102     /* Read channel enabled status */
0103     highlow = readl(priv->base + KMB_PWM_LEADIN_OFFSET(pwm->hwpwm));
0104     if (highlow & KMB_PWM_EN_BIT)
0105         state->enabled = true;
0106     else
0107         state->enabled = false;
0108 
0109     /* Read period and duty cycle */
0110     highlow = readl(priv->base + KMB_PWM_HIGHLOW_OFFSET(pwm->hwpwm));
0111     low = FIELD_GET(KMB_PWM_LOW_MASK, highlow) * NSEC_PER_SEC;
0112     high = FIELD_GET(KMB_PWM_HIGH_MASK, highlow) * NSEC_PER_SEC;
0113     state->duty_cycle = DIV_ROUND_UP_ULL(high, clk_rate);
0114     state->period = DIV_ROUND_UP_ULL(high + low, clk_rate);
0115     state->polarity = PWM_POLARITY_NORMAL;
0116 }
0117 
0118 static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0119                  const struct pwm_state *state)
0120 {
0121     struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
0122     struct pwm_state current_state;
0123     unsigned long long div;
0124     unsigned long clk_rate;
0125     u32 pwm_count = 0;
0126     u16 high, low;
0127 
0128     if (state->polarity != PWM_POLARITY_NORMAL)
0129         return -EINVAL;
0130 
0131     /*
0132      * Configure the pwm repeat count as infinite at (15:0) and leadin
0133      * low time as 0 at (30:16), which is in terms of clock cycles.
0134      */
0135     keembay_pwm_update_bits(priv, KMB_PWM_LEADIN_MASK, 0,
0136                 KMB_PWM_LEADIN_OFFSET(pwm->hwpwm));
0137 
0138     keembay_pwm_get_state(chip, pwm, &current_state);
0139 
0140     if (!state->enabled) {
0141         if (current_state.enabled)
0142             keembay_pwm_disable(priv, pwm->hwpwm);
0143         return 0;
0144     }
0145 
0146     /*
0147      * The upper 16 bits and lower 16 bits of the KMB_PWM_HIGHLOW_OFFSET
0148      * register contain the high time and low time of waveform accordingly.
0149      * All the values are in terms of clock cycles.
0150      */
0151 
0152     clk_rate = clk_get_rate(priv->clk);
0153     div = clk_rate * state->duty_cycle;
0154     div = DIV_ROUND_DOWN_ULL(div, NSEC_PER_SEC);
0155     if (div > KMB_PWM_COUNT_MAX)
0156         return -ERANGE;
0157 
0158     high = div;
0159     div = clk_rate * state->period;
0160     div = DIV_ROUND_DOWN_ULL(div, NSEC_PER_SEC);
0161     div = div - high;
0162     if (div > KMB_PWM_COUNT_MAX)
0163         return -ERANGE;
0164 
0165     low = div;
0166 
0167     pwm_count = FIELD_PREP(KMB_PWM_HIGH_MASK, high) |
0168             FIELD_PREP(KMB_PWM_LOW_MASK, low);
0169 
0170     writel(pwm_count, priv->base + KMB_PWM_HIGHLOW_OFFSET(pwm->hwpwm));
0171 
0172     if (state->enabled && !current_state.enabled)
0173         keembay_pwm_enable(priv, pwm->hwpwm);
0174 
0175     return 0;
0176 }
0177 
0178 static const struct pwm_ops keembay_pwm_ops = {
0179     .owner = THIS_MODULE,
0180     .apply = keembay_pwm_apply,
0181     .get_state = keembay_pwm_get_state,
0182 };
0183 
0184 static int keembay_pwm_probe(struct platform_device *pdev)
0185 {
0186     struct device *dev = &pdev->dev;
0187     struct keembay_pwm *priv;
0188     int ret;
0189 
0190     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0191     if (!priv)
0192         return -ENOMEM;
0193 
0194     priv->clk = devm_clk_get(dev, NULL);
0195     if (IS_ERR(priv->clk))
0196         return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
0197 
0198     priv->base = devm_platform_ioremap_resource(pdev, 0);
0199     if (IS_ERR(priv->base))
0200         return PTR_ERR(priv->base);
0201 
0202     ret = keembay_clk_enable(dev, priv->clk);
0203     if (ret)
0204         return ret;
0205 
0206     priv->chip.dev = dev;
0207     priv->chip.ops = &keembay_pwm_ops;
0208     priv->chip.npwm = KMB_TOTAL_PWM_CHANNELS;
0209 
0210     ret = devm_pwmchip_add(dev, &priv->chip);
0211     if (ret)
0212         return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
0213 
0214     return 0;
0215 }
0216 
0217 static const struct of_device_id keembay_pwm_of_match[] = {
0218     { .compatible = "intel,keembay-pwm" },
0219     { }
0220 };
0221 MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
0222 
0223 static struct platform_driver keembay_pwm_driver = {
0224     .probe  = keembay_pwm_probe,
0225     .driver = {
0226         .name = "pwm-keembay",
0227         .of_match_table = keembay_pwm_of_match,
0228     },
0229 };
0230 module_platform_driver(keembay_pwm_driver);
0231 
0232 MODULE_ALIAS("platform:pwm-keembay");
0233 MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
0234 MODULE_LICENSE("GPL v2");