disk: sdhc: Switch to clock frequency from DTS

The "spi-max-frequency" property already exists, but was unused. This
now sets the SPI clock frequency to this value (limited to 24MHz) once
initialisation is complete.

Due to the nature of the SPI API, it is necessary to have two separate
configuration structures to switch clock speed as some SPI drivers only
compare pointers to detect changes.

Fixes: #32996

Signed-off-by: Rich Barlow <rich@bennellick.com>
diff --git a/drivers/disk/sdmmc_spi.c b/drivers/disk/sdmmc_spi.c
index dae70cf..0453fb0 100644
--- a/drivers/disk/sdmmc_spi.c
+++ b/drivers/disk/sdmmc_spi.c
@@ -18,9 +18,11 @@
 #include "sdmmc_sdhc.h"
 
 /* Clock speed used during initialisation */
-#define SDHC_SPI_INITIAL_SPEED 400000
-/* Clock speed used after initialisation */
-#define SDHC_SPI_SPEED 4000000
+#define SDHC_SPI_INIT_SPEED KHZ(400)
+/* Maximum clock speed used after initialisation (actual speed set in DTS).
+ * SD Specifications Part 1 Physical layer states 25MHz maximum.
+ */
+#define SDHC_SPI_MAX_OPER_SPEED MHZ(25)
 
 #define SPI_SDHC_NODE DT_DRV_INST(0)
 
@@ -29,10 +31,7 @@
 #else
 struct sdhc_spi_data {
 	const struct device *spi;
-	struct spi_config cfg;
-#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
-	struct spi_cs_control cs;
-#endif
+	const struct spi_config *spi_cfg;
 	bool high_capacity;
 	uint32_t sector_count;
 	uint8_t status;
@@ -41,6 +40,27 @@
 #endif
 };
 
+struct sdhc_spi_config {
+	struct spi_config init_cfg;
+	struct spi_config oper_cfg;
+#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
+	struct spi_cs_control cs;
+#endif
+};
+
+static void sdhc_spi_set_status(const struct device *dev, uint8_t status)
+{
+	struct sdhc_spi_data *data = dev->data;
+	const struct sdhc_spi_config *cfg = dev->config;
+
+	data->status = status;
+	if (status == DISK_STATUS_UNINIT) {
+		data->spi_cfg = &cfg->init_cfg;
+	} else if (status == DISK_STATUS_OK) {
+		data->spi_cfg = &cfg->oper_cfg;
+	}
+}
+
 /* Traces card traffic for LOG_LEVEL_DBG */
 static int sdhc_spi_trace(struct sdhc_spi_data *data, int dir, int err,
 		      const uint8_t *buf, int len)
@@ -98,7 +118,7 @@
 	};
 
 	return sdhc_spi_trace(data, -1,
-			  spi_transceive(data->spi, &data->cfg, &tx, &rx),
+			  spi_transceive(data->spi, data->spi_cfg, &tx, &rx),
 			  buf, len);
 }
 
@@ -131,7 +151,7 @@
 	};
 
 	return sdhc_spi_trace(data, 1,
-			spi_write(data->spi, &data->cfg, &tx), buf,
+			spi_write(data->spi, data->spi_cfg, &tx), buf,
 			len);
 }
 
@@ -396,7 +416,7 @@
 		};
 
 		err = sdhc_spi_trace(data, -1,
-				spi_transceive(data->spi, &data->cfg,
+				spi_transceive(data->spi, data->spi_cfg,
 				&tx, &rx),
 				&buf[i], remain);
 		if (err != 0) {
@@ -459,7 +479,7 @@
 {
 	/* Write the initial >= 74 clocks */
 	sdhc_spi_tx(data, sdhc_ones, 10);
-	spi_release(data->spi, &data->cfg);
+	spi_release(data->spi, data->spi_cfg);
 
 	return sdhc_spi_cmd_r1_idle(data, SDHC_GO_IDLE_STATE, 0);
 }
@@ -491,8 +511,10 @@
 }
 
 /* Detect and initialise the card */
-static int sdhc_spi_detect(struct sdhc_spi_data *data)
+static int sdhc_spi_detect(const struct device *dev)
 {
+	struct sdhc_spi_data *data = dev->data;
+
 	int err;
 	uint32_t ocr;
 	struct sdhc_retry retry;
@@ -503,8 +525,7 @@
 	uint8_t buf[SDHC_CSD_SIZE];
 	bool is_v2;
 
-	data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
-	data->status = DISK_STATUS_UNINIT;
+	sdhc_spi_set_status(dev, DISK_STATUS_UNINIT);
 
 	sdhc_retry_init(&retry, SDHC_INIT_TIMEOUT, SDHC_RETRY_DELAY);
 
@@ -641,8 +662,7 @@
 		buf[7], buf[8], sys_get_be32(&buf[9]));
 
 	/* Initilisation complete */
-	data->cfg.frequency = SDHC_SPI_SPEED;
-	data->status = DISK_STATUS_OK;
+	sdhc_spi_set_status(dev, DISK_STATUS_OK);
 
 	return 0;
 }
