root/drivers/clk/clk-pwm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_clk_pwm
  2. clk_pwm_prepare
  3. clk_pwm_unprepare
  4. clk_pwm_recalc_rate
  5. clk_pwm_get_duty_cycle
  6. clk_pwm_probe
  7. clk_pwm_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2014 Philipp Zabel, Pengutronix
   4  *
   5  * PWM (mis)used as clock output
   6  */
   7 #include <linux/clk-provider.h>
   8 #include <linux/kernel.h>
   9 #include <linux/module.h>
  10 #include <linux/of.h>
  11 #include <linux/platform_device.h>
  12 #include <linux/pwm.h>
  13 
  14 struct clk_pwm {
  15         struct clk_hw hw;
  16         struct pwm_device *pwm;
  17         u32 fixed_rate;
  18 };
  19 
  20 static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw)
  21 {
  22         return container_of(hw, struct clk_pwm, hw);
  23 }
  24 
  25 static int clk_pwm_prepare(struct clk_hw *hw)
  26 {
  27         struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  28 
  29         return pwm_enable(clk_pwm->pwm);
  30 }
  31 
  32 static void clk_pwm_unprepare(struct clk_hw *hw)
  33 {
  34         struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  35 
  36         pwm_disable(clk_pwm->pwm);
  37 }
  38 
  39 static unsigned long clk_pwm_recalc_rate(struct clk_hw *hw,
  40                                          unsigned long parent_rate)
  41 {
  42         struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  43 
  44         return clk_pwm->fixed_rate;
  45 }
  46 
  47 static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
  48 {
  49         struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  50         struct pwm_state state;
  51 
  52         pwm_get_state(clk_pwm->pwm, &state);
  53 
  54         duty->num = state.duty_cycle;
  55         duty->den = state.period;
  56 
  57         return 0;
  58 }
  59 
  60 static const struct clk_ops clk_pwm_ops = {
  61         .prepare = clk_pwm_prepare,
  62         .unprepare = clk_pwm_unprepare,
  63         .recalc_rate = clk_pwm_recalc_rate,
  64         .get_duty_cycle = clk_pwm_get_duty_cycle,
  65 };
  66 
  67 static int clk_pwm_probe(struct platform_device *pdev)
  68 {
  69         struct device_node *node = pdev->dev.of_node;
  70         struct clk_init_data init;
  71         struct clk_pwm *clk_pwm;
  72         struct pwm_device *pwm;
  73         struct pwm_args pargs;
  74         const char *clk_name;
  75         int ret;
  76 
  77         clk_pwm = devm_kzalloc(&pdev->dev, sizeof(*clk_pwm), GFP_KERNEL);
  78         if (!clk_pwm)
  79                 return -ENOMEM;
  80 
  81         pwm = devm_pwm_get(&pdev->dev, NULL);
  82         if (IS_ERR(pwm))
  83                 return PTR_ERR(pwm);
  84 
  85         pwm_get_args(pwm, &pargs);
  86         if (!pargs.period) {
  87                 dev_err(&pdev->dev, "invalid PWM period\n");
  88                 return -EINVAL;
  89         }
  90 
  91         if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
  92                 clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period;
  93 
  94         if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
  95             pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
  96                 dev_err(&pdev->dev,
  97                         "clock-frequency does not match PWM period\n");
  98                 return -EINVAL;
  99         }
 100 
 101         /*
 102          * FIXME: pwm_apply_args() should be removed when switching to the
 103          * atomic PWM API.
 104          */
 105         pwm_apply_args(pwm);
 106         ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
 107         if (ret < 0)
 108                 return ret;
 109 
 110         clk_name = node->name;
 111         of_property_read_string(node, "clock-output-names", &clk_name);
 112 
 113         init.name = clk_name;
 114         init.ops = &clk_pwm_ops;
 115         init.flags = 0;
 116         init.num_parents = 0;
 117 
 118         clk_pwm->pwm = pwm;
 119         clk_pwm->hw.init = &init;
 120         ret = devm_clk_hw_register(&pdev->dev, &clk_pwm->hw);
 121         if (ret)
 122                 return ret;
 123 
 124         return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw);
 125 }
 126 
 127 static int clk_pwm_remove(struct platform_device *pdev)
 128 {
 129         of_clk_del_provider(pdev->dev.of_node);
 130 
 131         return 0;
 132 }
 133 
 134 static const struct of_device_id clk_pwm_dt_ids[] = {
 135         { .compatible = "pwm-clock" },
 136         { }
 137 };
 138 MODULE_DEVICE_TABLE(of, clk_pwm_dt_ids);
 139 
 140 static struct platform_driver clk_pwm_driver = {
 141         .probe = clk_pwm_probe,
 142         .remove = clk_pwm_remove,
 143         .driver = {
 144                 .name = "pwm-clock",
 145                 .of_match_table = of_match_ptr(clk_pwm_dt_ids),
 146         },
 147 };
 148 
 149 module_platform_driver(clk_pwm_driver);
 150 
 151 MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
 152 MODULE_DESCRIPTION("PWM clock driver");
 153 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */