0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #include <linux/kernel.h>
0022 #include <linux/math64.h>
0023 #include <linux/err.h>
0024 #include <linux/module.h>
0025 #include <linux/of.h>
0026 #include <linux/platform_device.h>
0027 #include <linux/clk.h>
0028 #include <linux/pwm.h>
0029
0030 struct pwm_clk_chip {
0031 struct pwm_chip chip;
0032 struct clk *clk;
0033 bool clk_enabled;
0034 };
0035
0036 #define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
0037
0038 static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0039 const struct pwm_state *state)
0040 {
0041 struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip);
0042 int ret;
0043 u32 rate;
0044 u64 period = state->period;
0045 u64 duty_cycle = state->duty_cycle;
0046
0047 if (!state->enabled) {
0048 if (pwm->state.enabled) {
0049 clk_disable(pcchip->clk);
0050 pcchip->clk_enabled = false;
0051 }
0052 return 0;
0053 } else if (!pwm->state.enabled) {
0054 ret = clk_enable(pcchip->clk);
0055 if (ret)
0056 return ret;
0057 pcchip->clk_enabled = true;
0058 }
0059
0060
0061
0062
0063
0064
0065
0066
0067 rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
0068 ret = clk_set_rate(pcchip->clk, rate);
0069 if (ret)
0070 return ret;
0071
0072 if (state->polarity == PWM_POLARITY_INVERSED)
0073 duty_cycle = period - duty_cycle;
0074
0075 return clk_set_duty_cycle(pcchip->clk, duty_cycle, period);
0076 }
0077
0078 static const struct pwm_ops pwm_clk_ops = {
0079 .apply = pwm_clk_apply,
0080 .owner = THIS_MODULE,
0081 };
0082
0083 static int pwm_clk_probe(struct platform_device *pdev)
0084 {
0085 struct pwm_clk_chip *pcchip;
0086 int ret;
0087
0088 pcchip = devm_kzalloc(&pdev->dev, sizeof(*pcchip), GFP_KERNEL);
0089 if (!pcchip)
0090 return -ENOMEM;
0091
0092 pcchip->clk = devm_clk_get(&pdev->dev, NULL);
0093 if (IS_ERR(pcchip->clk))
0094 return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
0095 "Failed to get clock\n");
0096
0097 pcchip->chip.dev = &pdev->dev;
0098 pcchip->chip.ops = &pwm_clk_ops;
0099 pcchip->chip.npwm = 1;
0100
0101 ret = clk_prepare(pcchip->clk);
0102 if (ret < 0)
0103 return dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
0104
0105 ret = pwmchip_add(&pcchip->chip);
0106 if (ret < 0) {
0107 clk_unprepare(pcchip->clk);
0108 return dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
0109 }
0110
0111 platform_set_drvdata(pdev, pcchip);
0112 return 0;
0113 }
0114
0115 static int pwm_clk_remove(struct platform_device *pdev)
0116 {
0117 struct pwm_clk_chip *pcchip = platform_get_drvdata(pdev);
0118
0119 pwmchip_remove(&pcchip->chip);
0120
0121 if (pcchip->clk_enabled)
0122 clk_disable(pcchip->clk);
0123
0124 clk_unprepare(pcchip->clk);
0125
0126 return 0;
0127 }
0128
0129 static const struct of_device_id pwm_clk_dt_ids[] = {
0130 { .compatible = "clk-pwm", },
0131 { }
0132 };
0133 MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids);
0134
0135 static struct platform_driver pwm_clk_driver = {
0136 .driver = {
0137 .name = "pwm-clk",
0138 .of_match_table = pwm_clk_dt_ids,
0139 },
0140 .probe = pwm_clk_probe,
0141 .remove = pwm_clk_remove,
0142 };
0143 module_platform_driver(pwm_clk_driver);
0144
0145 MODULE_ALIAS("platform:pwm-clk");
0146 MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>");
0147 MODULE_DESCRIPTION("Clock based PWM driver");
0148 MODULE_LICENSE("GPL");