Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac3e62b8 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branches 'spi/topic/octeon', 'spi/topic/omap2-mcspi',...

Merge remote-tracking branches 'spi/topic/octeon', 'spi/topic/omap2-mcspi', 'spi/topic/orion', 'spi/topic/pic32' and 'spi/topic/pic32-sqi' into spi-next
Loading
Loading
Loading
Loading
+48 −1
Original line number Diff line number Diff line
@@ -8,7 +8,15 @@ Required properties:
    - "marvell,armada-380-spi", for the Armada 38x SoCs
    - "marvell,armada-390-spi", for the Armada 39x SoCs
    - "marvell,armada-xp-spi", for the Armada XP SoCs
- reg : offset and length of the register set for the device
- reg : offset and length of the register set for the device.
	This property can optionally have additional entries to configure
	the SPI direct access mode that some of the Marvell SoCs support
	additionally to the normal indirect access (PIO) mode. The values
	for the MBus "target" and "attribute" are defined in the Marvell
	SoC "Functional Specifications" Manual in the chapter "Marvell
	Core Processor Address Decoding".
	The eight register sets following the control registers refer to
	chip-select lines 0 through 7 respectively.
- cell-index : Which of multiple SPI controllers is this.
Optional properties:
- interrupts : Is currently not used.
@@ -23,3 +31,42 @@ Example:
	       interrupts = <23>;
	       status = "disabled";
       };

Example with SPI direct mode support (optionally):
	spi0: spi@10600 {
		compatible = "marvell,orion-spi";
		#address-cells = <1>;
		#size-cells = <0>;
		cell-index = <0>;
		reg = <MBUS_ID(0xf0, 0x01) 0x10600 0x28>, /* control */
		      <MBUS_ID(0x01, 0x1e) 0 0xffffffff>, /* CS0 */
		      <MBUS_ID(0x01, 0x5e) 0 0xffffffff>, /* CS1 */
		      <MBUS_ID(0x01, 0x9e) 0 0xffffffff>, /* CS2 */
		      <MBUS_ID(0x01, 0xde) 0 0xffffffff>, /* CS3 */
		      <MBUS_ID(0x01, 0x1f) 0 0xffffffff>, /* CS4 */
		      <MBUS_ID(0x01, 0x5f) 0 0xffffffff>, /* CS5 */
		      <MBUS_ID(0x01, 0x9f) 0 0xffffffff>, /* CS6 */
		      <MBUS_ID(0x01, 0xdf) 0 0xffffffff>; /* CS7 */
		interrupts = <23>;
		status = "disabled";
	};

