Message ID | 20220912081306.24662-1-iivanov@suse.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v5] clk: bcm2835: Round UART input clock up | expand |
I am sorry, forgot to add change log. Change since v4: * Rename rounding function and it parameter from clock to rate. * Fix double ;; typo. Regards, Ivan On 2022-09-12 11:13, Ivan T. Ivanov wrote: > It was reported that RPi3[1] and RPi Zero 2W boards have issues with > the Bluetooth. It turns out that when switching from initial to > operation speed host and device no longer can talk each other because > host uses incorrect UART baud rate. > > The UART driver used in this case is amba-pl011. Original fix, see > below Github link[2], was inside pl011 module, but somehow it didn't > look as the right place to fix. Beside that this original rounding > function is not exactly perfect for all possible clock values. So I > deiced to move the hack to the platform which actually need it. > > The UART clock is initialised to be as close to the requested > frequency as possible without exceeding it. Now that there is a > clock manager that returns the actual frequencies, an expected > 48MHz clock is reported as 47999625. If the requested baud rate > == requested clock/16, there is no headroom and the slight > reduction in actual clock rate results in failure. > > If increasing a clock by less than 0.1% changes it from ..999.. > to ..000.., round it up. > > [1] https://bugzilla.suse.com/show_bug.cgi?id=1188238 > [2] > https://github.com/raspberrypi/linux/commit/ab3f1b39537f6d3825b8873006fbe2fc5ff057b7 > > Cc: Phil Elwell <phil@raspberrypi.com> > Signed-off-by: Ivan T. Ivanov <iivanov@suse.de> > Reviewed-by: Stefan Wahren <stefan.wahren@i2se.com> > --- > drivers/clk/bcm/clk-bcm2835.c | 35 +++++++++++++++++++++++++++++++++-- > 1 file changed, 33 insertions(+), 2 deletions(-) > > diff --git a/drivers/clk/bcm/clk-bcm2835.c > b/drivers/clk/bcm/clk-bcm2835.c > index 48a1eb9f2d55..4361ec4c659a 100644 > --- a/drivers/clk/bcm/clk-bcm2835.c > +++ b/drivers/clk/bcm/clk-bcm2835.c > @@ -30,6 +30,7 @@ > #include <linux/debugfs.h> > #include <linux/delay.h> > #include <linux/io.h> > +#include <linux/math.h> > #include <linux/module.h> > #include <linux/of_device.h> > #include <linux/platform_device.h> > @@ -502,6 +503,8 @@ struct bcm2835_clock_data { > bool low_jitter; > > u32 tcnt_mux; > + > + bool round_up; > }; > > struct bcm2835_gate_data { > @@ -993,12 +996,34 @@ static long > bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, > return temp; > } > > +static unsigned long bcm2835_round_rate(unsigned long rate) > +{ > + unsigned long scaler; > + unsigned long limit; > + > + limit = rate / 100000; > + > + scaler = 1; > + while (scaler < limit) > + scaler *= 10; > + > + /* > + * If increasing a clock by less than 0.1% changes it > + * from ..999.. to ..000.., round up. > + */ > + if ((rate + scaler - 1) / scaler % 1000 == 0) > + rate = roundup(rate, scaler); > + > + return rate; > +} > + > static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, > unsigned long parent_rate) > { > struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); > struct bcm2835_cprman *cprman = clock->cprman; > const struct bcm2835_clock_data *data = clock->data; > + unsigned long rate; > u32 div; > > if (data->int_bits == 0 && data->frac_bits == 0) > @@ -1006,7 +1031,12 @@ static unsigned long > bcm2835_clock_get_rate(struct clk_hw *hw, > > div = cprman_read(cprman, data->div_reg); > > - return bcm2835_clock_rate_from_divisor(clock, parent_rate, div); > + rate = bcm2835_clock_rate_from_divisor(clock, parent_rate, div); > + > + if (data->round_up) > + rate = bcm2835_round_rate(rate); > + > + return rate; > } > > static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock) > @@ -2143,7 +2173,8 @@ static const struct bcm2835_clk_desc > clk_desc_array[] = { > .div_reg = CM_UARTDIV, > .int_bits = 10, > .frac_bits = 12, > - .tcnt_mux = 28), > + .tcnt_mux = 28, > + .round_up = true), > > /* TV encoder clock. Only operating frequency is 108Mhz. */ > [BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(
Quoting Ivan T. Ivanov (2022-09-12 01:13:04) > It was reported that RPi3[1] and RPi Zero 2W boards have issues with > the Bluetooth. It turns out that when switching from initial to > operation speed host and device no longer can talk each other because > host uses incorrect UART baud rate. > > The UART driver used in this case is amba-pl011. Original fix, see > below Github link[2], was inside pl011 module, but somehow it didn't > look as the right place to fix. Beside that this original rounding > function is not exactly perfect for all possible clock values. So I > deiced to move the hack to the platform which actually need it. > > The UART clock is initialised to be as close to the requested > frequency as possible without exceeding it. Now that there is a > clock manager that returns the actual frequencies, an expected > 48MHz clock is reported as 47999625. If the requested baud rate > == requested clock/16, there is no headroom and the slight > reduction in actual clock rate results in failure. > > If increasing a clock by less than 0.1% changes it from ..999.. > to ..000.., round it up. > > [1] https://bugzilla.suse.com/show_bug.cgi?id=1188238 > [2] https://github.com/raspberrypi/linux/commit/ab3f1b39537f6d3825b8873006fbe2fc5ff057b7 > > Cc: Phil Elwell <phil@raspberrypi.com> > Signed-off-by: Ivan T. Ivanov <iivanov@suse.de> > Reviewed-by: Stefan Wahren <stefan.wahren@i2se.com> > --- Applied to clk-next
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 48a1eb9f2d55..4361ec4c659a 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -30,6 +30,7 @@ #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> @@ -502,6 +503,8 @@ struct bcm2835_clock_data { bool low_jitter; u32 tcnt_mux; + + bool round_up; }; struct bcm2835_gate_data { @@ -993,12 +996,34 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, return temp; } +static unsigned long bcm2835_round_rate(unsigned long rate) +{ + unsigned long scaler; + unsigned long limit; + + limit = rate / 100000; + + scaler = 1; + while (scaler < limit) + scaler *= 10; + + /* + * If increasing a clock by less than 0.1% changes it + * from ..999.. to ..000.., round up. + */ + if ((rate + scaler - 1) / scaler % 1000 == 0) + rate = roundup(rate, scaler); + + return rate; +} + static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, unsigned long parent_rate) { struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct bcm2835_cprman *cprman = clock->cprman; const struct bcm2835_clock_data *data = clock->data; + unsigned long rate; u32 div; if (data->int_bits == 0 && data->frac_bits == 0) @@ -1006,7 +1031,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, div = cprman_read(cprman, data->div_reg); - return bcm2835_clock_rate_from_divisor(clock, parent_rate, div); + rate = bcm2835_clock_rate_from_divisor(clock, parent_rate, div); + + if (data->round_up) + rate = bcm2835_round_rate(rate); + + return rate; } static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock) @@ -2143,7 +2173,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .div_reg = CM_UARTDIV, .int_bits = 10, .frac_bits = 12, - .tcnt_mux = 28), + .tcnt_mux = 28, + .round_up = true), /* TV encoder clock. Only operating frequency is 108Mhz. */ [BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(