diff mbox series

[2/2] spi: octeontx2: Add ACPI support

Message ID 20230327180753.2279-3-pmalgujar@marvell.com (mailing list archive)
State New, archived
Headers show
Series spi: octeontx2: Add spi driver for OcteonTX2 SOC | expand

Commit Message

Piyush Malgujar March 27, 2023, 6:07 p.m. UTC
Enable ACPI support for SPI controller on Marvell Octeontx2 SOC. This
supports reading the tx(rx)-bus-width from ACPI table which is used to
set the SPI mode - DUAL, QUAD, OCTAL.

Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
 drivers/spi/spi-octeontx2.c | 117 +++++++++++++++++++++++++++++++-----
 1 file changed, 103 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/drivers/spi/spi-octeontx2.c b/drivers/spi/spi-octeontx2.c
index 80d9355d119de5486a1a3803f798dd0673b0adf1..31a08b4e574f6f391336852ad0e90f84d63de51a 100644
--- a/drivers/spi/spi-octeontx2.c
+++ b/drivers/spi/spi-octeontx2.c
@@ -5,11 +5,16 @@ 
  * Copyright (C) 2018 Marvell International Ltd.
  */
 
+#include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/pci.h>
+#include <linux/property.h>
 #include <linux/spi/spi.h>
+#ifdef CONFIG_ACPI
+#include <linux/spi/spi-mem.h>
+#endif
 
 #include "spi-octeontx2.h"
 
@@ -227,6 +232,73 @@  int octeontx2_spi_transfer_one_message(struct spi_master *master,
 	return status;
 }
 
+#ifdef CONFIG_ACPI
+
+static bool octeontx2_spi_supports_op(struct spi_mem *mem,
+				      const struct spi_mem_op *op)
+{
+	struct spi_device *spi = mem->spi;
+	const union acpi_object *obj;
+	struct acpi_device *adev;
+
+	adev = ACPI_COMPANION(&spi->dev);
+
+	if (!acpi_dev_get_property(adev, "spi-tx-bus-width", ACPI_TYPE_INTEGER,
+				   &obj)) {
+		switch (obj->integer.value) {
+		case 1:
+			break;
+		case 2:
+			spi->mode |= SPI_TX_DUAL;
+			break;
+		case 4:
+			spi->mode |= SPI_TX_QUAD;
+			break;
+		case 8:
+			spi->mode |= SPI_TX_OCTAL;
+			break;
+		default:
+			dev_warn(&spi->dev,
+				 "spi-tx-bus-width %lld not supported\n",
+				 obj->integer.value);
+			break;
+		}
+	}
+
+	if (!acpi_dev_get_property(adev, "spi-rx-bus-width", ACPI_TYPE_INTEGER,
+				   &obj)) {
+		switch (obj->integer.value) {
+		case 1:
+			break;
+		case 2:
+			spi->mode |= SPI_RX_DUAL;
+			break;
+		case 4:
+			spi->mode |= SPI_RX_QUAD;
+			break;
+		case 8:
+			spi->mode |= SPI_RX_OCTAL;
+			break;
+		default:
+			dev_warn(&spi->dev,
+				 "spi-rx-bus-width %lld not supported\n",
+				 obj->integer.value);
+			break;
+		}
+	}
+
+	if (!spi_mem_default_supports_op(mem, op))
+		return false;
+
+	return true;
+}
+
+static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
+	.supports_op = octeontx2_spi_supports_op,
+};
+
+#endif
+
 static int octeontx2_spi_probe(struct pci_dev *pdev,
 			       const struct pci_device_id *ent)
 {
@@ -235,6 +307,7 @@  static int octeontx2_spi_probe(struct pci_dev *pdev,
 	struct octeontx2_spi *p;
 	union mpix_sts mpi_sts;
 	int ret = -ENOENT;
+	bool has_acpi;
 
 	/* may need to hunt for devtree entry */
 	if (!pdev->dev.of_node) {
@@ -248,6 +321,8 @@  static int octeontx2_spi_probe(struct pci_dev *pdev,
 		of_node_put(np);
 	}
 
+	has_acpi = has_acpi_companion(dev);
+
 	master = spi_alloc_master(dev, sizeof(struct octeontx2_spi));
 	if (!master) {
 		ret = -ENOMEM;
@@ -280,20 +355,26 @@  static int octeontx2_spi_probe(struct pci_dev *pdev,
 	mpi_sts.u64 = readq(p->register_base + OCTEONTX2_SPI_STS(p));
 	p->rcvd_present = mpi_sts.u64 & 0x4 ? true : false;
 
-	p->clk = devm_clk_get(dev, NULL);
-	if (IS_ERR(p->clk)) {
-		p->clk = devm_clk_get(dev, "sclk");
-		p->sys_freq = 0;
+	if (!has_acpi) {
+		p->clk = devm_clk_get(dev, NULL);
+		if (IS_ERR(p->clk)) {
+			p->clk = devm_clk_get(dev, "sclk");
+			p->sys_freq = 0;
+		} else {
+			ret = clk_prepare_enable(p->clk);
+			if (!ret)
+				p->sys_freq = clk_get_rate(p->clk);
+		}
+
+		if (!p->sys_freq)
+			p->sys_freq = SYS_FREQ_DEFAULT;
+		if (tbi_clk_en)
+			p->sys_freq = TBI_FREQ;
 	} else {
-		ret = clk_prepare_enable(p->clk);
-		if (!ret)
-			p->sys_freq = clk_get_rate(p->clk);
+		device_property_read_u32(dev, "sclk", &p->sys_freq);
+		if (!p->sys_freq)
+			p->sys_freq = TBI_FREQ;
 	}
-
-	if (!p->sys_freq)
-		p->sys_freq = SYS_FREQ_DEFAULT;
-	if (tbi_clk_en)
-		p->sys_freq = TBI_FREQ;
 	dev_info(dev, "Reference clock is %u\n", p->sys_freq);
 
 	master->num_chipselect = 4;
@@ -305,6 +386,10 @@  static int octeontx2_spi_probe(struct pci_dev *pdev,
 	master->bits_per_word_mask = SPI_BPW_MASK(8);
 	master->max_speed_hz = OCTEONTX2_SPI_MAX_CLOCK_HZ;
 	master->dev.of_node = pdev->dev.of_node;
+	master->dev.fwnode = pdev->dev.fwnode;
+	#ifdef CONFIG_ACPI
+		master->mem_ops = &octeontx2_spi_mem_ops;
+	#endif
 
 	pci_set_drvdata(pdev, master);
 
@@ -315,7 +400,8 @@  static int octeontx2_spi_probe(struct pci_dev *pdev,
 	return 0;
 
 error_disable:
-	clk_disable_unprepare(p->clk);
+	if (!has_acpi)
+		clk_disable_unprepare(p->clk);
 error_put:
 	spi_master_put(master);
 error:
@@ -327,10 +413,13 @@  static void octeontx2_spi_remove(struct pci_dev *pdev)
 	struct spi_master *master = pci_get_drvdata(pdev);
 	struct octeontx2_spi *p;
 
+	bool has_acpi = has_acpi_companion(&pdev->dev);
+
 	p = spi_master_get_devdata(master);
 	/* Put everything in a known state. */
 	if (p) {
-		clk_disable_unprepare(p->clk);
+		if (!has_acpi)
+			clk_disable_unprepare(p->clk);
 		writeq(0, p->register_base + OCTEONTX2_SPI_CFG(p));
 	}