Skip to content

Commit f690a4d

Browse files
hanzydbebarino
authored andcommitted
clk: bcm2835: Round UART input clock up
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] raspberrypi/linux@ab3f1b3 Cc: Phil Elwell <[email protected]> Signed-off-by: Ivan T. Ivanov <[email protected]> Reviewed-by: Stefan Wahren <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent 6c54228 commit f690a4d

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

drivers/clk/bcm/clk-bcm2835.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/debugfs.h>
3131
#include <linux/delay.h>
3232
#include <linux/io.h>
33+
#include <linux/math.h>
3334
#include <linux/module.h>
3435
#include <linux/of_device.h>
3536
#include <linux/platform_device.h>
@@ -502,6 +503,8 @@ struct bcm2835_clock_data {
502503
bool low_jitter;
503504

504505
u32 tcnt_mux;
506+
507+
bool round_up;
505508
};
506509

507510
struct bcm2835_gate_data {
@@ -993,20 +996,47 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
993996
return temp;
994997
}
995998

999+
static unsigned long bcm2835_round_rate(unsigned long rate)
1000+
{
1001+
unsigned long scaler;
1002+
unsigned long limit;
1003+
1004+
limit = rate / 100000;
1005+
1006+
scaler = 1;
1007+
while (scaler < limit)
1008+
scaler *= 10;
1009+
1010+
/*
1011+
* If increasing a clock by less than 0.1% changes it
1012+
* from ..999.. to ..000.., round up.
1013+
*/
1014+
if ((rate + scaler - 1) / scaler % 1000 == 0)
1015+
rate = roundup(rate, scaler);
1016+
1017+
return rate;
1018+
}
1019+
9961020
static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
9971021
unsigned long parent_rate)
9981022
{
9991023
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
10001024
struct bcm2835_cprman *cprman = clock->cprman;
10011025
const struct bcm2835_clock_data *data = clock->data;
1026+
unsigned long rate;
10021027
u32 div;
10031028

10041029
if (data->int_bits == 0 && data->frac_bits == 0)
10051030
return parent_rate;
10061031

10071032
div = cprman_read(cprman, data->div_reg);
10081033

1009-
return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
1034+
rate = bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
1035+
1036+
if (data->round_up)
1037+
rate = bcm2835_round_rate(rate);
1038+
1039+
return rate;
10101040
}
10111041

10121042
static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
@@ -2143,7 +2173,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
21432173
.div_reg = CM_UARTDIV,
21442174
.int_bits = 10,
21452175
.frac_bits = 12,
2146-
.tcnt_mux = 28),
2176+
.tcnt_mux = 28,
2177+
.round_up = true),
21472178

21482179
/* TV encoder clock. Only operating frequency is 108Mhz. */
21492180
[BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(

0 commit comments

Comments
 (0)