diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 4cf8fc80a7b7..e3b57252d075 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,12 @@ #define HISI_SFC_V3XX_VERSION (0x1f8) #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_RW_MSK BIT(8) #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, if (op->addr.nbytes) 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) { config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; 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 = { .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) { struct device *dev = &pdev->dev; @@ -222,6 +304,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits; + host = spi_controller_get_devdata(ctlr); host->dev = dev; @@ -277,7 +361,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = { .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_AUTHOR("John Garry "); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 38b4c78df506..292f26807b41 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -510,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; spi->cs_gpio = -ENOENT; + spi->mode = ctlr->buswidth_override_bits; spin_lock_init(&spi->statistics.lock); @@ -2181,9 +2182,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, return AE_NO_MEMORY; } + ACPI_COMPANION_SET(&spi->dev, adev); spi->max_speed_hz = lookup.max_speed_hz; - spi->mode = lookup.mode; + spi->mode |= lookup.mode; spi->irq = lookup.irq; spi->bits_per_word = lookup.bits_per_word; spi->chip_select = lookup.chip_select; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6d16ba01ff5a..600e3793303e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -481,6 +481,9 @@ struct spi_controller { /* spi_device.mode flags understood by this controller driver */ u32 mode_bits; + /* spi_device.mode flags override flags for this controller */ + u32 buswidth_override_bits; + /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; #define SPI_BPW_MASK(bits) BIT((bits) - 1)