@@ -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));
}
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(-)