|
|
@@ -18,6 +18,7 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/of.h>
|
|
|
+#include <linux/of_address.h>
|
|
|
#include <linux/of_device.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/sizes.h>
|
|
|
@@ -43,6 +44,9 @@
|
|
|
#define ORION_SPI_INT_CAUSE_REG 0x10
|
|
|
#define ORION_SPI_TIMING_PARAMS_REG 0x18
|
|
|
|
|
|
+/* Register for the "Direct Mode" */
|
|
|
+#define SPI_DIRECT_WRITE_CONFIG_REG 0x20
|
|
|
+
|
|
|
#define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6)
|
|
|
#define ORION_SPI_TMISO_SAMPLE_1 (1 << 6)
|
|
|
#define ORION_SPI_TMISO_SAMPLE_2 (2 << 6)
|
|
|
@@ -78,11 +82,18 @@ struct orion_spi_dev {
|
|
|
bool is_errata_50mhz_ac;
|
|
|
};
|
|
|
|
|
|
+struct orion_direct_acc {
|
|
|
+ void __iomem *vaddr;
|
|
|
+ u32 size;
|
|
|
+};
|
|
|
+
|
|
|
struct orion_spi {
|
|
|
struct spi_master *master;
|
|
|
void __iomem *base;
|
|
|
struct clk *clk;
|
|
|
const struct orion_spi_dev *devdata;
|
|
|
+
|
|
|
+ struct orion_direct_acc direct_access[ORION_NUM_CHIPSELECTS];
|
|
|
};
|
|
|
|
|
|
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
|
|
@@ -372,10 +383,39 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
|
|
{
|
|
|
unsigned int count;
|
|
|
int word_len;
|
|
|
+ struct orion_spi *orion_spi;
|
|
|
+ int cs = spi->chip_select;
|
|
|
|
|
|
word_len = spi->bits_per_word;
|
|
|
count = xfer->len;
|
|
|
|
|
|
+ orion_spi = spi_master_get_devdata(spi->master);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Use SPI direct write mode if base address is available. Otherwise
|
|
|
+ * fall back to PIO mode for this transfer.
|
|
|
+ */
|
|
|
+ if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) &&
|
|
|
+ (word_len == 8)) {
|
|
|
+ unsigned int cnt = count / 4;
|
|
|
+ unsigned int rem = count % 4;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Send the TX-data to the SPI device via the direct
|
|
|
+ * mapped address window
|
|
|
+ */
|
|
|
+ iowrite32_rep(orion_spi->direct_access[cs].vaddr,
|
|
|
+ xfer->tx_buf, cnt);
|
|
|
+ if (rem) {
|
|
|
+ u32 *buf = (u32 *)xfer->tx_buf;
|
|
|
+
|
|
|
+ iowrite8_rep(orion_spi->direct_access[cs].vaddr,
|
|
|
+ &buf[cnt], rem);
|
|
|
+ }
|
|
|
+
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
if (word_len == 8) {
|
|
|
const u8 *tx = xfer->tx_buf;
|
|
|
u8 *rx = xfer->rx_buf;
|
|
|
@@ -425,6 +465,10 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
|
|
|
{
|
|
|
/* Verify that the CS is deasserted */
|
|
|
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
|
|
|
+
|
|
|
+ /* Don't deassert CS between the direct mapped SPI transfers */
|
|
|
+ writel(0, spi_reg(orion_spi, SPI_DIRECT_WRITE_CONFIG_REG));
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -504,6 +548,7 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|
|
struct resource *r;
|
|
|
unsigned long tclk_hz;
|
|
|
int status = 0;
|
|
|
+ struct device_node *np;
|
|
|
|
|
|
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
|
|
|
if (master == NULL) {
|
|
|
@@ -576,6 +621,49 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|
|
goto out_rel_clk;
|
|
|
}
|
|
|
|
|
|
+ /* Scan all SPI devices of this controller for direct mapped devices */
|
|
|
+ for_each_available_child_of_node(pdev->dev.of_node, np) {
|
|
|
+ u32 cs;
|
|
|
+
|
|
|
+ /* Get chip-select number from the "reg" property */
|
|
|
+ status = of_property_read_u32(np, "reg", &cs);
|
|
|
+ if (status) {
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "%s has no valid 'reg' property (%d)\n",
|
|
|
+ np->full_name, status);
|
|
|
+ status = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if an address is configured for this SPI device. If
|
|
|
+ * not, the MBus mapping via the 'ranges' property in the 'soc'
|
|
|
+ * node is not configured and this device should not use the
|
|
|
+ * direct mode. In this case, just continue with the next
|
|
|
+ * device.
|
|
|
+ */
|
|
|
+ status = of_address_to_resource(pdev->dev.of_node, cs + 1, r);
|
|
|
+ if (status)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Only map one page for direct access. This is enough for the
|
|
|
+ * simple TX transfer which only writes to the first word.
|
|
|
+ * This needs to get extended for the direct SPI-NOR / SPI-NAND
|
|
|
+ * support, once this gets implemented.
|
|
|
+ */
|
|
|
+ spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev,
|
|
|
+ r->start,
|
|
|
+ PAGE_SIZE);
|
|
|
+ if (IS_ERR(spi->direct_access[cs].vaddr)) {
|
|
|
+ status = PTR_ERR(spi->direct_access[cs].vaddr);
|
|
|
+ goto out_rel_clk;
|
|
|
+ }
|
|
|
+ spi->direct_access[cs].size = PAGE_SIZE;
|
|
|
+
|
|
|
+ dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
|
|
|
+ }
|
|
|
+
|
|
|
pm_runtime_set_active(&pdev->dev);
|
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|