To enable the direct mode, the board specific 'ranges' property in the
'soc' node needs to add the entries for the desired SPI controllers
and its chip-selects that are used in the direct mode instead of PIO
mode. Here an example for this (SPI controller 0, device 1 and SPI
controller 1, device 2 are used in direct mode. All other SPI device
are used in the default indirect (PIO) mode):
	soc {
		/*
		 * Enable the SPI direct access by configuring an entry
		 * here in the board-specific ranges property
		 */
		ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000>,	/* internal regs */
			 <MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000>,	/* BootROM       */
			 <MBUS_ID(0x01, 0x5e) 0 0 0xf1100000 0x10000>,	/* SPI0-DEV1 */
			 <MBUS_ID(0x01, 0x9a) 0 0 0xf1110000 0x10000>;	/* SPI1-DEV2 */

For further information on the MBus bindings, please see the MBus
DT documentation:
Documentation/devicetree/bindings/bus/mvebu-mbus.txt
+1 −0
Original line number Diff line number Diff line
@@ -411,6 +411,7 @@ config SPI_OMAP24XX
	tristate "McSPI driver for OMAP"
	depends on HAS_DMA
	depends on ARCH_OMAP2PLUS || COMPILE_TEST
	select SG_SPLIT
	help
	  SPI master controller for OMAP24XX and later Multichannel SPI
	  (McSPI) modules.
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
obj-$(CONFIG_SPI_MXS)			+= spi-mxs.o
obj-$(CONFIG_SPI_NUC900)		+= spi-nuc900.o
obj-$(CONFIG_SPI_OC_TINY)		+= spi-oc-tiny.o
spi-octeon-objs				:= spi-cavium.o spi-cavium-octeon.o
obj-$(CONFIG_SPI_OCTEON)		+= spi-octeon.o
obj-$(CONFIG_SPI_OMAP_UWIRE)		+= spi-omap-uwire.o
obj-$(CONFIG_SPI_OMAP_100K)		+= spi-omap-100k.o
+104 −0
Original line number Diff line number Diff line
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2011, 2012 Cavium, Inc.
 */

#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>

#include <asm/octeon/octeon.h>

#include "spi-cavium.h"

static int octeon_spi_probe(struct platform_device *pdev)
{
	struct resource *res_mem;
	void __iomem *reg_base;
	struct spi_master *master;
	struct octeon_spi *p;
	int err = -ENOENT;

	master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi));
	if (!master)
		return -ENOMEM;
	p = spi_master_get_devdata(master);
	platform_set_drvdata(pdev, master);

	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	reg_base = devm_ioremap_resource(&pdev->dev, res_mem);
	if (IS_ERR(reg_base)) {
		err = PTR_ERR(reg_base);
		goto fail;
	}

	p->register_base = reg_base;
	p->sys_freq = octeon_get_io_clock_rate();

	p->regs.config = 0;
	p->regs.status = 0x08;
	p->regs.tx = 0x10;
	p->regs.data = 0x80;

	master->num_chipselect = 4;
	master->mode_bits = SPI_CPHA |
			    SPI_CPOL |
			    SPI_CS_HIGH |
			    SPI_LSB_FIRST |
			    SPI_3WIRE;

	master->transfer_one_message = octeon_spi_transfer_one_message;
	master->bits_per_word_mask = SPI_BPW_MASK(8);
	master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;

	master->dev.of_node = pdev->dev.of_node;
	err = devm_spi_register_master(&pdev->dev, master);
	if (err) {
		dev_err(&pdev->dev, "register master failed: %d\n", err);
		goto fail;
	}

	dev_info(&pdev->dev, "OCTEON SPI bus driver\n");

	return 0;
fail:
	spi_master_put(master);
	return err;
}

static int octeon_spi_remove(struct platform_device *pdev)
{
	struct spi_master *master = platform_get_drvdata(pdev);
	struct octeon_spi *p = spi_master_get_devdata(master);

	/* Clear the CSENA* and put everything in a known state. */
	writeq(0, p->register_base + OCTEON_SPI_CFG(p));

	return 0;
}

static const struct of_device_id octeon_spi_match[] = {
	{ .compatible = "cavium,octeon-3010-spi", },
	{},
};
MODULE_DEVICE_TABLE(of, octeon_spi_match);

static struct platform_driver octeon_spi_driver = {
	.driver = {
		.name		= "spi-octeon",
		.of_match_table = octeon_spi_match,
	},
	.probe		= octeon_spi_probe,
	.remove		= octeon_spi_remove,
};

module_platform_driver(octeon_spi_driver);

MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver");
MODULE_AUTHOR("David Daney");
MODULE_LICENSE("GPL");
+151 −0
Original line number Diff line number Diff line
@@ -6,31 +6,12 @@
 * Copyright (C) 2011, 2012 Cavium, Inc.
 */

#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>

#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-mpi-defs.h>

#define OCTEON_SPI_CFG 0
#define OCTEON_SPI_STS 0x08
#define OCTEON_SPI_TX 0x10
#define OCTEON_SPI_DAT0 0x80

#define OCTEON_SPI_MAX_BYTES 9

#define OCTEON_SPI_MAX_CLOCK_HZ 16000000

struct octeon_spi {
	u64 register_base;
	u64 last_cfg;
	u64 cs_enax;
};
#include "spi-cavium.h"

static void octeon_spi_wait_ready(struct octeon_spi *p)
{
@@ -40,7 +21,7 @@ static void octeon_spi_wait_ready(struct octeon_spi *p)
	do {
		if (loops++)
			__delay(500);
		mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS);
		mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p));
	} while (mpi_sts.s.busy);
}

@@ -53,7 +34,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
	union cvmx_mpi_cfg mpi_cfg;
	union cvmx_mpi_tx mpi_tx;
	unsigned int clkdiv;
	unsigned int speed_hz;
	int mode;
	bool cpha, cpol;
	const u8 *tx_buf;
