From nobody Wed Oct 1 22:19:17 2025 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9D4D92C3761; Wed, 1 Oct 2025 11:26:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317977; cv=none; b=jQBg+JVzy6Px4NrL8QuhEYnl0+i3xhh5VN0YaRoqeNZrNNd1MVSNgW02Qwu2tqzQ+hB6vEoyHg0MS/2g3EDH9MwwNQIDcDnRBoL/vU/lextKfALGHJuZE0JF0P/UCxPkUXNAehpeUyPROavTqJ1IH2QGsWlJA5FTGRgCnoVeedk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317977; c=relaxed/simple; bh=8+Ifldp/lAzDztipYv0RQaC10Vs5kNnCVwdiPdjpfI8=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=inNetZ476rN/bm0tQ8cCPGBnt3u4SBqXWuLmW0yIM8ZeLQY2PIint7Z1o65hyWkJUnXGZO+NUeuDI6usj8+GxQzSpYgrh8QvEFJqrhfniNrRtUQXqmnoDtQPQ4Zm5jQdkZYUU39S5nWehLdTUidYRXaY5uKKKyA7ajGQrTuVu18= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 1 Oct 2025 19:26:05 +0800 Received: from aspeedtech.com (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 1 Oct 2025 19:26:05 +0800 From: Chin-Ting Kuo To: , , , , , , , , , , , , , Subject: [PATCH 1/6] spi: aspeed: Update clock selection strategy Date: Wed, 1 Oct 2025 19:26:00 +0800 Message-ID: <20251001112605.1130723-2-chin-ting_kuo@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251001112605.1130723-1-chin-ting_kuo@aspeedtech.com> References: <20251001112605.1130723-1-chin-ting_kuo@aspeedtech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch updates the SPI clock selection logic for cases where timing calibration is not performed or the results are failed. Timing calibration process is skipped in the two scenarios below. - Low-entropy data in the calibration region: The driver skips timing calibration if the data read from the SPI flash contains mostly 0x00 or 0xFF. Originally, the driver used a low-frequency clock to read this region as golden data. However, due to variations in host characteristics and image layout, we cannot assume sufficient entropy in this region to ensure reliable calibration. - Low-speed configurations (< 40MHz): The ASPEED SPI controller does not support timing calibration when the max_speed_hz of the SPI device is below 40MHz. In both cases, the SPI clock frequency specified in the device tree should be used directly. When timing calibration is skipped, it is the board vendor's responsibility to ensure that the SPI flash SI (Signal Integrity) is sufficient for reliable operation at the configured frequency. When timing calibration processes is execued and all potential clock frequencies are performed, but are all failed, the lower clock frequency should be adopted to ensure the overall system can boot up successfully. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 177 +++++++++++++++++++++++++++++++---- 1 file changed, 159 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 62a11142bd63..9c54c05da3cc 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -82,6 +82,7 @@ struct aspeed_spi_data { u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg); u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg); u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end); + u32 (*get_clk_div)(struct aspeed_spi_chip *chip, u32 hz); int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv, const u8 *golden_buf, u8 *test_buf); }; @@ -942,26 +943,149 @@ static bool aspeed_spi_check_calib_data(const u8 *te= st_buf, u32 size) } =20 static const u32 aspeed_spi_hclk_divs[] =3D { - 0xf, /* HCLK */ - 0x7, /* HCLK/2 */ - 0xe, /* HCLK/3 */ - 0x6, /* HCLK/4 */ - 0xd, /* HCLK/5 */ + /* HCLK, HCLK/2, HCLK/3, HCLK/4, HCLK/5, ..., HCLK/16 */ + 0xf, 0x7, 0xe, 0x6, 0xd, + 0x5, 0xc, 0x4, 0xb, 0x3, + 0xa, 0x2, 0x9, 0x1, 0x8, + 0x0 }; =20 #define ASPEED_SPI_HCLK_DIV(i) \ (aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT) =20 +/* Transfer maximum clock frequency to register setting */ +static u32 aspeed_get_clk_div_ast2400(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev =3D chip->aspi->dev; + u32 hclk_clk =3D chip->aspi->clk_freq; + u32 div_ctl =3D 0; + u32 i; + bool found =3D false; + + /* FMC/SPIR10[11:8] */ + for (i =3D 1; i <=3D ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / i <=3D max_hz) { + found =3D true; + break; + } + } + + if (found) { + div_ctl =3D ASPEED_SPI_HCLK_DIV(i); + chip->clk_freq =3D hclk_clk / i; + } + + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + +static u32 aspeed_get_clk_div_ast2500(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev =3D chip->aspi->dev; + u32 hclk_clk =3D chip->aspi->clk_freq; + u32 div_ctl =3D 0; + u32 i; + bool found =3D false; + + /* FMC/SPIR10[11:8] */ + for (i =3D 1; i <=3D ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / i <=3D max_hz) { + found =3D true; + chip->clk_freq =3D hclk_clk / i; + break; + } + } + + if (found) { + div_ctl =3D ASPEED_SPI_HCLK_DIV(i); + goto end; + } + + for (i =3D 1; i <=3D ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / (i * 4) <=3D max_hz) { + found =3D true; + chip->clk_freq =3D hclk_clk / (i * 4); + break; + } + } + + if (found) + div_ctl =3D BIT(13) | ASPEED_SPI_HCLK_DIV(i); + +end: + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + +static u32 aspeed_get_clk_div_ast2600(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev =3D chip->aspi->dev; + u32 hclk_clk =3D chip->aspi->clk_freq; + u32 div_ctl =3D 0; + u32 i, j; + bool found =3D false; + + /* FMC/SPIR10[27:24] */ + for (j =3D 0; j < 16; j++) { + /* FMC/SPIR10[11:8] */ + for (i =3D 1; i <=3D ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (j =3D=3D 0 && i =3D=3D 1) + continue; + + if (hclk_clk / (j * 16 + i) <=3D max_hz) { + found =3D true; + break; + } + } + + if (found) { + div_ctl =3D ((j << 24) | ASPEED_SPI_HCLK_DIV(i)); + chip->clk_freq =3D hclk_clk / (j * 16 + i); + break; + } + } + + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) { struct aspeed_spi *aspi =3D chip->aspi; const struct aspeed_spi_data *data =3D aspi->data; u32 ahb_freq =3D aspi->clk_freq; u32 max_freq =3D chip->clk_freq; + bool exec_calib =3D false; + u32 best_freq =3D 0; u32 ctl_val; u8 *golden_buf =3D NULL; u8 *test_buf =3D NULL; - int i, rc, best_div =3D -1; + int i, rc; + u32 div_ctl; =20 dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz", ahb_freq / 1000000); @@ -982,7 +1106,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi= _chip *chip) memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) { dev_info(aspi->dev, "Calibration area too uniform, using low speed"); - goto no_calib; + goto end_calib; } =20 #if defined(VERBOSE_DEBUG) @@ -991,7 +1115,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi= _chip *chip) #endif =20 /* Now we iterate the HCLK dividers until we find our breaking point */ - for (i =3D ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--)= { + for (i =3D 5; i > data->hdiv_max - 1; i--) { u32 tv, freq; =20 freq =3D ahb_freq / i; @@ -1004,22 +1128,33 @@ static int aspeed_spi_do_calibration(struct aspeed_= spi_chip *chip) dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv); rc =3D data->calibrate(chip, i, golden_buf, test_buf); if (rc =3D=3D 0) - best_div =3D i; + best_freq =3D freq; + + exec_calib =3D true; } =20 - /* Nothing found ? */ - if (best_div < 0) { - dev_warn(aspi->dev, "No good frequency, using dumb slow"); +end_calib: + if (!exec_calib) { + /* calibration process is not executed */ + dev_warn(aspi->dev, "Force to dts configuration %dkHz.\n", + max_freq / 1000); + div_ctl =3D data->get_clk_div(chip, max_freq); + } else if (best_freq =3D=3D 0) { + /* calibration process is executed, but no good frequency */ + dev_warn(aspi->dev, "No good frequency, using dumb slow\n"); + div_ctl =3D 0; } else { - dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div); + dev_dbg(aspi->dev, "Found good read timings at %dMHz.\n", + best_freq / 1000000); + div_ctl =3D data->get_clk_div(chip, best_freq); + } =20 - /* Record the freq */ - for (i =3D 0; i < ASPEED_SPI_MAX; i++) - chip->ctl_val[i] =3D (chip->ctl_val[i] & data->hclk_mask) | - ASPEED_SPI_HCLK_DIV(best_div); + /* Record the freq */ + for (i =3D 0; i < ASPEED_SPI_MAX; i++) { + chip->ctl_val[i] =3D (chip->ctl_val[i] & data->hclk_mask) | + div_ctl; } =20 -no_calib: writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); kfree(test_buf); return 0; @@ -1096,6 +1231,7 @@ static const struct aspeed_spi_data ast2400_fmc_data = =3D { .hclk_mask =3D 0xfffff0ff, .hdiv_max =3D 1, .calibrate =3D aspeed_spi_calibrate, + .get_clk_div =3D aspeed_get_clk_div_ast2400, .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, .segment_reg =3D aspeed_spi_segment_reg, @@ -1109,6 +1245,7 @@ static const struct aspeed_spi_data ast2400_spi_data = =3D { .timing =3D 0x14, .hclk_mask =3D 0xfffff0ff, .hdiv_max =3D 1, + .get_clk_div =3D aspeed_get_clk_div_ast2400, .calibrate =3D aspeed_spi_calibrate, /* No segment registers */ }; @@ -1121,6 +1258,7 @@ static const struct aspeed_spi_data ast2500_fmc_data = =3D { .timing =3D CE0_TIMING_COMPENSATION_REG, .hclk_mask =3D 0xffffd0ff, .hdiv_max =3D 1, + .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, @@ -1135,6 +1273,7 @@ static const struct aspeed_spi_data ast2500_spi_data = =3D { .timing =3D CE0_TIMING_COMPENSATION_REG, .hclk_mask =3D 0xffffd0ff, .hdiv_max =3D 1, + .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, @@ -1150,6 +1289,7 @@ static const struct aspeed_spi_data ast2600_fmc_data = =3D { .timing =3D CE0_TIMING_COMPENSATION_REG, .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, + .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, .segment_end =3D aspeed_spi_segment_ast2600_end, @@ -1165,6 +1305,7 @@ static const struct aspeed_spi_data ast2600_spi_data = =3D { .timing =3D CE0_TIMING_COMPENSATION_REG, .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, + .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, .segment_end =3D aspeed_spi_segment_ast2600_end, --=20 2.34.1