Commit c33ba7ec8b9be0a6fe7a2bd47a36c991bf9189e1

Authored by Vasily Khoruzhick
Committed by Jagan Teki
1 parent ce138cb429

pwm: sunxi: choose best prescaler to improve PWM resolution

Choose best prescaler to improve PWM resolution. Without this change
driver chooses first prescaler that gives us period value within
range, but it could be not the best one.

Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Tested-by: Vagrant Cascadian <vagrant@debian.org>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
Reviewed-by: Jagan Teki <jagan@openedev.com>

Showing 1 changed file with 19 additions and 13 deletions Side-by-side Diff

drivers/pwm/sunxi_pwm.c
... ... @@ -67,49 +67,55 @@
67 67 {
68 68 struct sunxi_pwm_priv *priv = dev_get_priv(dev);
69 69 struct sunxi_pwm *regs = priv->regs;
70   - int prescaler;
71   - u32 v, period = 0, duty;
72   - u64 scaled_freq = 0;
  70 + int best_prescaler = 0;
  71 + u32 v, best_period = 0, duty;
  72 + u64 best_scaled_freq = 0;
73 73 const u32 nsecs_per_sec = 1000000000U;
74 74  
75 75 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
76 76  
77   - for (prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
  77 + for (int prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
78 78 prescaler++) {
  79 + u32 period = 0;
  80 + u64 scaled_freq = 0;
79 81 if (!prescaler_table[prescaler])
80 82 continue;
81 83 scaled_freq = lldiv(OSC_24MHZ, prescaler_table[prescaler]);
82 84 period = lldiv(scaled_freq * period_ns, nsecs_per_sec);
83   - if (period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX)
84   - break;
  85 + if ((period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) &&
  86 + best_period < period) {
  87 + best_period = period;
  88 + best_scaled_freq = scaled_freq;
  89 + best_prescaler = prescaler;
  90 + }
85 91 }
86 92  
87   - if (period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
  93 + if (best_period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
88 94 debug("%s: failed to find prescaler value\n", __func__);
89 95 return -EINVAL;
90 96 }
91 97  
92   - duty = lldiv(scaled_freq * duty_ns, nsecs_per_sec);
  98 + duty = lldiv(best_scaled_freq * duty_ns, nsecs_per_sec);
93 99  
94   - if (priv->prescaler != prescaler) {
  100 + if (priv->prescaler != best_prescaler) {
95 101 /* Mask clock to update prescaler */
96 102 v = readl(&regs->ctrl);
97 103 v &= ~SUNXI_PWM_CTRL_CLK_GATE;
98 104 writel(v, &regs->ctrl);
99 105 v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK;
100   - v |= (prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
  106 + v |= (best_prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
101 107 writel(v, &regs->ctrl);
102 108 v |= SUNXI_PWM_CTRL_CLK_GATE;
103 109 writel(v, &regs->ctrl);
104   - priv->prescaler = prescaler;
  110 + priv->prescaler = best_prescaler;
105 111 }
106 112  
107   - writel(SUNXI_PWM_CH0_PERIOD_PRD(period) |
  113 + writel(SUNXI_PWM_CH0_PERIOD_PRD(best_period) |
108 114 SUNXI_PWM_CH0_PERIOD_DUTY(duty), &regs->ch0_period);
109 115  
110 116 debug("%s: prescaler: %d, period: %d, duty: %d\n",
111 117 __func__, priv->prescaler,
112   - period, duty);
  118 + best_period, duty);
113 119  
114 120 return 0;
115 121 }