Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Toshiba Visconti pulse-width-modulation controller driver
0004  *
0005  * Copyright (c) 2020 - 2021 TOSHIBA CORPORATION
0006  * Copyright (c) 2020 - 2021 Toshiba Electronic Devices & Storage Corporation
0007  *
0008  * Authors: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
0009  *
0010  * Limitations:
0011  * - The fixed input clock is running at 1 MHz and is divided by either 1,
0012  *   2, 4 or 8.
0013  * - When the settings of the PWM are modified, the new values are shadowed
0014  *   in hardware until the PIPGM_PCSR register is written and the currently
0015  *   running period is completed. This way the hardware switches atomically
0016  *   from the old setting to the new.
0017  * - Disabling the hardware completes the currently running period and keeps
0018  *   the output at low level at all times.
0019  */
0020 
0021 #include <linux/err.h>
0022 #include <linux/io.h>
0023 #include <linux/module.h>
0024 #include <linux/of_device.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/pwm.h>
0027 
0028 #define PIPGM_PCSR(ch) (0x400 + 4 * (ch))
0029 #define PIPGM_PDUT(ch) (0x420 + 4 * (ch))
0030 #define PIPGM_PWMC(ch) (0x440 + 4 * (ch))
0031 
0032 #define PIPGM_PWMC_PWMACT       BIT(5)
0033 #define PIPGM_PWMC_CLK_MASK     GENMASK(1, 0)
0034 #define PIPGM_PWMC_POLARITY_MASK    GENMASK(5, 5)
0035 
0036 struct visconti_pwm_chip {
0037     struct pwm_chip chip;
0038     void __iomem *base;
0039 };
0040 
0041 static inline struct visconti_pwm_chip *visconti_pwm_from_chip(struct pwm_chip *chip)
0042 {
0043     return container_of(chip, struct visconti_pwm_chip, chip);
0044 }
0045 
0046 static int visconti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0047                   const struct pwm_state *state)
0048 {
0049     struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip);
0050     u32 period, duty_cycle, pwmc0;
0051 
0052     if (!state->enabled) {
0053         writel(0, priv->base + PIPGM_PCSR(pwm->hwpwm));
0054         return 0;
0055     }
0056 
0057     /*
0058      * The biggest period the hardware can provide is
0059      *  (0xffff << 3) * 1000 ns
0060      * This value fits easily in an u32, so simplify the maths by
0061      * capping the values to 32 bit integers.
0062      */
0063     if (state->period > (0xffff << 3) * 1000)
0064         period = (0xffff << 3) * 1000;
0065     else
0066         period = state->period;
0067 
0068     if (state->duty_cycle > period)
0069         duty_cycle = period;
0070     else
0071         duty_cycle = state->duty_cycle;
0072 
0073     /*
0074      * The input clock runs fixed at 1 MHz, so we have only
0075      * microsecond resolution and so can divide by
0076      * NSEC_PER_SEC / CLKFREQ = 1000 without losing precision.
0077      */
0078     period /= 1000;
0079     duty_cycle /= 1000;
0080 
0081     if (!period)
0082         return -ERANGE;
0083 
0084     /*
0085      * PWMC controls a divider that divides the input clk by a power of two
0086      * between 1 and 8. As a smaller divider yields higher precision, pick
0087      * the smallest possible one. As period is at most 0xffff << 3, pwmc0 is
0088      * in the intended range [0..3].
0089      */
0090     pwmc0 = fls(period >> 16);
0091     if (WARN_ON(pwmc0 > 3))
0092         return -EINVAL;
0093 
0094     period >>= pwmc0;
0095     duty_cycle >>= pwmc0;
0096 
0097     if (state->polarity == PWM_POLARITY_INVERSED)
0098         pwmc0 |= PIPGM_PWMC_PWMACT;
0099     writel(pwmc0, priv->base + PIPGM_PWMC(pwm->hwpwm));
0100     writel(duty_cycle, priv->base + PIPGM_PDUT(pwm->hwpwm));
0101     writel(period, priv->base + PIPGM_PCSR(pwm->hwpwm));
0102 
0103     return 0;
0104 }
0105 
0106 static void visconti_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
0107                    struct pwm_state *state)
0108 {
0109     struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip);
0110     u32 period, duty, pwmc0, pwmc0_clk;
0111 
0112     period = readl(priv->base + PIPGM_PCSR(pwm->hwpwm));
0113     duty = readl(priv->base + PIPGM_PDUT(pwm->hwpwm));
0114     pwmc0 = readl(priv->base + PIPGM_PWMC(pwm->hwpwm));
0115     pwmc0_clk = pwmc0 & PIPGM_PWMC_CLK_MASK;
0116 
0117     state->period = (period << pwmc0_clk) * NSEC_PER_USEC;
0118     state->duty_cycle = (duty << pwmc0_clk) * NSEC_PER_USEC;
0119     if (pwmc0 & PIPGM_PWMC_POLARITY_MASK)
0120         state->polarity = PWM_POLARITY_INVERSED;
0121     else
0122         state->polarity = PWM_POLARITY_NORMAL;
0123 
0124     state->enabled = true;
0125 }
0126 
0127 static const struct pwm_ops visconti_pwm_ops = {
0128     .apply = visconti_pwm_apply,
0129     .get_state = visconti_pwm_get_state,
0130     .owner = THIS_MODULE,
0131 };
0132 
0133 static int visconti_pwm_probe(struct platform_device *pdev)
0134 {
0135     struct device *dev = &pdev->dev;
0136     struct visconti_pwm_chip *priv;
0137     int ret;
0138 
0139     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0140     if (!priv)
0141         return -ENOMEM;
0142 
0143     priv->base = devm_platform_ioremap_resource(pdev, 0);
0144     if (IS_ERR(priv->base))
0145         return PTR_ERR(priv->base);
0146 
0147     priv->chip.dev = dev;
0148     priv->chip.ops = &visconti_pwm_ops;
0149     priv->chip.npwm = 4;
0150 
0151     ret = devm_pwmchip_add(&pdev->dev, &priv->chip);
0152     if (ret < 0)
0153         return dev_err_probe(&pdev->dev, ret, "Cannot register visconti PWM\n");
0154 
0155     return 0;
0156 }
0157 
0158 static const struct of_device_id visconti_pwm_of_match[] = {
0159     { .compatible = "toshiba,visconti-pwm", },
0160     { }
0161 };
0162 MODULE_DEVICE_TABLE(of, visconti_pwm_of_match);
0163 
0164 static struct platform_driver visconti_pwm_driver = {
0165     .driver = {
0166         .name = "pwm-visconti",
0167         .of_match_table = visconti_pwm_of_match,
0168     },
0169     .probe = visconti_pwm_probe,
0170 };
0171 module_platform_driver(visconti_pwm_driver);
0172 
0173 MODULE_LICENSE("GPL v2");
0174 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
0175 MODULE_ALIAS("platform:pwm-visconti");