^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Cirrus Logic CLPS711X PWM driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Author: Alexander Shiyan <shc_work@mail.ru>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/pwm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) struct clps711x_chip {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) struct pwm_chip chip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) void __iomem *pmpcon;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) spinlock_t lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) return container_of(chip, struct clps711x_chip, chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) u32 shift = (n + 1) * 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) u32 tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) spin_lock_irqsave(&priv->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) tmp = readl(priv->pmpcon);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) tmp &= ~(0xf << shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) tmp |= v << shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) writel(tmp, priv->pmpcon);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) spin_unlock_irqrestore(&priv->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) /* Duty cycle 0..15 max */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) return DIV64_U64_ROUND_CLOSEST(v * 0xf, pwm->args.period);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct clps711x_chip *priv = to_clps711x_chip(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) unsigned int freq = clk_get_rate(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (!freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* Store constant period value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) int duty_ns, int period_ns)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) struct clps711x_chip *priv = to_clps711x_chip(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) unsigned int duty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (period_ns != pwm->args.period)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) duty = clps711x_get_duty(pwm, duty_ns);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) struct clps711x_chip *priv = to_clps711x_chip(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) unsigned int duty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct clps711x_chip *priv = to_clps711x_chip(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) static const struct pwm_ops clps711x_pwm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .request = clps711x_pwm_request,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .config = clps711x_pwm_config,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .enable = clps711x_pwm_enable,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .disable = clps711x_pwm_disable,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) const struct of_phandle_args *args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) if (args->args[0] >= chip->npwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) return ERR_PTR(-EINVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return pwm_request_from_chip(chip, args->args[0], NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static int clps711x_pwm_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) struct clps711x_chip *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (IS_ERR(priv->pmpcon))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return PTR_ERR(priv->pmpcon);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) priv->clk = devm_clk_get(&pdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) if (IS_ERR(priv->clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return PTR_ERR(priv->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) priv->chip.ops = &clps711x_pwm_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) priv->chip.dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) priv->chip.base = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) priv->chip.npwm = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) priv->chip.of_xlate = clps711x_pwm_xlate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) priv->chip.of_pwm_n_cells = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) spin_lock_init(&priv->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) platform_set_drvdata(pdev, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return pwmchip_add(&priv->chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static int clps711x_pwm_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct clps711x_chip *priv = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) return pwmchip_remove(&priv->chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) { .compatible = "cirrus,ep7209-pwm", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) static struct platform_driver clps711x_pwm_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) .name = "clps711x-pwm",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) .probe = clps711x_pwm_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) .remove = clps711x_pwm_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) module_platform_driver(clps711x_pwm_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) MODULE_LICENSE("GPL");