summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Vokáč <michal.vokac@ysoft.com>2018-10-01 10:19:48 -0400
committerThierry Reding <thierry.reding@gmail.com>2018-12-12 05:52:47 -0500
commitbf9b0b1b0b6cd51797ce79b6fa5fc2d1baa2719e (patch)
treef8d92ab5fc082aea2e73d695902a2dc033c4478a
parent9f617ada9f823dff1944ebcf92ef4a05f5f322b7 (diff)
pwm: imx: Implement get_state() function for hardware readout
Implement the get_state() function and set the initial state to reflect real state of the hardware. This allows to keep the PWM running if it was enabled in bootloader. It is very similar to the GPIO behavior. GPIO pin set as output in bootloader keep the same setting in Linux unless it is reconfigured. If we find the PWM block enabled we need to prepare and enable its source clock otherwise the clock will be disabled late in the boot as unused. That will leave the PWM in enabled state but with disabled clock. That has a side effect that the PWM output is left at its current level at which the clock was disabled. It is totally non-deterministic and it may be LOW or HIGH. Signed-off-by: Michal Vokáč <michal.vokac@ysoft.com> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r--drivers/pwm/pwm-imx.c53
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 7a4907b73d7c..6cd3b72fbbc1 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -83,6 +83,9 @@
83 83
84#define MX3_PWM_SWR_LOOP 5 84#define MX3_PWM_SWR_LOOP 5
85 85
86/* PWMPR register value of 0xffff has the same effect as 0xfffe */
87#define MX3_PWMPR_MAX 0xfffe
88
86struct imx_chip { 89struct imx_chip {
87 struct clk *clk_per; 90 struct clk *clk_per;
88 91
@@ -93,6 +96,55 @@ struct imx_chip {
93 96
94#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) 97#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
95 98
99static void imx_pwm_get_state(struct pwm_chip *chip,
100 struct pwm_device *pwm, struct pwm_state *state)
101{
102 struct imx_chip *imx = to_imx_chip(chip);
103 u32 period, prescaler, pwm_clk, ret, val;
104 u64 tmp;
105
106 val = readl(imx->mmio_base + MX3_PWMCR);
107
108 if (val & MX3_PWMCR_EN) {
109 state->enabled = true;
110 ret = clk_prepare_enable(imx->clk_per);
111 if (ret)
112 return;
113 } else {
114 state->enabled = false;
115 }
116
117 switch (FIELD_GET(MX3_PWMCR_POUTC, val)) {
118 case MX3_PWMCR_POUTC_NORMAL:
119 state->polarity = PWM_POLARITY_NORMAL;
120 break;
121 case MX3_PWMCR_POUTC_INVERTED:
122 state->polarity = PWM_POLARITY_INVERSED;
123 break;
124 default:
125 dev_warn(chip->dev, "can't set polarity, output disconnected");
126 }
127
128 prescaler = MX3_PWMCR_PRESCALER_GET(val);
129 pwm_clk = clk_get_rate(imx->clk_per);
130 pwm_clk = DIV_ROUND_CLOSEST_ULL(pwm_clk, prescaler);
131 val = readl(imx->mmio_base + MX3_PWMPR);
132 period = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
133
134 /* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
135 tmp = NSEC_PER_SEC * (u64)(period + 2);
136 state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
137
138 /* PWMSAR can be read only if PWM is enabled */
139 if (state->enabled) {
140 val = readl(imx->mmio_base + MX3_PWMSAR);
141 tmp = NSEC_PER_SEC * (u64)(val);
142 state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
143 } else {
144 state->duty_cycle = 0;
145 }
146}
147
96static int imx_pwm_config_v1(struct pwm_chip *chip, 148static int imx_pwm_config_v1(struct pwm_chip *chip,
97 struct pwm_device *pwm, int duty_ns, int period_ns) 149 struct pwm_device *pwm, int duty_ns, int period_ns)
98{ 150{
@@ -272,6 +324,7 @@ static const struct pwm_ops imx_pwm_ops_v1 = {
272 324
273static const struct pwm_ops imx_pwm_ops_v2 = { 325static const struct pwm_ops imx_pwm_ops_v2 = {
274 .apply = imx_pwm_apply_v2, 326 .apply = imx_pwm_apply_v2,
327 .get_state = imx_pwm_get_state,
275 .owner = THIS_MODULE, 328 .owner = THIS_MODULE,
276}; 329};
277 330