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@xxxxxxxxxxx> --- drivers/spi/spi-octeontx2.c | 117 +++++++++++++++++++++++++++++++----- 1 file changed, 103 insertions(+), 14 deletions(-) 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)); } -- 2.17.1