summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2019-11-08 17:07:44 +0300
committerMark Brown <broonie@kernel.org>2019-11-08 20:45:09 +0300
commitd575c9b7c8b4c5ddfb1aa75ac91fdcc20ce328c4 (patch)
tree110b16c35651c703596da5091399b22997e6144f /drivers/spi
parent8f16292d8b492ca6b0d58ac0769de1c1a7bbb544 (diff)
downloadlinux-d575c9b7c8b4c5ddfb1aa75ac91fdcc20ce328c4.tar.xz
spi: zynq-qspi: Support two chip selects
The Zynq QSPI controller features 2 CS. When the num-cs DT property is set to 2, the hardware will be initialized to support having two devices connected over each CS. In this case, both CS lines are driven by the state of the U_PAGE (upper page) bit. When unset, the lower page (CS0) is selected, otherwise it is the upper page (CS1). Change tested on a custom design featuring two SPI-NORs with different CS on the Zynq-7000 QSPI bus. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/r/20191108140744.1734-8-miquel.raynal@bootlin.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-zynq-qspi.c33
1 files changed, 25 insertions, 8 deletions
diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
index e76f9c9738f0..17641157354d 100644
--- a/drivers/spi/spi-zynq-qspi.c
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -114,8 +114,8 @@
*/
#define ZYNQ_QSPI_MODEBITS (SPI_CPOL | SPI_CPHA)
-/* Default number of chip selects */
-#define ZYNQ_QSPI_DEFAULT_NUM_CS 1
+/* Maximum number of chip selects */
+#define ZYNQ_QSPI_MAX_NUM_CS 2
/**
* struct zynq_qspi - Defines qspi driver instance
@@ -159,6 +159,7 @@ static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset,
/**
* zynq_qspi_init_hw - Initialize the hardware
* @xqspi: Pointer to the zynq_qspi structure
+ * @num_cs: Number of connected CS (to enable dual memories if needed)
*
* The default settings of the QSPI controller's configurable parameters on
* reset are
@@ -176,7 +177,7 @@ static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset,
* - Set the little endian mode of TX FIFO and
* - Enable the QSPI controller
*/
-static void zynq_qspi_init_hw(struct zynq_qspi *xqspi)
+static void zynq_qspi_init_hw(struct zynq_qspi *xqspi, unsigned int num_cs)
{
u32 config_reg;
@@ -184,7 +185,12 @@ static void zynq_qspi_init_hw(struct zynq_qspi *xqspi)
zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK);
/* Disable linear mode as the boot loader may have used it */
- zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, 0);
+ config_reg = 0;
+ /* At the same time, enable dual mode if more than 1 CS is available */
+ if (num_cs > 1)
+ config_reg |= ZYNQ_QSPI_LCFG_TWO_MEM;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, config_reg);
/* Clear the RX FIFO */
while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) &
@@ -286,6 +292,17 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
struct zynq_qspi *xqspi = spi_controller_get_devdata(ctlr);
u32 config_reg;
+ /* Select the lower (CS0) or upper (CS1) memory */
+ if (ctlr->num_chipselect > 1) {
+ config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET);
+ if (!spi->chip_select)
+ config_reg &= ~ZYNQ_QSPI_LCFG_U_PAGE;
+ else
+ config_reg |= ZYNQ_QSPI_LCFG_U_PAGE;
+
+ zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, config_reg);
+ }
+
/* Ground the line to assert the CS */
config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
if (assert)
@@ -673,9 +690,9 @@ static int zynq_qspi_probe(struct platform_device *pdev)
ret = of_property_read_u32(np, "num-cs",
&num_cs);
if (ret < 0) {
- ctlr->num_chipselect = ZYNQ_QSPI_DEFAULT_NUM_CS;
- } else if (num_cs > ZYNQ_QSPI_DEFAULT_NUM_CS) {
- dev_err(&pdev->dev, "anything but CS0 is not yet supported\n");
+ ctlr->num_chipselect = 1;
+ } else if (num_cs > ZYNQ_QSPI_MAX_NUM_CS) {
+ dev_err(&pdev->dev, "only 2 chip selects are available\n");
goto remove_master;
} else {
ctlr->num_chipselect = num_cs;
@@ -689,7 +706,7 @@ static int zynq_qspi_probe(struct platform_device *pdev)
ctlr->dev.of_node = np;
/* QSPI controller initializations */
- zynq_qspi_init_hw(xqspi);
+ zynq_qspi_init_hw(xqspi, ctlr->num_chipselect);
ret = devm_spi_register_controller(&pdev->dev, ctlr);
if (ret) {