项目使用到licheepi的uart接口,外接使用921600频率的uart设备,后应用程序中设置波特率后发现程序乱码,后查到内核uart时钟为24M导致分频后偏差比较大。
在V3S的uart是挂在APB2下面的,需修改APB2时钟,而目前的内核默认使用的一般是uboot设置的时钟配置,一般使用clk_ops->get_parent获取寄存器中的数据,这是便是父时钟节点。
然后使用clk_ops->recalc_rate获取时钟频率。
而寄存器中的值一般是在uboot时期设置好的,clk_ops->set_parent一般在启动初始化时候未使用。
然后是修改,因为设备同时使用921600 和 115200 两个频率,即取两个频率的最大公约数的倍数。 然后我们选定120M作为APB2时钟。
修改uboot的arch/arm/mach-sunxi/clock_sun6i.c,
void clock_init_uart(void)
{
#if CONFIG_CONS_INDEX < 5
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* uart clock source is apb2 */
writel(APB2_CLK_SRC_PLL6 |
APB2_CLK_RATE_N_1|
APB2_CLK_RATE_M(5),
&ccm->apb2_div);
....
}
上面APB2_CLK_SRC_PLL6 为2,即使用PLL_PERIPH0时钟,其值为600M,
APB2 = 600 / 1/5 = 120M
然后再修改include/configs/sunxi-common.h
#define CONFIG_SYS_NS16550_CLK 120000000
#ifndef CONFIG_DM_SERIAL
最后在修改内核里面的APB2时钟设置 (为什么要设置呢? 因为内核只读取uboot的父时钟作为基准,这里读取的值是2 ,即使用PLL_PERIPH0时钟)
修改 内核里 drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
0, 4, /* M */
16, 1, /* P */
24, 2, /* mux */
0);
OK,修改完成,虽然频率有点偏差,但是不影响正常使用。
附: 内核读取时钟方式:
ccu: clock@01c20000 {
compatible = "allwinner,sun8i-v3s-ccu";
reg = <0x01c20000 0x400>;
clocks = <&osc24M>, <&osc32k>;
clock-names = "hosc", "losc";
#clock-cells = <1>;
#reset-cells = <1>;
};
uart0: serial@01c28000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28000 0x400>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART0>;
resets = <&ccu RST_BUS_UART0>;
status = "disabled";
};
驱动中则使用上述的clock-names属性作为clk_get()或devm_clk_get()的第二个参数来申请时钟,譬如获取第2个时钟
devm_clk_get(pctl->dev, “hosc”)
clocks = <&ccu CLK_BUS_UART0> 里的CLK_BUS_UART0这个index是与相应时钟驱动中ccu的表的顺序对应的,这里定义在include/dt-bindings/clock/sun8i-v3s-ccu.h,一般dts里有显示包含文件。一般devm_clk_get也可以根据这个index来查找。