From b90cd60f3f5975f25bbeff1a4ebf21d51a4e55ea Mon Sep 17 00:00:00 2001 From: deooi Date: Fri, 14 Mar 2025 14:46:08 +0800 Subject: [PATCH] pl35x-nand-controller: Enable dynamic clk rate setting NI Zynq based devices require different clk frequencies to be set at different timing modes. Enable the memclk rate to be set according to the specifications in the device tree. If the device tree does not have this memclk-timing-frequency property specified, the driver proceeds as usual. Signed-off-by: deooi --- arch/arm/boot/dts/xilinx/ni-zynq.dtsi | 10 +++++ drivers/mtd/nand/raw/pl35x-nand-controller.c | 41 ++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi index 086e17729433b..d3347dd83ff83 100644 --- a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi +++ b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi @@ -140,6 +140,16 @@ &smcc { status = "okay"; + /* + * NI Zynq based devices require different memclk frequency + * for different timing modes + */ + memclk-timing-frequency = <0 83333333>, + <1 166666666>, + <2 166666666>, + <3 166666666>, + <4 166666666>, + <5 166666666>; }; &nfc0 { diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 021750b8a2aed..cfe599adba4de 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -795,6 +795,28 @@ static int pl35x_nfc_exec_op(struct nand_chip *chip, op, check_only); } +static unsigned long of_get_memclk_freq(const struct device_node *np, const int timing_mode) +{ + int memclk_frequency, i, total_elements; + u32 memclk_mode; + + total_elements = of_property_count_u32_elems(np, "memclk-timing-frequency"); + if (total_elements <= 0) + return 0; + + for (i = 0; i < total_elements; i = i+2) { + of_property_read_u32_index(np, "memclk-timing-frequency", i, &memclk_mode); + if (memclk_mode == timing_mode) { + of_property_read_u32_index(np, "memclk-timing-frequency", + i+1, &memclk_frequency); + return memclk_frequency; + } + } + + pr_err("Failed to find matching memclk timing mode %d\n", timing_mode); + return 0; +} + static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs, const struct nand_interface_config *conf) { @@ -804,6 +826,8 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs, const struct nand_sdr_timings *sdr; unsigned int period_ns, val; struct clk *mclk; + unsigned long memclk_of_timing_freq; + int ret = 0; sdr = nand_get_sdr_timings(conf); if (IS_ERR(sdr)) @@ -815,6 +839,23 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs, return PTR_ERR(mclk); } + /* + * NI Zynq based devices require different rates for different timing modes + * so we need to set the rate according to the timing modes + */ + memclk_of_timing_freq = of_get_memclk_freq( + nfc->dev->parent->of_node, + conf->timings.mode); + + if (memclk_of_timing_freq) { + ret = clk_set_rate(mclk, memclk_of_timing_freq); + if (ret) { + pr_err("Failed to set memclk timing frequency to %lu\n", + memclk_of_timing_freq); + return ret; + } + } + /* * SDR timings are given in pico-seconds while NFC timings must be * expressed in NAND controller clock cycles. We use the TO_CYCLE()