spi: aspeed: Clock frequency adjustment support

Driver can configure the SPI clock frequnecy to the
target value of "spi-max-frequency" property in
the device tree. The frequency is divided from HCLK,
200MHz. Usually, the ASPEED SPI clock frequency range
is between 12.5MHz and 100MHz. On AST2600, the lowest
SPI clock frequency can be about 780kHz.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 2b6c4b4..a3c9633 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -60,6 +60,7 @@
 	u8 max_cs;
 	void __iomem *ahb_base; /* AHB address base for all flash devices. */
 	fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */
+	u32 hclk_rate; /* AHB clock rate */
 };
 
 struct aspeed_spi_flash {
@@ -67,6 +68,7 @@
 	u32 ahb_decoded_sz;
 	u32 ce_ctrl_user;
 	u32 ce_ctrl_read;
+	u32 max_freq;
 };
 
 struct aspeed_spi_priv {
@@ -81,11 +83,13 @@
 	u32 io_mode_mask;
 	u32 max_bus_width;
 	u32 min_decoded_sz;
+	u32 clk_ctrl_mask;
 	void (*set_4byte)(struct udevice *bus, u32 cs);
 	u32 (*segment_start)(struct udevice *bus, u32 reg);
 	u32 (*segment_end)(struct udevice *bus, u32 reg);
 	u32 (*segment_reg)(u32 start, u32 end);
 	int (*adjust_decoded_sz)(struct udevice *bus);
+	u32 (*get_clk_setting)(struct udevice *dev, uint hz);
 };
 
 struct aspeed_spi_decoded_range {
@@ -165,6 +169,44 @@
 	writel(flash->ce_ctrl_read, &priv->regs->ctrl);
 }
 
+/* Transfer maximum clock frequency to register setting */
+static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0000; /* default value */
+	u32 i;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[11:8] */
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / (i + 1) <= max_hz) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		hclk_div = hclk_masks[i] << 8;
+		priv->flashes[slave_plat->cs].max_freq = hclk_clk / (i + 1);
+	}
+
+	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: %d (mask %x), speed: %d\n",
+			i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -253,6 +295,58 @@
 	return 0;
 }
 
+static u32 ast2500_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0000; /* default value */
+	u32 i;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[11:8] */
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / (i + 1) <= max_hz) {
+			found = true;
+			priv->flashes[slave_plat->cs].max_freq =
+							hclk_clk / (i + 1);
+			break;
+		}
+	}
+
+	if (found) {
+		hclk_div = hclk_masks[i] << 8;
+		goto end;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+		if (hclk_clk / ((i + 1) * 4) <= max_hz) {
+			found = true;
+			priv->flashes[slave_plat->cs].max_freq =
+						hclk_clk / ((i + 1) * 4);
+			break;
+		}
+	}
+
+	if (found)
+		hclk_div = BIT(13) | (hclk_masks[i] << 8);
+
+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: %d (mask %x), speed: %d\n",
+			i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
@@ -335,6 +429,51 @@
 	return 0;
 }
 
