From nobody Wed Oct 1 21:24:54 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 From nobody Wed Oct 1 21:24:54 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 020EB2D12EF; Wed, 1 Oct 2025 11:26:17 +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=1759317979; cv=none; b=tcfk7GZEO6tAH99UA7AGDtN8GR6izQhehHeB8aj/XxU7RuCL2nSPy8sn/kRr9XNxgD0bdnDwdfpZ9kTR9fzNDMMrEjVkxPQhA+ZNnPG9y1iHr5tYl4zltz0B4TjD1n5u6Ppvkbg4ZG6sKhS/ucF0AYSQk6/45XjOUqNamhxpkjk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317979; c=relaxed/simple; bh=+JQlGOi82GStO5BpCxC0o9ag/xFIx8Vvi+EBjRTSKrM=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=NjrXY3cciWCty0ZZMD9GIrjHfaQnv9hFE/zbzv+ktLoq9QJxDxz3lDC+gKHMXpm9gIY4q+Y0VpqjoRdtkOldRg6hxSqI30S2S0LbYQnKWYfwyL8VEa4KIJvEYwfFlarIn/FCdNcJFi0Mku19IRzNMZVmJF4lv/RcArG7CTkwyoA= 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 2/6] spi: aspeed: Improve timing calibration algorithm for AST2600 platform Date: Wed, 1 Oct 2025 19:26:01 +0800 Message-ID: <20251001112605.1130723-3-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" Starting with the AST2600 platform, most platfom manufacturers have adopted more complex board designs and signal routing, making SPI timing calibration increasingly sensitive and critical. Previously, the driver selected the first "PASS" timing point during calibration, which may not yield the most stable result. This patch introduces a more robust calibration method: - It evaluates all combinations of HCLK sample point delay and DI input delay. The results are stored in a 2D buffer for further comparison. - Because the timing delay behavior is non-linear across HCLK sample points, the optimal timing point is selected as the center of the longest consecutive "PASS" interval within a single HCLK sample point row. This approach ensures better stability and precision in SPI read timing, especially under complex signal integrity conditions. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 70 +++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 9c54c05da3cc..d2d9e13e9bda 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -1162,21 +1162,57 @@ static int aspeed_spi_do_calibration(struct aspeed_= spi_chip *chip) =20 #define TIMING_DELAY_DI BIT(3) #define TIMING_DELAY_HCYCLE_MAX 5 +#define TIMING_DELAY_INPUT_MAX 16 #define TIMING_REG_AST2600(chip) \ ((chip)->aspi->regs + (chip)->aspi->data->timing + \ (chip)->cs * 4) =20 +/* + * This function returns the center point of the longest + * continuous "pass" interval within the buffer. The interval + * must contains the highest number of consecutive "pass" + * results and not span across multiple rows. + */ +static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, + u8 buf[rows][cols]) +{ + int r =3D 0, c =3D 0; + int max =3D 0; + int i, j; + + for (i =3D 0; i < rows; i++) { + for (j =3D 0; j < cols;) { + int k =3D j; + + while (k < cols && buf[i][k]) + k++; + + if (k - j > max) { + max =3D k - j; + r =3D i; + c =3D j + (k - j) / 2; + } + + j =3D k + 1; + } + } + + return max > 4 ? r * cols + c : 0; +} + static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 = hdiv, const u8 *golden_buf, u8 *test_buf) { struct aspeed_spi *aspi =3D chip->aspi; int hcycle; + int delay_ns; u32 shift =3D (hdiv - 2) << 3; - u32 mask =3D ~(0xfu << shift); + u32 mask =3D ~(0xffu << shift); u32 fread_timing_val =3D 0; + u8 calib_res[6][17] =3D {0}; + u32 calib_point; =20 for (hcycle =3D 0; hcycle <=3D TIMING_DELAY_HCYCLE_MAX; hcycle++) { - int delay_ns; bool pass =3D false; =20 fread_timing_val &=3D mask; @@ -1189,14 +1225,14 @@ static int aspeed_spi_ast2600_calibrate(struct aspe= ed_spi_chip *chip, u32 hdiv, " * [%08x] %d HCLK delay, DI delay none : %s", fread_timing_val, hcycle, pass ? "PASS" : "FAIL"); if (pass) - return 0; + calib_res[hcycle][0] =3D 1; =20 /* Add DI input delays */ fread_timing_val &=3D mask; fread_timing_val |=3D (TIMING_DELAY_DI | hcycle) << shift; =20 - for (delay_ns =3D 0; delay_ns < 0x10; delay_ns++) { - fread_timing_val &=3D ~(0xf << (4 + shift)); + for (delay_ns =3D 0; delay_ns < TIMING_DELAY_INPUT_MAX; delay_ns++) { + fread_timing_val &=3D ~(0xfu << (4 + shift)); fread_timing_val |=3D delay_ns << (4 + shift); =20 writel(fread_timing_val, TIMING_REG_AST2600(chip)); @@ -1205,18 +1241,28 @@ static int aspeed_spi_ast2600_calibrate(struct aspe= ed_spi_chip *chip, u32 hdiv, " * [%08x] %d HCLK delay, DI delay %d.%dns : %s", fread_timing_val, hcycle, (delay_ns + 1) / 2, (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL"); - /* - * TODO: This is optimistic. We should look - * for a working interval and save the middle - * value in the read timing register. - */ + if (pass) - return 0; + calib_res[hcycle][delay_ns + 1] =3D 1; } } =20 + calib_point =3D aspeed_spi_ast2600_optimized_timing(6, 17, calib_res); /* No good setting for this frequency */ - return -1; + if (calib_point =3D=3D 0) + return -1; + + hcycle =3D calib_point / 17; + delay_ns =3D calib_point % 17; + + fread_timing_val =3D (TIMING_DELAY_DI | hcycle | (delay_ns << 4)) << shif= t; + + dev_dbg(aspi->dev, "timing val: %08x, final hcycle: %d, delay_ns: %d\n", + fread_timing_val, hcycle, delay_ns); + + writel(fread_timing_val, TIMING_REG_AST2600(chip)); + + return 0; } =20 /* --=20 2.34.1 From nobody Wed Oct 1 21:24:54 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 079692D94B8; Wed, 1 Oct 2025 11:26:20 +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=1759317981; cv=none; b=VTgCp+2DTEsVVN8ELREp+/TgHfbQgIzEF+cSxye0IUsQncdytwuSKcqxwYognoSyWyz+1WYaPZKq0Yvl1+XJqg8cI4P6N3NeXgDV/22ouKXLHa1SMD2DCdzAdam/lBh+aOSZsr+aAYrQhYC4jn5WIwJ9HCz/xAx2q/SGYu924FY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317981; c=relaxed/simple; bh=kWwBSjHFdmeMoQzQntQiDIMtXSZuuRdxJQo9GY7+DNE=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TIEnvbmYX5ySv+nEENfDnU9JLwuczoT3lT1QdNSsd2K6Ufk609az9hPmjGS7QjTJBhLZgU4EB2OIRknRNijIH8xyQnUq2vGd/PusBE4N2unce7zzP3yMQVD6+Xjx6kB6x2OCO/tYfwfgPfR6YeMssJ8JXxJTcCBcRBMr9KS6/p8= 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 3/6] spi: aspeed: Force default address decoding range assignment for each CS Date: Wed, 1 Oct 2025 19:26:02 +0800 Message-ID: <20251001112605.1130723-4-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" On some platforms, we cannot assume that the whole address decoding range value is ready for each CS. Especially for chip selects other than CS0, the address decoding range may not be properly configured before the kernel stage, or the existing configuration may be unsuitable. This can lead to SPI flash detection failures during driver probe. To ensure reliable initialization, this patch forcibly assigns a default address decoding range to each chip select based on a platform-specific minimum window size. Unused chip selects are explicitly disabled to avoid conflicts. This change improves robustness across platforms with varying bootloader behavior and ensures consistent SPI flash initialization. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 106 ++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 39 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index d2d9e13e9bda..29fed8477958 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -78,6 +78,7 @@ struct aspeed_spi_data { u32 timing; u32 hclk_mask; u32 hdiv_max; + u32 min_window_size; =20 u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg); u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg); @@ -96,6 +97,7 @@ struct aspeed_spi { void __iomem *ahb_base; u32 ahb_base_phy; u32 ahb_window_size; + u32 num_cs; struct device *dev; =20 struct clk *clk; @@ -401,35 +403,6 @@ static void aspeed_spi_get_windows(struct aspeed_spi *= aspi, } } =20 -/* - * On the AST2600, some CE windows are closed by default at reset but - * U-Boot should open all. - */ -static int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip) -{ - struct aspeed_spi *aspi =3D chip->aspi; - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] =3D { 0 }; - struct aspeed_spi_window *win =3D &windows[chip->cs]; - - /* No segment registers for the AST2400 SPI controller */ - if (aspi->data =3D=3D &ast2400_spi_data) { - win->offset =3D 0; - win->size =3D aspi->ahb_window_size; - } else { - aspeed_spi_get_windows(aspi, windows); - } - - chip->ahb_base =3D aspi->ahb_base + win->offset; - chip->ahb_window_size =3D win->size; - - dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ] %dMB", - chip->cs, aspi->ahb_base_phy + win->offset, - aspi->ahb_base_phy + win->offset + win->size - 1, - win->size >> 20); - - return chip->ahb_window_size ? 0 : -1; -} - static int aspeed_spi_set_window(struct aspeed_spi *aspi, const struct aspeed_spi_window *win) { @@ -464,16 +437,63 @@ static int aspeed_spi_set_window(struct aspeed_spi *a= spi, return 0; } =20 +static const struct aspeed_spi_data ast2500_spi_data; +static const struct aspeed_spi_data ast2600_spi_data; +static const struct aspeed_spi_data ast2600_fmc_data; + +static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi) +{ + int ret; + u32 cs; + struct aspeed_spi_window win; + + /* No segment registers for the AST2400 SPI controller */ + if (aspi->data =3D=3D &ast2400_spi_data) { + aspi->chips[0].ahb_base =3D aspi->ahb_base; + aspi->chips[0].ahb_window_size =3D aspi->ahb_window_size; + return 0; + } + + /* Assign the minimum window size to each CS */ + for (cs =3D 0; cs < aspi->num_cs; cs++) { + if (cs =3D=3D 0) + aspi->chips[cs].ahb_base =3D aspi->ahb_base; + else + aspi->chips[cs].ahb_base =3D + aspi->chips[cs - 1].ahb_base + + aspi->chips[cs - 1].ahb_window_size; + + aspi->chips[cs].ahb_window_size =3D aspi->data->min_window_size; + + dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ]", + cs, aspi->ahb_base_phy + aspi->data->min_window_size * cs, + aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1); + } + + /* Close unused CS */ + for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) { + aspi->chips[cs].ahb_base =3D aspi->ahb_base; + aspi->chips[cs].ahb_window_size =3D 0; + } + + for (cs =3D 0; cs < aspi->num_cs; cs++) { + win.cs =3D cs; + win.offset =3D aspi->chips[cs].ahb_base - aspi->ahb_base; + win.size =3D aspi->chips[cs].ahb_window_size; + ret =3D aspeed_spi_set_window(aspi, &win); + if (ret) + return ret; + } + + return 0; +} + /* * Yet to be done when possible : * - Align mappings on flash size (we don't have the info) * - ioremap each window, not strictly necessary since the overall window * is correct. */ -static const struct aspeed_spi_data ast2500_spi_data; -static const struct aspeed_spi_data ast2600_spi_data; -static const struct aspeed_spi_data ast2600_fmc_data; - static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip, u32 local_offset, u32 size) { @@ -678,11 +698,6 @@ static int aspeed_spi_setup(struct spi_device *spi) if (data->hastype) aspeed_spi_chip_set_type(aspi, cs, CONFIG_TYPE_SPI); =20 - if (aspeed_spi_chip_set_default_window(chip) < 0) { - dev_warn(aspi->dev, "CE%d window invalid", cs); - return -EINVAL; - } - aspeed_spi_chip_enable(aspi, cs, true); =20 chip->ctl_val[ASPEED_SPI_BASE] =3D CTRL_CE_STOP_ACTIVE | CTRL_IO_MODE_USE= R; @@ -763,9 +778,17 @@ static int aspeed_spi_probe(struct platform_device *pd= ev) ctlr->mem_ops =3D &aspeed_spi_mem_ops; ctlr->setup =3D aspeed_spi_setup; ctlr->cleanup =3D aspeed_spi_cleanup; - ctlr->num_chipselect =3D data->max_cs; + ctlr->num_chipselect =3D of_get_available_child_count(dev->of_node); ctlr->dev.of_node =3D dev->of_node; =20 + aspi->num_cs =3D ctlr->num_chipselect; + + ret =3D aspeed_spi_chip_set_default_window(aspi); + if (ret) { + dev_err(&pdev->dev, "fail to set default window\n"); + return ret; + } + ret =3D devm_spi_register_controller(dev, ctlr); if (ret) dev_err(&pdev->dev, "spi_register_controller failed\n"); @@ -1276,6 +1299,7 @@ static const struct aspeed_spi_data ast2400_fmc_data = =3D { .timing =3D CE0_TIMING_COMPENSATION_REG, .hclk_mask =3D 0xfffff0ff, .hdiv_max =3D 1, + .min_window_size =3D 0x800000, .calibrate =3D aspeed_spi_calibrate, .get_clk_div =3D aspeed_get_clk_div_ast2400, .segment_start =3D aspeed_spi_segment_start, @@ -1304,6 +1328,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, + .min_window_size =3D 0x800000, .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, @@ -1319,6 +1344,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, + .min_window_size =3D 0x800000, .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, @@ -1335,6 +1361,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, + .min_window_size =3D 0x200000, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, @@ -1351,6 +1378,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, + .min_window_size =3D 0x200000, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, --=20 2.34.1 From nobody Wed Oct 1 21:24:54 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 2F1B72DAFD7; Wed, 1 Oct 2025 11:26:22 +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=1759317984; cv=none; b=gBQrgiwksCqlljzy4gKS7+mNc10sg+FqqPzIqt/+JXM5pJuXxZ4NnJzQeC4zygiKyUbT3I8vXHyHeEAkFbmjqvqUmizZcUaN5nA8CS7xNK9rZI6oaqLWT/nzTcAratQfZk/hGc2BASn+V+HRuNpCAYi1xY22mbyueHe6k25S/UY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317984; c=relaxed/simple; bh=JtpMEjZllLzSzcx74PmQXUWPMzeACcb8nLbX10ryd80=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qRCFfPtkWpcB24QyRpBLFB30LhsjFP6mXNDlsVZzSBnUdx2/RfAM9nlwmzXCCOKearLlW/tSEDKclbFP1gELgBZOCTDsovw7OmmA7H5vOptyDnZIfkAL2jSAPKThM3DiSJH1jfZ+Si4bt7bx2Q3I2P65e0xV5AFUnMoP9dJ8fY8= 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 4/6] spi: aspeed: Centralize address decoding region management Date: Wed, 1 Oct 2025 19:26:03 +0800 Message-ID: <20251001112605.1130723-5-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" The original approach to handling address decoding overlaps was to trim the next region directly. If the next CS's decoding range was fully overlapped by the current one, it would be forcibly closed by trimming its size to zero. This could lead expected behavior, especially on the platform with multiple flashes layout. To solve improper trimming problem, this patch collects the required address decoding size at each stage, then, (re-)arragne address decoding region to each CS centrally with knowing the total AHB decoding size. If a segment register cannot be updated (e.g. due to bootloader write protection), the original value is kept to avoid breaking access and an error is reported if the total decoding size of all CS exceeds the total AHB decoding size. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 156 +++++++++++++---------------------- 1 file changed, 58 insertions(+), 98 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 29fed8477958..83a47ac0711e 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -379,61 +379,59 @@ static const char *aspeed_spi_get_name(struct spi_mem= *mem) spi_get_chipselect(mem->spi, 0)); } =20 -struct aspeed_spi_window { - u32 cs; - u32 offset; - u32 size; -}; - -static void aspeed_spi_get_windows(struct aspeed_spi *aspi, - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS]) +static int aspeed_spi_set_window(struct aspeed_spi *aspi) { - const struct aspeed_spi_data *data =3D aspi->data; - u32 reg_val; + struct device *dev =3D aspi->dev; + off_t offset =3D 0; + phys_addr_t start; + phys_addr_t end; + void __iomem *seg_reg_base =3D aspi->regs + CE0_SEGMENT_ADDR_REG; + void __iomem *seg_reg; + u32 seg_val_backup; + u32 seg_val; u32 cs; + size_t window_size; =20 for (cs =3D 0; cs < aspi->data->max_cs; cs++) { - reg_val =3D readl(aspi->regs + CE0_SEGMENT_ADDR_REG + cs * 4); - windows[cs].cs =3D cs; - windows[cs].size =3D data->segment_end(aspi, reg_val) - - data->segment_start(aspi, reg_val); - windows[cs].offset =3D data->segment_start(aspi, reg_val) - aspi->ahb_ba= se_phy; - dev_vdbg(aspi->dev, "CE%d offset=3D0x%.8x size=3D0x%x\n", cs, - windows[cs].offset, windows[cs].size); - } -} + seg_reg =3D seg_reg_base + cs * 4; + seg_val_backup =3D readl(seg_reg); =20 -static int aspeed_spi_set_window(struct aspeed_spi *aspi, - const struct aspeed_spi_window *win) -{ - u32 start =3D aspi->ahb_base_phy + win->offset; - u32 end =3D start + win->size; - void __iomem *seg_reg =3D aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4; - u32 seg_val_backup =3D readl(seg_reg); - u32 seg_val =3D aspi->data->segment_reg(aspi, start, end); + start =3D aspi->ahb_base_phy + offset; + window_size =3D aspi->chips[cs].ahb_window_size; + end =3D start + window_size; =20 - if (seg_val =3D=3D seg_val_backup) - return 0; + seg_val =3D aspi->data->segment_reg(aspi, start, end); + writel(seg_val, seg_reg); =20 - writel(seg_val, seg_reg); + /* + * Restore initial value if something goes wrong or the segment + * register is written protected. + */ + if (seg_val !=3D readl(seg_reg)) { + dev_warn(dev, "CE%d expected window [ 0x%.9llx - 0x%.9llx ] %zdMB\n", + cs, (u64)start, (u64)end - 1, window_size >> 20); + writel(seg_val_backup, seg_reg); + window_size =3D aspi->data->segment_end(aspi, seg_val_backup) - + aspi->data->segment_start(aspi, seg_val_backup); + aspi->chips[cs].ahb_window_size =3D window_size; + end =3D start + window_size; + } =20 - /* - * Restore initial value if something goes wrong else we could - * loose access to the chip. - */ - if (seg_val !=3D readl(seg_reg)) { - dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB", - win->cs, start, end - 1, win->size >> 20); - writel(seg_val_backup, seg_reg); - return -EIO; + if (window_size !=3D 0) + dev_dbg(dev, "CE%d window [ 0x%.9llx - 0x%.9llx ] %zdMB\n", + cs, (u64)start, (u64)end - 1, window_size >> 20); + else + dev_dbg(dev, "CE%d window closed\n", cs); + + aspi->chips[cs].ahb_base =3D aspi->ahb_base + offset; + offset +=3D window_size; + if (offset > aspi->ahb_window_size) { + dev_err(dev, "CE%d offset value 0x%llx is too large.\n", + cs, (u64)offset); + return -ENOSPC; + } } =20 - if (win->size) - dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB", - win->cs, start, end - 1, win->size >> 20); - else - dev_dbg(aspi->dev, "CE%d window closed", win->cs); - return 0; } =20 @@ -443,9 +441,7 @@ static const struct aspeed_spi_data ast2600_fmc_data; =20 static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi) { - int ret; u32 cs; - struct aspeed_spi_window win; =20 /* No segment registers for the AST2400 SPI controller */ if (aspi->data =3D=3D &ast2400_spi_data) { @@ -456,36 +452,17 @@ static int aspeed_spi_chip_set_default_window(struct = aspeed_spi *aspi) =20 /* Assign the minimum window size to each CS */ for (cs =3D 0; cs < aspi->num_cs; cs++) { - if (cs =3D=3D 0) - aspi->chips[cs].ahb_base =3D aspi->ahb_base; - else - aspi->chips[cs].ahb_base =3D - aspi->chips[cs - 1].ahb_base + - aspi->chips[cs - 1].ahb_window_size; - aspi->chips[cs].ahb_window_size =3D aspi->data->min_window_size; - dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ]", cs, aspi->ahb_base_phy + aspi->data->min_window_size * cs, aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1); } =20 /* Close unused CS */ - for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) { - aspi->chips[cs].ahb_base =3D aspi->ahb_base; + for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) aspi->chips[cs].ahb_window_size =3D 0; - } - - for (cs =3D 0; cs < aspi->num_cs; cs++) { - win.cs =3D cs; - win.offset =3D aspi->chips[cs].ahb_base - aspi->ahb_base; - win.size =3D aspi->chips[cs].ahb_window_size; - ret =3D aspeed_spi_set_window(aspi, &win); - if (ret) - return ret; - } =20 - return 0; + return aspeed_spi_set_window(aspi); } =20 /* @@ -498,8 +475,8 @@ static int aspeed_spi_chip_adjust_window(struct aspeed_= spi_chip *chip, u32 local_offset, u32 size) { struct aspeed_spi *aspi =3D chip->aspi; - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] =3D { 0 }; - struct aspeed_spi_window *win =3D &windows[chip->cs]; + u32 cs; + u32 total_window_size; int ret; =20 /* No segment registers for the AST2400 SPI controller */ @@ -527,41 +504,24 @@ static int aspeed_spi_chip_adjust_window(struct aspee= d_spi_chip *chip, chip->cs, size >> 20); } =20 - aspeed_spi_get_windows(aspi, windows); - /* Adjust this chip window */ - win->offset +=3D local_offset; - win->size =3D size; + aspi->chips[chip->cs].ahb_window_size =3D size; =20 - if (win->offset + win->size > aspi->ahb_window_size) { - win->size =3D aspi->ahb_window_size - win->offset; - dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >= > 20); + total_window_size =3D 0; + for (cs =3D 0; cs < aspi->data->max_cs; cs++) + total_window_size +=3D aspi->chips[cs].ahb_window_size; + + if (total_window_size > aspi->ahb_window_size) { + aspi->chips[chip->cs].ahb_window_size -=3D (total_window_size - + aspi->ahb_window_size); + dev_warn(aspi->dev, "CE%d window resized to %zdMB", + chip->cs, aspi->chips[chip->cs].ahb_window_size >> 20); } =20 - ret =3D aspeed_spi_set_window(aspi, win); + ret =3D aspeed_spi_set_window(aspi); if (ret) return ret; =20 - /* Update chip mapping info */ - chip->ahb_base =3D aspi->ahb_base + win->offset; - chip->ahb_window_size =3D win->size; - - /* - * Also adjust next chip window to make sure that it does not - * overlap with the current window. - */ - if (chip->cs < aspi->data->max_cs - 1) { - struct aspeed_spi_window *next =3D &windows[chip->cs + 1]; - - /* Change offset and size to keep the same end address */ - if ((next->offset + next->size) > (win->offset + win->size)) - next->size =3D (next->offset + next->size) - (win->offset + win->size); - else - next->size =3D 0; - next->offset =3D win->offset + win->size; - - aspeed_spi_set_window(aspi, next); - } return 0; } =20 --=20 2.34.1 From nobody Wed Oct 1 21:24:54 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 42C562DC340; Wed, 1 Oct 2025 11:26:24 +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=1759317986; cv=none; b=dsRa2VFZ7GvkCM5aowOuHLUZoY0KtT1P/z1BbaZMwZ5bXPo4FE+vza6AC0DXLlX9uCK1ShclNU7dV9RdsRv6Ez6OM1PcfIS8biLvIYdPTyEuausfXIEhRYMm8NFtCxi9zOGFKwLGbYFH8FJQwiygVLPH+RS05w2ZxlELmiaHIpw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317986; c=relaxed/simple; bh=et7w4n2HS3uGSJML310hMjttohvcDYIqkbFpfsT1h8Y=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=tb1RQoiDLdOSOzwC2E52nROyQBuS1xOi+yX9Ng0Zj5o6Nxp6jigLgpT26r5JDxZQ0TJi4Bl1lRQW7UGc4U9xvjNvo3Rk+khHc9ddySOy2q4mVgAt/MrFPjLr9UhTPi0SdHzYRFTpkjwh3xGAkUt6vH6awszdqHhSHBguPTvPs8w= 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 5/6] spi: aspeed: Add per-platform adjust_window callback for decoding range Date: Wed, 1 Oct 2025 19:26:04 +0800 Message-ID: <20251001112605.1130723-6-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" Different ASPEED SoCs have specific limitations on SPI address decoding, such as total range size, minimum window size per CS, and alignment requirements. The original adjustment logic only handles simple cases and could fail in more complex setups found in advanced board designs, e.g., small flash on CS0 and large flash on CS1, or when the total physical flash size exceeds the decoding range supported by the SPI controller. This patch introduces a per-platform adjust_window callback to handle these constraints properly. Each platform defines its own logic to adjust decoding ranges, trim excess size, and ensure alignment. If trimming is required, the affected CS will fall back to user mode access to ensure the entire flash remains accessible from the MTD layer. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 202 +++++++++++++++++++++++++++++------ 1 file changed, 168 insertions(+), 34 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 83a47ac0711e..4f6ae48dd904 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -67,6 +67,7 @@ struct aspeed_spi_chip { u32 ahb_window_size; u32 ctl_val[ASPEED_SPI_MAX]; u32 clk_freq; + bool force_user_mode; }; =20 struct aspeed_spi_data { @@ -83,6 +84,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); + int (*adjust_window)(struct aspeed_spi *aspi); 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); @@ -462,9 +464,166 @@ static int aspeed_spi_chip_set_default_window(struct = aspeed_spi *aspi) for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) aspi->chips[cs].ahb_window_size =3D 0; =20 + if (aspi->data->adjust_window) + aspi->data->adjust_window(aspi); + return aspeed_spi_set_window(aspi); } =20 +/* + * As the flash size grows up, we need to trim some decoding + * size if needed for the sake of conforming the maximum + * decoding size. We trim the decoding size from the rear CS + * to avoid affecting the default boot up sequence, usually, + * from CS0. Notice, if a CS decoding size is trimmed, + * command mode may not work perfectly on that CS, but it only + * affect performance and the debug function. + */ +static int aspeed_spi_trim_window_size(struct aspeed_spi *aspi) +{ + struct aspeed_spi_chip *chips =3D aspi->chips; + size_t total_sz; + int cs =3D aspi->data->max_cs - 1; + u32 i; + bool trimmed =3D false; + + do { + total_sz =3D 0; + for (i =3D 0; i < aspi->data->max_cs; i++) + total_sz +=3D chips[i].ahb_window_size; + + if (cs < 0) + return -ENOMEM; + + if (chips[cs].ahb_window_size <=3D aspi->data->min_window_size) { + cs--; + continue; + } + + if (total_sz > aspi->ahb_window_size) { + chips[cs].ahb_window_size -=3D + aspi->data->min_window_size; + total_sz -=3D aspi->data->min_window_size; + /* + * If the ahb window size is ever trimmed, only user + * mode can be adopted to access the whole flash. + */ + chips[cs].force_user_mode =3D true; + trimmed =3D true; + } + } while (total_sz > aspi->ahb_window_size); + + if (trimmed) { + dev_warn(aspi->dev, "Window size after triming:\n"); + for (cs =3D 0; cs < aspi->data->max_cs; cs++) { + dev_warn(aspi->dev, "CE%d: 0x%08x\n", + cs, chips[cs].ahb_window_size); + } + } + + return 0; +} + +static int aspeed_adjust_window_ast2400(struct aspeed_spi *aspi) +{ + int ret; + int cs; + struct aspeed_spi_chip *chips =3D aspi->chips; + + /* Close unused CS. */ + for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) + chips[cs].ahb_window_size =3D 0; + + ret =3D aspeed_spi_trim_window_size(aspi); + if (ret !=3D 0) + return ret; + + return 0; +} + +/* + * For AST2500, the minimum address decoding size for each CS + * is 8MB. This address decoding size is mandatory for each + * CS no matter whether it will be used. This is a HW limitation. + */ +static int aspeed_adjust_window_ast2500(struct aspeed_spi *aspi) +{ + int ret; + int cs, i; + u32 cum_size, rem_size; + struct aspeed_spi_chip *chips =3D aspi->chips; + + /* Assign min_window_sz to unused CS. */ + for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) { + if (chips[cs].ahb_window_size < aspi->data->min_window_size) + chips[cs].ahb_window_size =3D + aspi->data->min_window_size; + } + + /* + * If command mode or normal mode is used by dirmap read, the start + * address of a window should be multiple of its related flash size. + * Namely, the total windows size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). + */ + for (cs =3D aspi->num_cs - 1; cs >=3D 0; cs--) { + cum_size =3D 0; + for (i =3D 0; i < cs; i++) + cum_size +=3D chips[i].ahb_window_size; + + rem_size =3D cum_size % chips[cs].ahb_window_size; + if (chips[cs].ahb_window_size !=3D 0 && rem_size !=3D 0) + chips[0].ahb_window_size +=3D + chips[cs].ahb_window_size - rem_size; + } + + ret =3D aspeed_spi_trim_window_size(aspi); + if (ret !=3D 0) + return ret; + + /* The total window size of AST2500 SPI1 CS0 and CS1 must be 128MB */ + if (aspi->data =3D=3D &ast2500_spi_data) + chips[1].ahb_window_size =3D + 0x08000000 - chips[0].ahb_window_size; + + return 0; +} + +static int aspeed_adjust_window_ast2600(struct aspeed_spi *aspi) +{ + int ret; + int cs, i; + u32 cum_size, rem_size; + struct aspeed_spi_chip *chips =3D aspi->chips; + + /* Close unused CS. */ + for (cs =3D aspi->num_cs; cs < aspi->data->max_cs; cs++) + chips[cs].ahb_window_size =3D 0; + + /* + * If command mode or normal mode is used by dirmap read, the start + * address of a window should be multiple of its related flash size. + * Namely, the total windows size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). + */ + for (cs =3D aspi->num_cs - 1; cs >=3D 0; cs--) { + cum_size =3D 0; + for (i =3D 0; i < cs; i++) + cum_size +=3D chips[i].ahb_window_size; + + rem_size =3D cum_size % chips[cs].ahb_window_size; + if (chips[cs].ahb_window_size !=3D 0 && rem_size !=3D 0) + chips[0].ahb_window_size +=3D + chips[cs].ahb_window_size - rem_size; + } + + ret =3D aspeed_spi_trim_window_size(aspi); + if (ret !=3D 0) + return ret; + + return 0; +} + /* * Yet to be done when possible : * - Align mappings on flash size (we don't have the info) @@ -475,48 +634,18 @@ static int aspeed_spi_chip_adjust_window(struct aspee= d_spi_chip *chip, u32 local_offset, u32 size) { struct aspeed_spi *aspi =3D chip->aspi; - u32 cs; - u32 total_window_size; int ret; =20 /* No segment registers for the AST2400 SPI controller */ if (aspi->data =3D=3D &ast2400_spi_data) return 0; =20 - /* - * Due to an HW issue on the AST2500 SPI controller, the CE0 - * window size should be smaller than the maximum 128MB. - */ - if (aspi->data =3D=3D &ast2500_spi_data && chip->cs =3D=3D 0 && size =3D= =3D SZ_128M) { - size =3D 120 << 20; - dev_info(aspi->dev, "CE%d window resized to %dMB (AST2500 HW quirk)", - chip->cs, size >> 20); - } - - /* - * The decoding size of AST2600 SPI controller should set at - * least 2MB. - */ - if ((aspi->data =3D=3D &ast2600_spi_data || aspi->data =3D=3D &ast2600_fm= c_data) && - size < SZ_2M) { - size =3D SZ_2M; - dev_info(aspi->dev, "CE%d window resized to %dMB (AST2600 Decoding)", - chip->cs, size >> 20); - } - /* Adjust this chip window */ aspi->chips[chip->cs].ahb_window_size =3D size; =20 - total_window_size =3D 0; - for (cs =3D 0; cs < aspi->data->max_cs; cs++) - total_window_size +=3D aspi->chips[cs].ahb_window_size; - - if (total_window_size > aspi->ahb_window_size) { - aspi->chips[chip->cs].ahb_window_size -=3D (total_window_size - - aspi->ahb_window_size); - dev_warn(aspi->dev, "CE%d window resized to %zdMB", - chip->cs, aspi->chips[chip->cs].ahb_window_size >> 20); - } + /* Adjust the overall windows size regarding each platform */ + if (aspi->data->adjust_window) + aspi->data->adjust_window(aspi); =20 ret =3D aspeed_spi_set_window(aspi); if (ret) @@ -600,7 +729,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_di= rmap_desc *desc, struct aspeed_spi_chip *chip =3D &aspi->chips[spi_get_chipselect(desc->me= m->spi, 0)]; =20 /* Switch to USER command mode if mapping window is too small */ - if (chip->ahb_window_size < offset + len) { + if (chip->ahb_window_size < offset + len || chip->force_user_mode) { int ret; =20 ret =3D aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf= ); @@ -1265,6 +1394,7 @@ static const struct aspeed_spi_data ast2400_fmc_data = =3D { .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, .segment_reg =3D aspeed_spi_segment_reg, + .adjust_window =3D aspeed_adjust_window_ast2400, }; =20 static const struct aspeed_spi_data ast2400_spi_data =3D { @@ -1294,6 +1424,7 @@ static const struct aspeed_spi_data ast2500_fmc_data = =3D { .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, .segment_reg =3D aspeed_spi_segment_reg, + .adjust_window =3D aspeed_adjust_window_ast2500, }; =20 static const struct aspeed_spi_data ast2500_spi_data =3D { @@ -1310,6 +1441,7 @@ static const struct aspeed_spi_data ast2500_spi_data = =3D { .segment_start =3D aspeed_spi_segment_start, .segment_end =3D aspeed_spi_segment_end, .segment_reg =3D aspeed_spi_segment_reg, + .adjust_window =3D aspeed_adjust_window_ast2500, }; =20 static const struct aspeed_spi_data ast2600_fmc_data =3D { @@ -1327,6 +1459,7 @@ static const struct aspeed_spi_data ast2600_fmc_data = =3D { .segment_start =3D aspeed_spi_segment_ast2600_start, .segment_end =3D aspeed_spi_segment_ast2600_end, .segment_reg =3D aspeed_spi_segment_ast2600_reg, + .adjust_window =3D aspeed_adjust_window_ast2600, }; =20 static const struct aspeed_spi_data ast2600_spi_data =3D { @@ -1344,6 +1477,7 @@ static const struct aspeed_spi_data ast2600_spi_data = =3D { .segment_start =3D aspeed_spi_segment_ast2600_start, .segment_end =3D aspeed_spi_segment_ast2600_end, .segment_reg =3D aspeed_spi_segment_ast2600_reg, + .adjust_window =3D aspeed_adjust_window_ast2600, }; =20 static const struct of_device_id aspeed_spi_matches[] =3D { --=20 2.34.1 From nobody Wed Oct 1 21:24:54 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 67AC42DCBEB; Wed, 1 Oct 2025 11:26:26 +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=1759317988; cv=none; b=Of+Nt/UG3sKi+zQEXCF3RkRQG+Yn8lEWkxduBVUXHu1iHvwBDCFhp0RVmyx94pt6pBLNZS+wxoAQEPSiarO0ofuZdN6blXFvp4fLozqZh/xgVGHTkWJFdfy96Bjf8ksaijLxoSU8Tx3+LRXf3MFkYWuy4Gv+sLSYpoFQPFKUawA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759317988; c=relaxed/simple; bh=vKwBowoTaymQ8jmDwD4h32a8cDe97YdUoEzA8j+qWz4=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ed2GUgUnA8F+EWtq6zsjkmwFkAk6I+JhSOoYuFiWDUOriCVZaXU6ZbA8BMcKhyLSNIfpSkdONqadNtqYGuxOiMg7XFnhmfnQanE0boevzHeHBbHicM9bdD6g2QRs5Ihdz3ETgoBx6ySEUfxyZFXknvtdXyvM58KmdNCzC5oO8k4= 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 6/6] spi: aspeed: Only map necessary address window region Date: Wed, 1 Oct 2025 19:26:05 +0800 Message-ID: <20251001112605.1130723-7-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" Previously, the driver mapped the entire SPI address decoding region during probe. On systems with small flash or limited memory, this could lead to excessive memory usage or allocation failures. This patch changes the strategy to initially map a small address window for SPI flash device probing. After determining each chip select's flash size, the driver unmaps the temporary region and remaps only the required address window accordingly. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 39 +++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 4f6ae48dd904..0c3de371fd39 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -7,6 +7,7 @@ */ =20 #include +#include #include #include #include @@ -96,7 +97,6 @@ struct aspeed_spi { const struct aspeed_spi_data *data; =20 void __iomem *regs; - void __iomem *ahb_base; u32 ahb_base_phy; u32 ahb_window_size; u32 num_cs; @@ -394,6 +394,13 @@ static int aspeed_spi_set_window(struct aspeed_spi *as= pi) u32 cs; size_t window_size; =20 + for (cs =3D 0; cs < aspi->data->max_cs; cs++) { + if (aspi->chips[cs].ahb_base) { + iounmap(aspi->chips[cs].ahb_base); + aspi->chips[cs].ahb_base =3D NULL; + } + } + for (cs =3D 0; cs < aspi->data->max_cs; cs++) { seg_reg =3D seg_reg_base + cs * 4; seg_val_backup =3D readl(seg_reg); @@ -425,13 +432,29 @@ static int aspeed_spi_set_window(struct aspeed_spi *a= spi) else dev_dbg(dev, "CE%d window closed\n", cs); =20 - aspi->chips[cs].ahb_base =3D aspi->ahb_base + offset; offset +=3D window_size; if (offset > aspi->ahb_window_size) { dev_err(dev, "CE%d offset value 0x%llx is too large.\n", cs, (u64)offset); return -ENOSPC; } + + /* + * No need to map the address deocding range when + * - window size is 0. + * - the CS is unused. + */ + if (window_size =3D=3D 0 || cs >=3D aspi->num_cs) + continue; + + aspi->chips[cs].ahb_base =3D + devm_ioremap(aspi->dev, start, window_size); + if (!aspi->chips[cs].ahb_base) { + dev_err(aspi->dev, + "Fail to remap window [0x%.9llx - 0x%.9llx]\n", + (u64)start, (u64)end - 1); + return -ENOMEM; + } } =20 return 0; @@ -447,7 +470,9 @@ static int aspeed_spi_chip_set_default_window(struct as= peed_spi *aspi) =20 /* No segment registers for the AST2400 SPI controller */ if (aspi->data =3D=3D &ast2400_spi_data) { - aspi->chips[0].ahb_base =3D aspi->ahb_base; + aspi->chips[0].ahb_base =3D devm_ioremap(aspi->dev, + aspi->ahb_base_phy, + aspi->ahb_window_size); aspi->chips[0].ahb_window_size =3D aspi->ahb_window_size; return 0; } @@ -839,10 +864,10 @@ static int aspeed_spi_probe(struct platform_device *p= dev) if (IS_ERR(aspi->regs)) return PTR_ERR(aspi->regs); =20 - aspi->ahb_base =3D devm_platform_get_and_ioremap_resource(pdev, 1, &res); - if (IS_ERR(aspi->ahb_base)) { - dev_err(dev, "missing AHB mapping window\n"); - return PTR_ERR(aspi->ahb_base); + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (IS_ERR(res)) { + dev_err(dev, "missing AHB memory\n"); + return PTR_ERR(res); } =20 aspi->ahb_window_size =3D resource_size(res); --=20 2.34.1