@@ -65,9 +45,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
	cpha = mode & SPI_CPHA;
	cpol = mode & SPI_CPOL;

	speed_hz = xfer->speed_hz;

	clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz);
	clkdiv = p->sys_freq / (2 * xfer->speed_hz);

	mpi_cfg.u64 = 0;

@@ -85,7 +63,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,

	if (mpi_cfg.u64 != p->last_cfg) {
		p->last_cfg = mpi_cfg.u64;
		cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64);
		writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p));
	}
	tx_buf = xfer->tx_buf;
	rx_buf = xfer->rx_buf;
@@ -97,19 +75,19 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
				d = *tx_buf++;
			else
				d = 0;
			cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
			writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
		}
		mpi_tx.u64 = 0;
		mpi_tx.s.csid = spi->chip_select;
		mpi_tx.s.leavecs = 1;
		mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
		mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
		cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64);
		writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));

		octeon_spi_wait_ready(p);
		if (rx_buf)
			for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
				u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i));
				u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
				*rx_buf++ = (u8)v;
			}
		len -= OCTEON_SPI_MAX_BYTES;
@@ -121,7 +99,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
			d = *tx_buf++;
		else
			d = 0;
		cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
		writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
	}

	mpi_tx.u64 = 0;
@@ -132,12 +110,12 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
		mpi_tx.s.leavecs = !xfer->cs_change;
	mpi_tx.s.txnum = tx_buf ? len : 0;
	mpi_tx.s.totnum = len;
	cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64);
	writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));

	octeon_spi_wait_ready(p);
	if (rx_buf)
		for (i = 0; i < len; i++) {
			u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i));
			u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
			*rx_buf++ = (u8)v;
		}

@@ -147,7 +125,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
	return xfer->len;
}

static int octeon_spi_transfer_one_message(struct spi_master *master,
int octeon_spi_transfer_one_message(struct spi_master *master,
				    struct spi_message *msg)
{
	struct octeon_spi *p = spi_master_get_devdata(master);
@@ -171,85 +149,3 @@ static int octeon_spi_transfer_one_message(struct spi_master *master,
	spi_finalize_current_message(master);
	return status;
}

static int octeon_spi_probe(struct platform_device *pdev)
{
	struct resource *res_mem;
	void __iomem *reg_base;
	struct spi_master *master;
	struct octeon_spi *p;
	int err = -ENOENT;

	master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi));
	if (!master)
		return -ENOMEM;
	p = spi_master_get_devdata(master);
	platform_set_drvdata(pdev, master);

	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	reg_base = devm_ioremap_resource(&pdev->dev, res_mem);
	if (IS_ERR(reg_base)) {
		err = PTR_ERR(reg_base);
		goto fail;
	}

	p->register_base = (u64)reg_base;

	master->num_chipselect = 4;
	master->mode_bits = SPI_CPHA |
			    SPI_CPOL |
			    SPI_CS_HIGH |
			    SPI_LSB_FIRST |
			    SPI_3WIRE;

	master->transfer_one_message = octeon_spi_transfer_one_message;
	master->bits_per_word_mask = SPI_BPW_MASK(8);
	master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;

	master->dev.of_node = pdev->dev.of_node;
	err = devm_spi_register_master(&pdev->dev, master);
	if (err) {
		dev_err(&pdev->dev, "register master failed: %d\n", err);
		goto fail;
	}

	dev_info(&pdev->dev, "OCTEON SPI bus driver\n");

	return 0;
fail:
	spi_master_put(master);
	return err;
}

static int octeon_spi_remove(struct platform_device *pdev)
{
	struct spi_master *master = platform_get_drvdata(pdev);
	struct octeon_spi *p = spi_master_get_devdata(master);
	u64 register_base = p->register_base;

	/* Clear the CSENA* and put everything in a known state. */
	cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0);

	return 0;
}

static const struct of_device_id octeon_spi_match[] = {
	{ .compatible = "cavium,octeon-3010-spi", },
	{},
};
MODULE_DEVICE_TABLE(of, octeon_spi_match);

static struct platform_driver octeon_spi_driver = {
	.driver = {
		.name		= "spi-octeon",
		.of_match_table = octeon_spi_match,
	},
	.probe		= octeon_spi_probe,
	.remove		= octeon_spi_remove,
};

module_platform_driver(octeon_spi_driver);

MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver");
MODULE_AUTHOR("David Daney");
MODULE_LICENSE("GPL");
Loading