+static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 hclk_clk = plat->hclk_rate;
+	u32 hclk_div = 0x0400; /* default value */
+	u32 i, j;
+	bool found = false;
+	/* HCLK/1 ..	HCLK/16 */
+	u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+			    11, 3, 10, 2, 9,  1, 8,  0};
+
+	/* FMC/SPIR10[27:24] */
+	for (j = 0; j < 0xf; j++) {
+		/* FMC/SPIR10[11:8] */
+		for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+			if (i == 0 && j == 0)
+				continue;
+
+			if (hclk_clk / (i + 1 + (j * 16)) <= max_hz) {
+				found = true;
+				break;
+			}
+		}
+
+		if (found) {
+			hclk_div = ((j << 24) | hclk_masks[i] << 8);
+			priv->flashes[slave_plat->cs].max_freq =
+						hclk_clk / (i + 1 + j * 16);
+			break;
+		}
+	}
+
+	dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+		hclk_clk, max_hz);
+
+	if (found) {
+		dev_dbg(dev, "base_clk: %d, h_div: %d (mask %x), speed: %d\n",
+			j, i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+	}
+
+	return hclk_div;
+}
+
 /*
  * As the flash size grows up, we need to trim some decoded
  * size if needed for the sake of conforming the maximum
@@ -531,7 +670,7 @@
 	struct spi_mem_op op_tmpl = desc->info.op_tmpl;
 	u32 i;
 	u32 cs = slave_plat->cs;
-	u32 reg_val;
+	u32 cmd_io_conf;
 	u32 ce_ctrl_reg;
 
 	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
@@ -564,15 +703,16 @@
 			return ret;
 	}
 
-	reg_val = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
-		  op_tmpl.cmd.opcode << 16 |
-		  ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
-		  ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
-		  CTRL_IO_MODE_CMD_READ;
+	cmd_io_conf = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
+		      op_tmpl.cmd.opcode << 16 |
+		      ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
+		      ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
+		      CTRL_IO_MODE_CMD_READ;
 
-	writel(reg_val, ce_ctrl_reg);
+	priv->flashes[cs].ce_ctrl_read &= priv->info->clk_ctrl_mask;
+	priv->flashes[cs].ce_ctrl_read |= cmd_io_conf;
 
-	priv->flashes[cs].ce_ctrl_read = reg_val;
+	writel(priv->flashes[cs].ce_ctrl_read, ce_ctrl_reg);
 
 	dev_dbg(dev, "read bus width: %d ce_ctrl_val: 0x%08x\n",
 		op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
@@ -806,7 +946,8 @@
 
 	/* Initial user mode. */
 	for (cs = 0; cs < priv->num_cs; cs++) {
-		priv->flashes[cs].ce_ctrl_user =
+		priv->flashes[cs].ce_ctrl_user &= priv->info->clk_ctrl_mask;
+		priv->flashes[cs].ce_ctrl_user |=
 				(CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
 	}
 
@@ -848,31 +989,37 @@
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2400_fmc_chip_set_4byte,
 	.segment_start = ast2400_spi_segment_start,
 	.segment_end = ast2400_spi_segment_end,
 	.segment_reg = ast2400_spi_segment_reg,
+	.get_clk_setting = ast2400_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2400_spi_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00000f00,
 	.set_4byte = ast2400_spi_chip_set_4byte,
 	.segment_start = ast2400_spi_segment_start,
 	.segment_end = ast2400_spi_segment_end,
 	.segment_reg = ast2400_spi_segment_reg,
+	.get_clk_setting = ast2400_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2500_fmc_info = {
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2500_spi_chip_set_4byte,
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
 	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
 };
 
 /*
@@ -883,42 +1030,59 @@
 	.io_mode_mask = 0x70000000,
 	.max_bus_width = 2,
 	.min_decoded_sz = 0x800000,
+	.clk_ctrl_mask = 0x00002f00,
 	.set_4byte = ast2500_spi_chip_set_4byte,
 	.segment_start = ast2500_spi_segment_start,
 	.segment_end = ast2500_spi_segment_end,
 	.segment_reg = ast2500_spi_segment_reg,
 	.adjust_decoded_sz = ast2500_adjust_decoded_size,
+	.get_clk_setting = ast2500_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2600_fmc_info = {
 	.io_mode_mask = 0xf0000000,
 	.max_bus_width = 4,
 	.min_decoded_sz = 0x200000,
+	.clk_ctrl_mask = 0x0f000f00,
 	.set_4byte = ast2600_spi_chip_set_4byte,
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
 	.adjust_decoded_sz = ast2600_adjust_decoded_size,
+	.get_clk_setting = ast2600_get_clk_setting,
 };
 
 static const struct aspeed_spi_info ast2600_spi_info = {
 	.io_mode_mask = 0xf0000000,
 	.max_bus_width = 4,
 	.min_decoded_sz = 0x200000,
+	.clk_ctrl_mask = 0x0f000f00,
 	.set_4byte = ast2600_spi_chip_set_4byte,
 	.segment_start = ast2600_spi_segment_start,
 	.segment_end = ast2600_spi_segment_end,
 	.segment_reg = ast2600_spi_segment_reg,
 	.adjust_decoded_sz = ast2600_adjust_decoded_size,
+	.get_clk_setting = ast2600_get_clk_setting,
 };
 
 static int aspeed_spi_claim_bus(struct udevice *dev)
 {
 	struct udevice *bus = dev->parent;
 	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct aspeed_spi_flash *flash = &priv->flashes[slave_plat->cs];
+	u32 clk_setting;
 
 	dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs);
 
+	if (flash->max_freq == 0) {
+		clk_setting = priv->info->get_clk_setting(dev, slave_plat->max_hz);
+		flash->ce_ctrl_user &= ~(priv->info->clk_ctrl_mask);
+		flash->ce_ctrl_user |= clk_setting;
+		flash->ce_ctrl_read &= ~(priv->info->clk_ctrl_mask);
+		flash->ce_ctrl_read |= clk_setting;
+	}
+
 	return 0;
 }
 
@@ -958,6 +1122,8 @@
 {
 	struct aspeed_spi_plat *plat = dev_get_plat(bus);
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	int ret;
+	struct clk hclk;
 
 	priv->regs = (void __iomem *)devfdt_get_addr_index(bus, 0);
 	if ((u32)priv->regs == FDT_ADDR_T_NONE) {
@@ -976,9 +1142,19 @@
 	if (plat->max_cs > ASPEED_SPI_MAX_CS)
 		return -EINVAL;
 
+	ret = clk_get_by_index(bus, 0, &hclk);
+	if (ret < 0) {
+		dev_err(bus, "%s could not get clock: %d\n", bus->name, ret);
+		return ret;
+	}
+
+	plat->hclk_rate = clk_get_rate(&hclk);
+	clk_free(&hclk);
+
 	dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%lx\n",
 		(u32)priv->regs, plat->ahb_base, plat->ahb_sz);
-	dev_dbg(bus, "max_cs = %d\n", plat->max_cs);
+	dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
+		plat->hclk_rate / 1000000, plat->max_cs);
 
 	return 0;
 }