@@ -690,7 +710,7 @@
 	err = sdhc_spi_skip_until_ready(data);
 
 error:
-	spi_release(data->spi, &data->cfg);
+	spi_release(data->spi, data->spi_cfg);
 
 	return err;
 }
@@ -745,7 +765,7 @@
 
 	err = 0;
 error:
-	spi_release(data->spi, &data->cfg);
+	spi_release(data->spi, data->spi_cfg);
 
 	return err;
 }
@@ -824,7 +844,7 @@
 
 	err = 0;
 exit:
-	spi_release(data->spi, &data->cfg);
+	spi_release(data->spi, data->spi_cfg);
 
 	return err;
 }
@@ -837,20 +857,6 @@
 
 	data->spi = device_get_binding(DT_BUS_LABEL(SPI_SDHC_NODE));
 
-	data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
-	data->cfg.operation = SPI_WORD_SET(8) | SPI_HOLD_ON_CS;
-	data->cfg.slave = DT_REG_ADDR(SPI_SDHC_NODE);
-
-#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
-	data->cs.gpio_dev =
-		device_get_binding(DT_SPI_DEV_CS_GPIOS_LABEL(SPI_SDHC_NODE));
-	__ASSERT_NO_MSG(data->cs.gpio_dev != NULL);
-
-	data->cs.gpio_pin = DT_SPI_DEV_CS_GPIOS_PIN(SPI_SDHC_NODE);
-	data->cs.gpio_dt_flags = DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_SDHC_NODE);
-	data->cfg.cs = &data->cs;
-#endif
-
 	disk_spi_sdhc_init(dev);
 
 	return 0;
@@ -947,8 +953,8 @@
 	struct sdhc_spi_data *data = dev->data;
 	int err;
 
-	err = sdhc_spi_detect(data);
-	spi_release(data->spi, &data->cfg);
+	err = sdhc_spi_detect(dev);
+	spi_release(data->spi, data->spi_cfg);
 
 	return err;
 }
@@ -968,9 +974,7 @@
 
 static int disk_spi_sdhc_init(const struct device *dev)
 {
-	struct sdhc_spi_data *data = dev->data;
-
-	data->status = DISK_STATUS_UNINIT;
+	sdhc_spi_set_status(dev, DISK_STATUS_UNINIT);
 
 	spi_sdhc_disk.dev = dev;
 
@@ -979,7 +983,34 @@
 
 static struct sdhc_spi_data sdhc_spi_data_0;
 
+static const struct sdhc_spi_config sdhc_spi_cfg_0 = {
+	.init_cfg = {
+		.frequency = SDHC_SPI_INIT_SPEED,
+		.operation = SPI_WORD_SET(8) | SPI_HOLD_ON_CS,
+		.slave = DT_REG_ADDR(SPI_SDHC_NODE),
+#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
+		.cs = &sdhc_spi_cfg_0.cs,
+#endif
+	},
+	.oper_cfg = {
+		.frequency = MIN(SDHC_SPI_MAX_OPER_SPEED,
+				 DT_INST_PROP(0, spi_max_frequency)),
+		.operation = SPI_WORD_SET(8) | SPI_HOLD_ON_CS,
+		.slave = DT_REG_ADDR(SPI_SDHC_NODE),
+#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
+		.cs = &sdhc_spi_cfg_0.cs,
+#endif
+	},
+#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
+	.cs = {
+		.gpio_dev = DEVICE_DT_GET(DT_SPI_DEV_CS_GPIOS_CTLR(SPI_SDHC_NODE)),
+		.gpio_pin = DT_SPI_DEV_CS_GPIOS_PIN(SPI_SDHC_NODE),
+		.gpio_dt_flags = DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_SDHC_NODE),
+	},
+#endif
+};
+
 DEVICE_DT_INST_DEFINE(0, sdhc_spi_init, device_pm_control_nop,
-	&sdhc_spi_data_0, NULL,
+	&sdhc_spi_data_0, &sdhc_spi_cfg_0,
 	POST_KERNEL, CONFIG_SDMMC_INIT_PRIORITY, NULL);
 #endif