Commit 6c4a3372 authored by Mark Brown's avatar Mark Brown

Merge series "spi/HiSilicon v3xx: Support dual and quad mode through DMI...

Merge series "spi/HiSilicon v3xx: Support dual and quad mode through DMI quirks" from John Garry <john.garry@huawei.com>:

As discussed during the original HiSilicon v3xx SPI driver upstreaming,
currently there is no method for the ACPI SPI Serial Bus Connection
Resource Descriptor to define the data buswidth [0], [1].

So we can look to get the ACPI spec updated for this, and I have
submitted a proposal for a new feature here:
https://bugzilla.tianocore.org/show_bug.cgi?id=2557

However I am not sure how successful that will be.

In the meantime, as an alternate approach, this RFC proposes to allow the
SPI controller driver override the device buswidth. In this example,
the driver uses DMI quirks to discover the host machine and set the
buswidth override accordingly when the machine is known to support
dual or quad mode of operation.

I also have included a fix for dual and quad modes in the driver.

Comments welcome. thanks.

[0] https://lore.kernel.org/linux-mtd/20200109212842.GK3702@sirena.org.uk/
[1] https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf,
     19.6.126

John Garry (3):
  spi: Allow SPI controller override device buswidth
  spi: HiSilicon v3xx: Properly set CMD_CONFIG for Dual/Quad modes
  spi: HiSilicon v3xx: Use DMI quirk to set controller buswidth override
    bits

 drivers/spi/spi-hisi-sfc-v3xx.c | 99 ++++++++++++++++++++++++++++++++-
 drivers/spi/spi.c               |  4 +-
 include/linux/spi/spi.h         |  3 +
 3 files changed, 104 insertions(+), 2 deletions(-)

--
2.17.1

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
parents c0e035ac 34e608b0
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/dmi.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -17,6 +18,12 @@ ...@@ -17,6 +18,12 @@
#define HISI_SFC_V3XX_VERSION (0x1f8) #define HISI_SFC_V3XX_VERSION (0x1f8)
#define HISI_SFC_V3XX_CMD_CFG (0x300) #define HISI_SFC_V3XX_CMD_CFG (0x300)
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17)
#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17)
#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17)
#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17)
#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17)
#define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7) #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
...@@ -161,6 +168,43 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, ...@@ -161,6 +168,43 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
if (op->addr.nbytes) if (op->addr.nbytes)
config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
switch (op->data.buswidth) {
case 0 ... 1:
break;
case 2:
if (op->addr.buswidth <= 1) {
config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT;
} else if (op->addr.buswidth == 2) {
if (op->cmd.buswidth <= 1) {
config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO;
} else if (op->cmd.buswidth == 2) {
config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO;
} else {
return -EIO;
}
} else {
return -EIO;
}
break;
case 4:
if (op->addr.buswidth <= 1) {
config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT;
} else if (op->addr.buswidth == 4) {
if (op->cmd.buswidth <= 1) {
config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO;
} else if (op->cmd.buswidth == 4) {
config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO;
} else {
return -EIO;
}
} else {
return -EIO;
}
break;
default:
return -EOPNOTSUPP;
}
if (op->data.dir != SPI_MEM_NO_DATA) { if (op->data.dir != SPI_MEM_NO_DATA) {
config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK; config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
...@@ -207,6 +251,44 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { ...@@ -207,6 +251,44 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
.exec_op = hisi_sfc_v3xx_exec_op, .exec_op = hisi_sfc_v3xx_exec_op,
}; };
static int hisi_sfc_v3xx_buswidth_override_bits;
/*
* ACPI FW does not allow us to currently set the device buswidth, so quirk it
* depending on the board.
*/
static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d)
{
hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
return 0;
}
static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[] = {
{
.callback = hisi_sfc_v3xx_dmi_quirk,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
DMI_MATCH(DMI_PRODUCT_NAME, "D06"),
},
},
{
.callback = hisi_sfc_v3xx_dmi_quirk,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"),
},
},
{
.callback = hisi_sfc_v3xx_dmi_quirk,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"),
},
},
{}
};
static int hisi_sfc_v3xx_probe(struct platform_device *pdev) static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -222,6 +304,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) ...@@ -222,6 +304,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
SPI_TX_DUAL | SPI_TX_QUAD; SPI_TX_DUAL | SPI_TX_QUAD;
ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
host = spi_controller_get_devdata(ctlr); host = spi_controller_get_devdata(ctlr);
host->dev = dev; host->dev = dev;
...@@ -277,7 +361,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = { ...@@ -277,7 +361,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = {
.probe = hisi_sfc_v3xx_probe, .probe = hisi_sfc_v3xx_probe,
}; };
module_platform_driver(hisi_sfc_v3xx_spi_driver); static int __init hisi_sfc_v3xx_spi_init(void)
{
dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table);
return platform_driver_register(&hisi_sfc_v3xx_spi_driver);
}
static void __exit hisi_sfc_v3xx_spi_exit(void)
{
platform_driver_unregister(&hisi_sfc_v3xx_spi_driver);
}
module_init(hisi_sfc_v3xx_spi_init);
module_exit(hisi_sfc_v3xx_spi_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Garry <john.garry@huawei.com>"); MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
......
...@@ -510,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) ...@@ -510,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
spi->dev.bus = &spi_bus_type; spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release; spi->dev.release = spidev_release;
spi->cs_gpio = -ENOENT; spi->cs_gpio = -ENOENT;
spi->mode = ctlr->buswidth_override_bits;
spin_lock_init(&spi->statistics.lock); spin_lock_init(&spi->statistics.lock);
...@@ -2181,9 +2182,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, ...@@ -2181,9 +2182,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
return AE_NO_MEMORY; return AE_NO_MEMORY;
} }
ACPI_COMPANION_SET(&spi->dev, adev); ACPI_COMPANION_SET(&spi->dev, adev);
spi->max_speed_hz = lookup.max_speed_hz; spi->max_speed_hz = lookup.max_speed_hz;
spi->mode = lookup.mode; spi->mode |= lookup.mode;
spi->irq = lookup.irq; spi->irq = lookup.irq;
spi->bits_per_word = lookup.bits_per_word; spi->bits_per_word = lookup.bits_per_word;
spi->chip_select = lookup.chip_select; spi->chip_select = lookup.chip_select;
......
...@@ -481,6 +481,9 @@ struct spi_controller { ...@@ -481,6 +481,9 @@ struct spi_controller {
/* spi_device.mode flags understood by this controller driver */ /* spi_device.mode flags understood by this controller driver */
u32 mode_bits; u32 mode_bits;
/* spi_device.mode flags override flags for this controller */
u32 buswidth_override_bits;
/* bitmask of supported bits_per_word for transfers */ /* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask; u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1) #define SPI_BPW_MASK(bits) BIT((bits) - 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment