summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-dw-mmio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-dw-mmio.c')
-rw-r--r--drivers/spi/spi-dw-mmio.c152
1 files changed, 108 insertions, 44 deletions
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 5101c4c6017b..33239b4778cb 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -54,6 +54,20 @@ struct dw_spi_mscc {
};
/*
+ * Elba SoC does not use ssi, pin override is used for cs 0,1 and
+ * gpios for cs 2,3 as defined in the device tree.
+ *
+ * cs: | 1 0
+ * bit: |---3-------2-------1-------0
+ * | cs1 cs1_ovr cs0 cs0_ovr
+ */
+#define ELBA_SPICS_REG 0x2468
+#define ELBA_SPICS_OFFSET(cs) ((cs) << 1)
+#define ELBA_SPICS_MASK(cs) (GENMASK(1, 0) << ELBA_SPICS_OFFSET(cs))
+#define ELBA_SPICS_SET(cs, val) \
+ ((((val) << 1) | BIT(0)) << ELBA_SPICS_OFFSET(cs))
+
+/*
* The Designware SPI controller (referred to as master in the documentation)
* automatically deasserts chip select when the tx fifo is empty. The chip
* selects then needs to be either driven as GPIOs or, for the first 4 using
@@ -62,10 +76,10 @@ struct dw_spi_mscc {
*/
static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable)
{
- struct dw_spi *dws = spi_master_get_devdata(spi->master);
+ struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
struct dw_spi_mscc *dwsmscc = dwsmmio->priv;
- u32 cs = spi->chip_select;
+ u32 cs = spi_get_chipselect(spi, 0);
if (cs < 4) {
u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE;
@@ -135,10 +149,10 @@ static int dw_spi_mscc_jaguar2_init(struct platform_device *pdev,
*/
static void dw_spi_sparx5_set_cs(struct spi_device *spi, bool enable)
{
- struct dw_spi *dws = spi_master_get_devdata(spi->master);
+ struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
struct dw_spi_mscc *dwsmscc = dwsmmio->priv;
- u8 cs = spi->chip_select;
+ u8 cs = spi_get_chipselect(spi, 0);
if (!enable) {
/* CS override drive enable */
@@ -214,11 +228,28 @@ static int dw_spi_hssi_init(struct platform_device *pdev,
return 0;
}
-static int dw_spi_keembay_init(struct platform_device *pdev,
- struct dw_spi_mmio *dwsmmio)
+static int dw_spi_intel_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
{
dwsmmio->dws.ip = DW_HSSI_ID;
- dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST;
+
+ return 0;
+}
+
+/*
+ * DMA-based mem ops are not configured for this device and are not tested.
+ */
+static int dw_spi_mountevans_imc_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ /*
+ * The Intel Mount Evans SoC's Integrated Management Complex DW
+ * apb_ssi_v4.02a controller has an errata where a full TX FIFO can
+ * result in data corruption. The suggested workaround is to never
+ * completely fill the FIFO. The TX FIFO has a size of 32 so the
+ * fifo_len is set to 31.
+ */
+ dwsmmio->dws.fifo_len = 31;
return 0;
}
@@ -238,6 +269,49 @@ static int dw_spi_canaan_k210_init(struct platform_device *pdev,
return 0;
}
+static void dw_spi_elba_override_cs(struct regmap *syscon, int cs, int enable)
+{
+ regmap_update_bits(syscon, ELBA_SPICS_REG, ELBA_SPICS_MASK(cs),
+ ELBA_SPICS_SET(cs, enable));
+}
+
+static void dw_spi_elba_set_cs(struct spi_device *spi, bool enable)
+{
+ struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
+ struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
+ struct regmap *syscon = dwsmmio->priv;
+ u8 cs;
+
+ cs = spi_get_chipselect(spi, 0);
+ if (cs < 2)
+ dw_spi_elba_override_cs(syscon, spi_get_chipselect(spi, 0), enable);
+
+ /*
+ * The DW SPI controller needs a native CS bit selected to start
+ * the serial engine.
+ */
+ spi_set_chipselect(spi, 0, 0);
+ dw_spi_set_cs(spi, enable);
+ spi_set_chipselect(spi, 0, cs);
+}
+
+static int dw_spi_elba_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ struct regmap *syscon;
+
+ syscon = syscon_regmap_lookup_by_phandle(dev_of_node(&pdev->dev),
+ "amd,pensando-elba-syscon");
+ if (IS_ERR(syscon))
+ return dev_err_probe(&pdev->dev, PTR_ERR(syscon),
+ "syscon regmap lookup failed\n");
+
+ dwsmmio->priv = syscon;
+ dwsmmio->dws.set_cs = dw_spi_elba_set_cs;
+
+ return 0;
+}
+
static int dw_spi_mmio_probe(struct platform_device *pdev)
{
int (*init_func)(struct platform_device *pdev,
@@ -246,7 +320,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
struct resource *mem;
struct dw_spi *dws;
int ret;
- int num_cs;
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
GFP_KERNEL);
@@ -266,53 +339,45 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
if (dws->irq < 0)
return dws->irq; /* -ENXIO */
- dwsmmio->clk = devm_clk_get(&pdev->dev, NULL);
+ dwsmmio->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dwsmmio->clk))
return PTR_ERR(dwsmmio->clk);
- ret = clk_prepare_enable(dwsmmio->clk);
- if (ret)
- return ret;
/* Optional clock needed to access the registers */
- dwsmmio->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
- if (IS_ERR(dwsmmio->pclk)) {
- ret = PTR_ERR(dwsmmio->pclk);
- goto out_clk;
- }
- ret = clk_prepare_enable(dwsmmio->pclk);
- if (ret)
- goto out_clk;
+ dwsmmio->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(dwsmmio->pclk))
+ return PTR_ERR(dwsmmio->pclk);
/* find an optional reset controller */
dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
- if (IS_ERR(dwsmmio->rstc)) {
- ret = PTR_ERR(dwsmmio->rstc);
- goto out_clk;
- }
- reset_control_deassert(dwsmmio->rstc);
+ if (IS_ERR(dwsmmio->rstc))
+ return PTR_ERR(dwsmmio->rstc);
+
+ ret = reset_control_deassert(dwsmmio->rstc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to deassert resets\n");
dws->bus_num = pdev->id;
dws->max_freq = clk_get_rate(dwsmmio->clk);
- device_property_read_u32(&pdev->dev, "reg-io-width", &dws->reg_io_width);
+ if (device_property_read_u32(&pdev->dev, "reg-io-width",
+ &dws->reg_io_width))
+ dws->reg_io_width = 4;
- num_cs = 4;
-
- device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
-
- dws->num_cs = num_cs;
+ /* Rely on the auto-detection if no property specified */
+ device_property_read_u32(&pdev->dev, "num-cs", &dws->num_cs);
init_func = device_get_match_data(&pdev->dev);
if (init_func) {
ret = init_func(pdev, dwsmmio);
if (ret)
- goto out;
+ goto out_reset;
}
pm_runtime_enable(&pdev->dev);
- ret = dw_spi_add_host(&pdev->dev, dws);
+ ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret)
goto out;
@@ -321,25 +386,19 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
out:
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(dwsmmio->pclk);
-out_clk:
- clk_disable_unprepare(dwsmmio->clk);
+out_reset:
reset_control_assert(dwsmmio->rstc);
return ret;
}
-static int dw_spi_mmio_remove(struct platform_device *pdev)
+static void dw_spi_mmio_remove(struct platform_device *pdev)
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
- dw_spi_remove_host(&dwsmmio->dws);
+ dw_spi_remove_controller(&dwsmmio->dws);
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(dwsmmio->pclk);
- clk_disable_unprepare(dwsmmio->clk);
reset_control_assert(dwsmmio->rstc);
-
- return 0;
}
static const struct of_device_id dw_spi_mmio_of_match[] = {
@@ -349,9 +408,14 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
{ .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init},
{ .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init},
{ .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init},
- { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init},
+ { .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init},
+ {
+ .compatible = "intel,mountevans-imc-ssi",
+ .data = dw_spi_mountevans_imc_init,
+ },
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
+ { .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
@@ -378,4 +442,4 @@ module_platform_driver(dw_spi_mmio_driver);
MODULE_AUTHOR("Jean-Hugues Deschenes <jean-hugues.deschenes@octasic.com>");
MODULE_DESCRIPTION("Memory-mapped I/O interface driver for DW SPI Core");
MODULE_LICENSE("GPL v2");
-MODULE_IMPORT_NS(SPI_DW_CORE);
+MODULE_IMPORT_NS("SPI_DW_CORE");