无法将 SPI 时钟降低到 18Mhz 左右以下

问题描述 投票:0回答:1

我有一块开发板,LS1028ARDB。我需要对 LS1028 的 FlexSPI 与我的自定义设备之间的通信进行一些概念验证。

所以连接如下:

LS1028ARDB <--- QSPI port (managed by FlexSPI) <----> My device.

问题在于开发板暴露了具有 1.8V 电压引脚的 QSPI 端口,而我的目标设备在 3.3V 下运行。我想到了可以使用电压切换器的想法。问题是它们大多很慢,这似乎在我的情况下,所以通信不可靠 - 这对我来说很重要 - 我的测试不需要快速通信 - 我需要可靠的通信 - 一般来说较低的 SPI频率越高越好。

问题是我无法将 SPI 时钟重新配置为低于 18Mhz,这在我使用电压切换器的情况下相当快。我通过设备树中的 spi-max-Frequency 属性管理 SPI 频率。我可以将时钟设置为例如。 50Mhz 并且此更改应用正确,但任何其他低值(如 10Mhz、5Mhz、1Mhz 等)都会导致时钟设置为 18Mhz 左右

下面我发布了一些代码,但请记住,所有时钟设置、时钟配置等都是由我没有接触过的主线驱动程序管理的。我所做的唯一更改是设备树中的一个附加节点和一个触发 SPI 操作的非常小的驱动程序。 我在 /sys/kernel/debug/clk/fspi_clk/clk_rate 中验证的时钟速度。

如果我将 spi-max-Frequency 设置为 50Mhz 那么 /sys/kernel/debug/clk/fspi_clk/clk_rate 显示:大约 48Mhz

如果我将 spi-max-Frequency 设置为 10/5/1Mhz,则 /sys/kernel/debug/clk/fspi_clk/clk_rate 显示:大约 18Mhz。

主线 fsl-ls-1028a.dtsi fspi 节点:

    fspi: spi@20c0000 {
        compatible = "nxp,lx2160a-fspi";
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0x0 0x20c0000 0x0 0x10000>,
              <0x0 0x20000000 0x0 0x10000000>;
        reg-names = "fspi_base", "fspi_mmap";
        interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&fspi_clk>, <&fspi_clk>;
        clock-names = "fspi_en", "fspi";
        status = "disabled";
    };

主线 fsl-ls-1028a-rdb.dts - 我仅显示我所做的修改:

&fspi {
    status = "okay";

    pbspimem: pbspimem@0 {
        compatible = "pb,pb-spi-mem";
        #address-cells = <1>;
        #size-cells = <1>;
        spi-max-frequency = <5000000>;
        spi-rx-bus-width = <1>; /* 1 SPI Rx lines */
        spi-tx-bus-width = <1>; /* 1 SPI Tx line */
        reg = <0>;
    };
};

我非常简单的驱动程序:

#include "linux/types.h"
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/spi/spi-mem.h>
#include <linux/dma-mapping.h>


static int pb_spi_mem_probe(struct spi_mem *spimem)
{
    int ret;
    uint8_t buf[64];
    memset(buf, 0, sizeof(buf));

    struct spi_mem_op op = SPI_MEM_OP(
        SPI_MEM_OP_CMD(0x80, 1),
        SPI_MEM_OP_ADDR(3, 0x000004, 1),
        SPI_MEM_OP_NO_DUMMY,
        SPI_MEM_OP_DATA_IN(4, buf, 1)
    );

    ret = spi_mem_exec_op(spimem, &op);

    if (ret) {
        pr_info("Cannot execute op\n");
    }

    return ret;
}

static int pb_spi_mem_remove(struct spi_mem *spimem)
{
    pr_info("%s\n", __func__);

    return 0;
}


static const struct spi_device_id spi_nor_dev_ids[] = {
    {"pb-spi-mem"},
    { } /* sentinel */
};
MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids);

static const struct of_device_id pb_spi_mem_of_table[] = {
    { .compatible = "pb,pb-spi-mem" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pb_spi_mem_of_table);

static struct spi_mem_driver pb_spi_mem_driver = {
    .spidrv = {
        .driver = {
            .name = "pb-spi-mem-namer",
            .of_match_table = pb_spi_mem_of_table
        },
        .id_table = spi_nor_dev_ids
    },
    .probe = pb_spi_mem_probe,
    .remove = pb_spi_mem_remove
};

module_spi_mem_driver(pb_spi_mem_driver);

MODULE_LICENSE("GPL");

如您所见,它触发了 spi mem 操作。如果您不知道那是什么 - 没关系 - 相信我,在幕后这个调用最终会在 nxp_fspi_exec_op 中结束,它是 FlexSPI 控制器的驱动程序。

控制器驱动程序中发生了很多事情,因此发布所有驱动程序源代码是没有用的,所以我只提取了相关部分 - 我跟踪了调用堆栈,我很确定调用堆栈到达了这部分:

static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
{
    unsigned long rate = spi->max_speed_hz;
    int ret;
    uint64_t size_kb;
    
    [...]

    nxp_fspi_clk_disable_unprep(f);

    ret = clk_set_rate(f->clk, rate);
    if (ret)
        return;

    ret = nxp_fspi_clk_prep_enable(f);
    if (ret)
        return;

    f->selected = spi->chip_select;
}

static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f)
{
    int ret;

    if (is_acpi_node(f->dev->fwnode))
        return 0;

    ret = clk_prepare_enable(f->clk_en);
    if (ret)
        return ret;

    ret = clk_prepare_enable(f->clk);
    if (ret) {
        clk_disable_unprepare(f->clk_en);
        return ret;
    }

    return 0;
}

正如你所看到的,有一个对 clk_set_rate(f->clk,rate) 的调用,它实际上是有效的,因为如果我将时钟 spi-最大频率设置为 50Mhz,它会改变时钟。它也适用于其他值,但不允许设置低于 18Mhz 的频率。当然所有操作都以成功结束,没有错误等等。

所以我正在尝试解决这个问题,并找出如何设置为低于 18Mhz 的值,这实际上是我测试所需要的。

linux-device-driver embedded-linux device-tree
1个回答
0
投票

其实我已经知道如何解决这个问题了。 首先 - 在当前设置中 - 无法低于 18Mhz。 为什么? 我们看图:

如您所见,FlexSPI 与具有 7 个输入的 MUX1 连接。输入选择由 RCW 设置。

在当前设置中,HWA_CGA_M1_CLK_SEL 设置为 1。 医生对这个值有什么看法?

0b001 异步模式——集群 A 组 PLL 1 /1 是时钟

所以我们知道,通过多路复用器,我们选择 CGA_PLL1:1,它不会划分 PLL。 让我们继续。 SysClk 等于 100Mhz。根据代码CGA_PLL1_RAT设置为15。 100 * 15 -> 1500Mhz。

我们可以在 Linux 上查看 /sys/kernel/debug/clk/clk_summary 来确认:

 cg-pll1-div1                   0        0        0  1500000000          0     0  50000         Y

好吧,让我们继续吧。 FlexSPi本身具有分频器功能,以便我们可以配置其时钟。除法器的最大值是多少? 80.

简单的数学计算表明:1500 / 80 = 18,75Mhz,这正是 Linux 对我的 FlexSPI 时钟当前设置的描述。

所以我的解决方案是重新配置 RCW 以使用:Cluster Group A PLL 1 /4 是时钟

© www.soinside.com 2019 - 2024. All rights reserved.