spi: Updates for v4.3
A few core tweaks this time together with the usual collection of driver specific updates and fixes plus a larger than average selection of new device support: - Fix DMA mapping of unaligned vmalloc() buffers. - Statistics tracking transfer volumes exposed via sysfs. - New drivers for Freescale MPC5125, Intel Sunrise Point, Mediatek SoCs, and Netlogic XLP SoCs. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJV5F3EAAoJECTWi3JdVIfQ45kH/isvhUPuvaZS8zQggQ95xIjy 7uZMQzf2JHwmeFiYsqFuWdf6IVgGmg68cQ4ZIzyXk1quEJkOLNCrlOvn4Qv7z6lJ fqr5qbU+BWrtv0IjN5szIx7BR+ZWQ9LHdZI651A1XbCPvU8H1PZL+PfWDK6pMHCM tqCLUVgnwRYNT0ELmgl7Up8SWKMbs98Wit2dQzNvwrfEZ1GHTMk3ChYAABfMkKIU wbuug6+cv5+VdhV73nX29BZXJj7+EyRATQknADQT3nugcXkMysi0IvLZIrSFEJTp iBApQsO3EaOVUR5/hN6zHpS8lqZdEkQvlKRuodnW31dXLrIw7TyVDT1wWknRZeU= =Jykk -----END PGP SIGNATURE----- Merge tag 'spi-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "A few core tweaks this time together with the usual collection of driver specific updates and fixes plus a larger than average selection of new device support: - fix DMA mapping of unaligned vmalloc() buffers - statistics tracking transfer volumes exposed via sysfs - new drivers for Freescale MPC5125, Intel Sunrise Point, Mediatek SoCs, and Netlogic XLP SoCs" * tag 'spi-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (66 commits) spi: sh-msiof: Fix FIFO size to 64 word from 256 word spi: fsl-(e)spi: Fix checking return value of devm_ioremap_resource spi: Add DT bindings documentation for Netlogic XLP SPI controller spi/xlp: SPI controller driver for Netlogic XLP SoCs spi: fsl-espi: add runtime PM spi: fsl-(e)spi: simplify cleanup code spi: fsl-(e)spi: migrate to using devm_ functions to simplify cleanup spi: mediatek: fix SPI_CMD_PAUSE_IE macro error spi: check bits_per_word in spi_setup spi: mediatek: replace *_time name spi: mediatek: add PM clk_prepare_enable fail flow spi: mediatek: replace int with u32, delete TAB and define MTK_SPI_PAUSE_INT_STATUS marco spi: mediatek: add linux/io.h include file spi/bcm63xx-hsspi: add support for dual spi read/write spi: dw: Allow interface drivers to limit data I/O to word sizes dt: snps,dw-apb-ssi: Document new I/O data register width property spi: Fall back to master maximum speed if no slave speed specified spi: mediatek: use BIT() to instead of SPI_CMD_*_OFFSET spi: medaitek: revise quirks compatibility style spi: mediatek: fix spi incorrect endian usage ...
This commit is contained in:
commit
e5aeced6bc
|
@ -6,14 +6,14 @@ PSC in UART mode
|
|||
For PSC in UART mode the needed PSC serial devices
|
||||
are specified by fsl,mpc5121-psc-uart nodes in the
|
||||
fsl,mpc5121-immr SoC node. Additionally the PSC FIFO
|
||||
Controller node fsl,mpc5121-psc-fifo is requered there:
|
||||
Controller node fsl,mpc5121-psc-fifo is required there:
|
||||
|
||||
fsl,mpc5121-psc-uart nodes
|
||||
fsl,mpc512x-psc-uart nodes
|
||||
--------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : Should contain "fsl,mpc5121-psc-uart" and "fsl,mpc5121-psc"
|
||||
- cell-index : Index of the PSC in hardware
|
||||
- compatible : Should contain "fsl,<soc>-psc-uart" and "fsl,<soc>-psc"
|
||||
Supported <soc>s: mpc5121, mpc5125
|
||||
- reg : Offset and length of the register set for the PSC device
|
||||
- interrupts : <a b> where a is the interrupt number of the
|
||||
PSC FIFO Controller and b is a field that represents an
|
||||
|
@ -25,12 +25,21 @@ Recommended properties :
|
|||
- fsl,rx-fifo-size : the size of the RX fifo slice (a multiple of 4)
|
||||
- fsl,tx-fifo-size : the size of the TX fifo slice (a multiple of 4)
|
||||
|
||||
PSC in SPI mode
|
||||
---------------
|
||||
|
||||
fsl,mpc5121-psc-fifo node
|
||||
Similar to the UART mode a PSC can be operated in SPI mode. The compatible used
|
||||
for that is fsl,mpc5121-psc-spi. It requires a fsl,mpc5121-psc-fifo as well.
|
||||
The required and recommended properties are identical to the
|
||||
fsl,mpc5121-psc-uart nodes, just use spi instead of uart in the compatible
|
||||
string.
|
||||
|
||||
fsl,mpc512x-psc-fifo node
|
||||
-------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "fsl,mpc5121-psc-fifo"
|
||||
- compatible : Should be "fsl,<soc>-psc-fifo"
|
||||
Supported <soc>s: mpc5121, mpc5125
|
||||
- reg : Offset and length of the register set for the PSC
|
||||
FIFO Controller
|
||||
- interrupts : <a b> where a is the interrupt number of the
|
||||
|
@ -39,6 +48,9 @@ Required properties :
|
|||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
|
||||
Recommended properties :
|
||||
- clocks : specifies the clock needed to operate the fifo controller
|
||||
- clock-names : name(s) for the clock(s) listed in clocks
|
||||
|
||||
Example for a board using PSC0 and PSC1 devices in serial mode:
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ Required properties:
|
|||
Optional properties:
|
||||
- cs-gpios : Specifies the gpio pis to be used for chipselects.
|
||||
- num-cs : The number of chipselects. If omitted, this will default to 4.
|
||||
- reg-io-width : The I/O register width (in bytes) implemented by this
|
||||
device. Supported values are 2 or 4 (the default).
|
||||
|
||||
Child nodes as per the generic SPI binding.
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ Required properties:
|
|||
- compatible:
|
||||
- "ti,dm6441-spi" for SPI used similar to that on DM644x SoC family
|
||||
- "ti,da830-spi" for SPI used similar to that on DA8xx SoC family
|
||||
- "ti,keystone-spi" for SPI used similar to that on Keystone2 SoC
|
||||
family
|
||||
- reg: Offset and length of SPI controller register space
|
||||
- num-cs: Number of chip selects. This includes internal as well as
|
||||
GPIO chip selects.
|
||||
|
|
|
@ -21,6 +21,7 @@ Required properties:
|
|||
Optional properties:
|
||||
- img,supports-quad-mode: Should be set if the interface supports quad mode
|
||||
SPI transfers.
|
||||
- spfi-max-frequency: Maximum speed supported by the spfi block.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
Binding for MTK SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following.
|
||||
- mediatek,mt8173-spi: for mt8173 platforms
|
||||
- mediatek,mt8135-spi: for mt8135 platforms
|
||||
- mediatek,mt6589-spi: for mt6589 platforms
|
||||
|
||||
- #address-cells: should be 1.
|
||||
|
||||
- #size-cells: should be 0.
|
||||
|
||||
- reg: Address and length of the register set for the device
|
||||
|
||||
- interrupts: Should contain spi interrupt
|
||||
|
||||
- clocks: phandles to input clocks.
|
||||
The first should be <&topckgen CLK_TOP_SPI_SEL>.
|
||||
The second should be one of the following.
|
||||
- <&clk26m>: specify parent clock 26MHZ.
|
||||
- <&topckgen CLK_TOP_SYSPLL3_D2>: specify parent clock 109MHZ.
|
||||
It's the default one.
|
||||
- <&topckgen CLK_TOP_SYSPLL4_D2>: specify parent clock 78MHZ.
|
||||
- <&topckgen CLK_TOP_UNIVPLL2_D4>: specify parent clock 104MHZ.
|
||||
- <&topckgen CLK_TOP_UNIVPLL1_D8>: specify parent clock 78MHZ.
|
||||
|
||||
- clock-names: shall be "spi-clk" for the controller clock, and
|
||||
"parent-clk" for the parent clock.
|
||||
|
||||
Optional properties:
|
||||
- mediatek,pad-select: specify which pins group(ck/mi/mo/cs) spi
|
||||
controller used, this value should be 0~3, only required for MT8173.
|
||||
0: specify GPIO69,70,71,72 for spi pins.
|
||||
1: specify GPIO102,103,104,105 for spi pins.
|
||||
2: specify GPIO128,129,130,131 for spi pins.
|
||||
3: specify GPIO5,6,7,8 for spi pins.
|
||||
|
||||
Example:
|
||||
|
||||
- SoC Specific Portion:
|
||||
spi: spi@1100a000 {
|
||||
compatible = "mediatek,mt8173-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0 0x1100a000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&topckgen CLK_TOP_SPI_SEL>, <&topckgen CLK_TOP_SYSPLL3_D2>;
|
||||
clock-names = "spi-clk", "parent-clk";
|
||||
mediatek,pad-select = <0>;
|
||||
status = "disabled";
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
SPI Master controller for Netlogic XLP MIPS64 SOCs
|
||||
==================================================
|
||||
|
||||
Currently this SPI controller driver is supported for the following
|
||||
Netlogic XLP SoCs:
|
||||
XLP832, XLP316, XLP208, XLP980, XLP532
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "netlogic,xlp832-spi".
|
||||
- #address-cells : Number of cells required to define a chip select address
|
||||
on the SPI bus.
|
||||
- #size-cells : Should be zero.
|
||||
- reg : Should contain register location and length.
|
||||
- clocks : Phandle of the spi clock
|
||||
- interrupts : Interrupt number used by this controller.
|
||||
- interrupt-parent : Phandle of the parent interrupt controller.
|
||||
|
||||
SPI slave nodes must be children of the SPI master node and can contain
|
||||
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
|
||||
Example:
|
||||
|
||||
spi: xlp_spi@3a100 {
|
||||
compatible = "netlogic,xlp832-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0 0x3a100 0x100>;
|
||||
clocks = <&spi_clk>;
|
||||
interrupts = <34>;
|
||||
interrupt-parent = <&pic>;
|
||||
|
||||
spi_nor@1 {
|
||||
compatible = "spansion,s25sl12801";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <1>; /* Chip Select */
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
};
|
|
@ -1010,11 +1010,13 @@ static struct davinci_spi_platform_data da8xx_spi_pdata[] = {
|
|||
.version = SPI_VERSION_2,
|
||||
.intr_line = 1,
|
||||
.dma_event_q = EVENTQ_0,
|
||||
.prescaler_limit = 2,
|
||||
},
|
||||
[1] = {
|
||||
.version = SPI_VERSION_2,
|
||||
.intr_line = 1,
|
||||
.dma_event_q = EVENTQ_0,
|
||||
.prescaler_limit = 2,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -411,6 +411,7 @@ static struct davinci_spi_platform_data dm355_spi0_pdata = {
|
|||
.num_chipselect = 2,
|
||||
.cshold_bug = true,
|
||||
.dma_event_q = EVENTQ_1,
|
||||
.prescaler_limit = 1,
|
||||
};
|
||||
static struct platform_device dm355_spi0_device = {
|
||||
.name = "spi_davinci",
|
||||
|
|
|
@ -646,6 +646,7 @@ static struct davinci_spi_platform_data dm365_spi0_pdata = {
|
|||
.version = SPI_VERSION_1,
|
||||
.num_chipselect = 2,
|
||||
.dma_event_q = EVENTQ_3,
|
||||
.prescaler_limit = 1,
|
||||
};
|
||||
|
||||
static struct resource dm365_spi0_resources[] = {
|
||||
|
|
|
@ -150,7 +150,10 @@
|
|||
|
||||
/* Structure of the hardware registers */
|
||||
struct mpc52xx_psc {
|
||||
u8 mode; /* PSC + 0x00 */
|
||||
union {
|
||||
u8 mode; /* PSC + 0x00 */
|
||||
u8 mr2;
|
||||
};
|
||||
u8 reserved0[3];
|
||||
union { /* PSC + 0x04 */
|
||||
u16 status;
|
||||
|
|
|
@ -326,6 +326,15 @@ config SPI_MESON_SPIFC
|
|||
This enables master mode support for the SPIFC (SPI flash
|
||||
controller) available in Amlogic Meson SoCs.
|
||||
|
||||
config SPI_MT65XX
|
||||
tristate "MediaTek SPI controller"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
help
|
||||
This selects the MediaTek(R) SPI bus driver.
|
||||
If you want to use MediaTek(R) SPI interface,
|
||||
say Y or M here.If you are not sure, say N.
|
||||
SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
|
||||
|
||||
config SPI_OC_TINY
|
||||
tristate "OpenCores tiny SPI"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
|
@ -598,6 +607,17 @@ config SPI_XILINX
|
|||
|
||||
Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
|
||||
|
||||
config SPI_XLP
|
||||
tristate "Netlogic XLP SPI controller driver"
|
||||
depends on CPU_XLP || COMPILE_TEST
|
||||
help
|
||||
Enable support for the SPI controller on the Netlogic XLP SoCs.
|
||||
Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, XLP9XX
|
||||
and XLP5XX.
|
||||
|
||||
If you have a Netlogic XLP platform say Y here.
|
||||
If unsure, say N.
|
||||
|
||||
config SPI_XTENSA_XTFPGA
|
||||
tristate "Xtensa SPI controller for xtfpga"
|
||||
depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST
|
||||
|
|
|
@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
|
|||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
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
|
||||
|
@ -88,5 +89,6 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
|||
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
||||
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
||||
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
|
||||
obj-$(CONFIG_SPI_XLP) += spi-xlp.o
|
||||
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
|
||||
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <linux/platform_data/dma-atmel.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
|
|
@ -480,7 +480,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
|
|||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr,
|
||||
u32 cs,
|
||||
unsigned long xfer_time_us)
|
||||
unsigned long long xfer_time_us)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
unsigned long timeout;
|
||||
|
@ -531,7 +531,8 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
|
|||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
unsigned long spi_hz, clk_hz, cdiv;
|
||||
unsigned long spi_used_hz, xfer_time_us;
|
||||
unsigned long spi_used_hz;
|
||||
unsigned long long xfer_time_us;
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* set clock */
|
||||
|
@ -553,13 +554,11 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
|
|||
spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
|
||||
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
||||
|
||||
/* handle all the modes */
|
||||
/* handle all the 3-wire mode */
|
||||
if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf))
|
||||
cs |= BCM2835_SPI_CS_REN;
|
||||
if (spi->mode & SPI_CPOL)
|
||||
cs |= BCM2835_SPI_CS_CPOL;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cs |= BCM2835_SPI_CS_CPHA;
|
||||
else
|
||||
cs &= ~BCM2835_SPI_CS_REN;
|
||||
|
||||
/* for gpio_cs set dummy CS so that no HW-CS get changed
|
||||
* we can not run this in bcm2835_spi_set_cs, as it does
|
||||
|
@ -575,9 +574,10 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
|
|||
bs->rx_len = tfr->len;
|
||||
|
||||
/* calculate the estimated time in us the transfer runs */
|
||||
xfer_time_us = tfr->len
|
||||
xfer_time_us = (unsigned long long)tfr->len
|
||||
* 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */
|
||||
* 1000000 / spi_used_hz;
|
||||
* 1000000;
|
||||
do_div(xfer_time_us, spi_used_hz);
|
||||
|
||||
/* for short requests run polling*/
|
||||
if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US)
|
||||
|
@ -592,6 +592,25 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
|
|||
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
|
||||
}
|
||||
|
||||
static int bcm2835_spi_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_device *spi = msg->spi;
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA);
|
||||
|
||||
if (spi->mode & SPI_CPOL)
|
||||
cs |= BCM2835_SPI_CS_CPOL;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cs |= BCM2835_SPI_CS_CPHA;
|
||||
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm2835_spi_handle_err(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
|
@ -739,6 +758,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||
master->set_cs = bcm2835_spi_set_cs;
|
||||
master->transfer_one = bcm2835_spi_transfer_one;
|
||||
master->handle_err = bcm2835_spi_handle_err;
|
||||
master->prepare_message = bcm2835_spi_prepare_message;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
bs = spi_master_get_devdata(master);
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200)
|
||||
|
||||
|
||||
#define HSSPI_OP_MULTIBIT BIT(11)
|
||||
#define HSSPI_OP_CODE_SHIFT 13
|
||||
#define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT)
|
||||
#define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT)
|
||||
|
@ -171,9 +172,12 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||
if (opcode != HSSPI_OP_READ)
|
||||
step_size -= HSSPI_OPCODE_LEN;
|
||||
|
||||
__raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT |
|
||||
2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT |
|
||||
2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff,
|
||||
if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) ||
|
||||
(opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL))
|
||||
opcode |= HSSPI_OP_MULTIBIT;
|
||||
|
||||
__raw_writel(1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT |
|
||||
1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT | 0xff,
|
||||
bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select));
|
||||
|
||||
while (pending > 0) {
|
||||
|
@ -374,7 +378,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
|||
master->num_chipselect = 8;
|
||||
master->setup = bcm63xx_hsspi_setup;
|
||||
master->transfer_one_message = bcm63xx_hsspi_transfer_one;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH |
|
||||
SPI_RX_DUAL | SPI_TX_DUAL;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ bitbang_txrx_be_cpha0(struct spi_device *spi,
|
|||
{
|
||||
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
|
||||
|
||||
bool oldbit = !(word & 1);
|
||||
u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
|
||||
/* clock starts at inactive polarity */
|
||||
for (word <<= (32 - bits); likely(bits); bits--) {
|
||||
|
||||
|
@ -81,7 +81,7 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
|
|||
{
|
||||
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
|
||||
|
||||
bool oldbit = !(word & (1 << 31));
|
||||
u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
|
||||
/* clock starts at inactive polarity */
|
||||
for (word <<= (32 - bits); likely(bits); bits--) {
|
||||
|
||||
|
|
|
@ -139,6 +139,8 @@ struct davinci_spi {
|
|||
u32 (*get_tx)(struct davinci_spi *);
|
||||
|
||||
u8 *bytes_per_word;
|
||||
|
||||
u8 prescaler_limit;
|
||||
};
|
||||
|
||||
static struct davinci_spi_config davinci_spi_default_cfg;
|
||||
|
@ -255,7 +257,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
* This function calculates the prescale value that generates a clock rate
|
||||
* less than or equal to the specified maximum.
|
||||
*
|
||||
* Returns: calculated prescale - 1 for easy programming into SPI registers
|
||||
* Returns: calculated prescale value for easy programming into SPI registers
|
||||
* or negative error number if valid prescalar cannot be updated.
|
||||
*/
|
||||
static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
|
||||
|
@ -263,12 +265,13 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
|
||||
/* Subtract 1 to match what will be programmed into SPI register. */
|
||||
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz) - 1;
|
||||
|
||||
if (ret < 1 || ret > 256)
|
||||
if (ret < dspi->prescaler_limit || ret > 255)
|
||||
return -EINVAL;
|
||||
|
||||
return ret - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -832,13 +835,40 @@ rx_dma_failed:
|
|||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
|
||||
/* OF SPI data structure */
|
||||
struct davinci_spi_of_data {
|
||||
u8 version;
|
||||
u8 prescaler_limit;
|
||||
};
|
||||
|
||||
static const struct davinci_spi_of_data dm6441_spi_data = {
|
||||
.version = SPI_VERSION_1,
|
||||
.prescaler_limit = 2,
|
||||
};
|
||||
|
||||
static const struct davinci_spi_of_data da830_spi_data = {
|
||||
.version = SPI_VERSION_2,
|
||||
.prescaler_limit = 2,
|
||||
};
|
||||
|
||||
static const struct davinci_spi_of_data keystone_spi_data = {
|
||||
.version = SPI_VERSION_1,
|
||||
.prescaler_limit = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id davinci_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,dm6441-spi",
|
||||
.data = &dm6441_spi_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,da830-spi",
|
||||
.data = (void *)SPI_VERSION_2,
|
||||
.data = &da830_spi_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,keystone-spi",
|
||||
.data = &keystone_spi_data,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
@ -857,21 +887,21 @@ static int spi_davinci_get_pdata(struct platform_device *pdev,
|
|||
struct davinci_spi *dspi)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct davinci_spi_of_data *spi_data;
|
||||
struct davinci_spi_platform_data *pdata;
|
||||
unsigned int num_cs, intr_line = 0;
|
||||
const struct of_device_id *match;
|
||||
|
||||
pdata = &dspi->pdata;
|
||||
|
||||
pdata->version = SPI_VERSION_1;
|
||||
match = of_match_device(davinci_spi_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
/* match data has the SPI version number for SPI_VERSION_2 */
|
||||
if (match->data == (void *)SPI_VERSION_2)
|
||||
pdata->version = SPI_VERSION_2;
|
||||
spi_data = (struct davinci_spi_of_data *)match->data;
|
||||
|
||||
pdata->version = spi_data->version;
|
||||
pdata->prescaler_limit = spi_data->prescaler_limit;
|
||||
/*
|
||||
* default num_cs is 1 and all chipsel are internal to the chip
|
||||
* indicated by chip_sel being NULL or cs_gpios being NULL or
|
||||
|
@ -991,7 +1021,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
|
||||
dspi->bitbang.chipselect = davinci_spi_chipselect;
|
||||
dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;
|
||||
|
||||
dspi->prescaler_limit = pdata->prescaler_limit;
|
||||
dspi->version = pdata->version;
|
||||
|
||||
dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP;
|
||||
|
|
|
@ -74,6 +74,9 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||
|
||||
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
||||
|
||||
of_property_read_u32(pdev->dev.of_node, "reg-io-width",
|
||||
&dws->reg_io_width);
|
||||
|
||||
num_cs = 4;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
|
|
|
@ -194,7 +194,7 @@ static void dw_writer(struct dw_spi *dws)
|
|||
else
|
||||
txw = *(u16 *)(dws->tx);
|
||||
}
|
||||
dw_writel(dws, DW_SPI_DR, txw);
|
||||
dw_write_io_reg(dws, DW_SPI_DR, txw);
|
||||
dws->tx += dws->n_bytes;
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ static void dw_reader(struct dw_spi *dws)
|
|||
u16 rxw;
|
||||
|
||||
while (max--) {
|
||||
rxw = dw_readl(dws, DW_SPI_DR);
|
||||
rxw = dw_read_io_reg(dws, DW_SPI_DR);
|
||||
/* Care rx only if the transfer's original "rx" is not null */
|
||||
if (dws->rx_end - dws->len) {
|
||||
if (dws->n_bytes == 1)
|
||||
|
|
|
@ -109,6 +109,7 @@ struct dw_spi {
|
|||
u32 fifo_len; /* depth of the FIFO buffer */
|
||||
u32 max_freq; /* max bus freq supported */
|
||||
|
||||
u32 reg_io_width; /* DR I/O width in bytes */
|
||||
u16 bus_num;
|
||||
u16 num_cs; /* supported slave numbers */
|
||||
|
||||
|
@ -145,11 +146,45 @@ static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
|
|||
return __raw_readl(dws->regs + offset);
|
||||
}
|
||||
|
||||
static inline u16 dw_readw(struct dw_spi *dws, u32 offset)
|
||||
{
|
||||
return __raw_readw(dws->regs + offset);
|
||||
}
|
||||
|
||||
static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
|
||||
{
|
||||
__raw_writel(val, dws->regs + offset);
|
||||
}
|
||||
|
||||
static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val)
|
||||
{
|
||||
__raw_writew(val, dws->regs + offset);
|
||||
}
|
||||
|
||||
static inline u32 dw_read_io_reg(struct dw_spi *dws, u32 offset)
|
||||
{
|
||||
switch (dws->reg_io_width) {
|
||||
case 2:
|
||||
return dw_readw(dws, offset);
|
||||
case 4:
|
||||
default:
|
||||
return dw_readl(dws, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dw_write_io_reg(struct dw_spi *dws, u32 offset, u32 val)
|
||||
{
|
||||
switch (dws->reg_io_width) {
|
||||
case 2:
|
||||
dw_writew(dws, offset, val);
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
dw_writel(dws, offset, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
|
||||
{
|
||||
dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
|
@ -85,6 +86,8 @@ struct fsl_espi_transfer {
|
|||
#define SPCOM_TRANLEN(x) ((x) << 0)
|
||||
#define SPCOM_TRANLEN_MAX 0xFFFF /* Max transaction length */
|
||||
|
||||
#define AUTOSUSPEND_TIMEOUT 2000
|
||||
|
||||
static void fsl_espi_change_mode(struct spi_device *spi)
|
||||
{
|
||||
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
|
||||
|
@ -485,6 +488,8 @@ static int fsl_espi_setup(struct spi_device *spi)
|
|||
mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
pm_runtime_get_sync(mpc8xxx_spi->dev);
|
||||
|
||||
hw_mode = cs->hw_mode; /* Save original settings */
|
||||
cs->hw_mode = mpc8xxx_spi_read_reg(
|
||||
®_base->csmode[spi->chip_select]);
|
||||
|
@ -507,6 +512,10 @@ static int fsl_espi_setup(struct spi_device *spi)
|
|||
mpc8xxx_spi_write_reg(®_base->mode, loop_mode);
|
||||
|
||||
retval = fsl_espi_setup_transfer(spi, NULL);
|
||||
|
||||
pm_runtime_mark_last_busy(mpc8xxx_spi->dev);
|
||||
pm_runtime_put_autosuspend(mpc8xxx_spi->dev);
|
||||
|
||||
if (retval < 0) {
|
||||
cs->hw_mode = hw_mode; /* Restore settings */
|
||||
return retval;
|
||||
|
@ -604,20 +613,14 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_espi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
iounmap(mspi->reg_base);
|
||||
}
|
||||
|
||||
static int fsl_espi_suspend(struct spi_master *master)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
u32 regval;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
regval = mpc8xxx_spi_read_reg(®_base->mode);
|
||||
regval &= ~SPMODE_ENABLE;
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
@ -625,21 +628,20 @@ static int fsl_espi_suspend(struct spi_master *master)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_espi_resume(struct spi_master *master)
|
||||
static int fsl_espi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
u32 regval;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
regval = mpc8xxx_spi_read_reg(®_base->mode);
|
||||
regval |= SPMODE_ENABLE;
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct spi_master * fsl_espi_probe(struct device *dev,
|
||||
struct resource *mem, unsigned int irq)
|
||||
|
@ -667,25 +669,23 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
|
|||
master->setup = fsl_espi_setup;
|
||||
master->cleanup = fsl_espi_cleanup;
|
||||
master->transfer_one_message = fsl_espi_do_one_msg;
|
||||
master->prepare_transfer_hardware = fsl_espi_resume;
|
||||
master->unprepare_transfer_hardware = fsl_espi_suspend;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->spi_remove = fsl_espi_remove;
|
||||
|
||||
mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
|
||||
if (!mpc8xxx_spi->reg_base) {
|
||||
ret = -ENOMEM;
|
||||
mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(mpc8xxx_spi->reg_base)) {
|
||||
ret = PTR_ERR(mpc8xxx_spi->reg_base);
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
/* Register for SPI Interrupt */
|
||||
ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
|
||||
ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_espi_irq,
|
||||
0, "fsl_espi", mpc8xxx_spi);
|
||||
if (ret)
|
||||
goto free_irq;
|
||||
goto err_probe;
|
||||
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
|
||||
mpc8xxx_spi->rx_shift = 16;
|
||||
|
@ -731,18 +731,27 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
|
|||
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
ret = devm_spi_register_master(dev, master);
|
||||
if (ret < 0)
|
||||
goto unreg_master;
|
||||
goto err_pm;
|
||||
|
||||
dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return master;
|
||||
|
||||
unreg_master:
|
||||
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
|
||||
free_irq:
|
||||
iounmap(mpc8xxx_spi->reg_base);
|
||||
err_pm:
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
err_probe:
|
||||
spi_master_put(master);
|
||||
err:
|
||||
|
@ -809,7 +818,9 @@ err:
|
|||
|
||||
static int of_fsl_espi_remove(struct platform_device *dev)
|
||||
{
|
||||
return mpc8xxx_spi_remove(&dev->dev);
|
||||
pm_runtime_disable(&dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -824,7 +835,11 @@ static int of_fsl_espi_suspend(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return fsl_espi_suspend(master);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_fsl_espi_resume(struct device *dev)
|
||||
|
@ -834,7 +849,7 @@ static int of_fsl_espi_resume(struct device *dev)
|
|||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
u32 regval;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
@ -854,11 +869,17 @@ static int of_fsl_espi_resume(struct device *dev)
|
|||
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return spi_master_resume(master);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops espi_pm = {
|
||||
SET_RUNTIME_PM_OPS(fsl_espi_runtime_suspend,
|
||||
fsl_espi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume)
|
||||
};
|
||||
|
||||
|
|
|
@ -114,25 +114,6 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mpc8xxx_spi_probe);
|
||||
|
||||
int mpc8xxx_spi_remove(struct device *dev)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct spi_master *master;
|
||||
|
||||
master = dev_get_drvdata(dev);
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
|
||||
|
||||
if (mpc8xxx_spi->spi_remove)
|
||||
mpc8xxx_spi->spi_remove(mpc8xxx_spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mpc8xxx_spi_remove);
|
||||
|
||||
int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct device *dev = &ofdev->dev;
|
||||
|
|
|
@ -54,9 +54,6 @@ struct mpc8xxx_spi {
|
|||
void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
|
||||
u32(*get_tx) (struct mpc8xxx_spi *);
|
||||
|
||||
/* hooks for different controller driver */
|
||||
void (*spi_remove) (struct mpc8xxx_spi *mspi);
|
||||
|
||||
unsigned int count;
|
||||
unsigned int irq;
|
||||
|
||||
|
|
|
@ -559,12 +559,6 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
iounmap(mspi->reg_base);
|
||||
fsl_spi_cpm_free(mspi);
|
||||
}
|
||||
|
||||
static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
|
@ -631,7 +625,6 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
|||
master->transfer_one_message = fsl_spi_do_one_msg;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->spi_remove = fsl_spi_remove;
|
||||
mpc8xxx_spi->max_bits_per_word = 32;
|
||||
mpc8xxx_spi->type = fsl_spi_get_type(dev);
|
||||
|
||||
|
@ -639,10 +632,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
|||
if (ret)
|
||||
goto err_cpm_init;
|
||||
|
||||
mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
|
||||
if (mpc8xxx_spi->reg_base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(mpc8xxx_spi->reg_base)) {
|
||||
ret = PTR_ERR(mpc8xxx_spi->reg_base);
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
if (mpc8xxx_spi->type == TYPE_GRLIB)
|
||||
|
@ -661,11 +654,11 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
|||
&mpc8xxx_spi->tx_shift, 8, 1);
|
||||
|
||||
/* Register for SPI Interrupt */
|
||||
ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq,
|
||||
0, "fsl_spi", mpc8xxx_spi);
|
||||
ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_spi_irq,
|
||||
0, "fsl_spi", mpc8xxx_spi);
|
||||
|
||||
if (ret != 0)
|
||||
goto free_irq;
|
||||
goto err_probe;
|
||||
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
|
@ -686,20 +679,16 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
|||
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
ret = devm_spi_register_master(dev, master);
|
||||
if (ret < 0)
|
||||
goto unreg_master;
|
||||
goto err_probe;
|
||||
|
||||
dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base,
|
||||
mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
|
||||
|
||||
return master;
|
||||
|
||||
unreg_master:
|
||||
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
|
||||
free_irq:
|
||||
iounmap(mpc8xxx_spi->reg_base);
|
||||
err_ioremap:
|
||||
err_probe:
|
||||
fsl_spi_cpm_free(mpc8xxx_spi);
|
||||
err_cpm_init:
|
||||
spi_master_put(master);
|
||||
|
@ -866,11 +855,8 @@ static int of_fsl_spi_remove(struct platform_device *ofdev)
|
|||
{
|
||||
struct spi_master *master = platform_get_drvdata(ofdev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = mpc8xxx_spi_remove(&ofdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
fsl_spi_cpm_free(mpc8xxx_spi);
|
||||
if (mpc8xxx_spi->type == TYPE_FSL)
|
||||
of_fsl_spi_free_chipselects(&ofdev->dev);
|
||||
return 0;
|
||||
|
@ -916,7 +902,12 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
static int plat_mpc8xxx_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
return mpc8xxx_spi_remove(&pdev->dev);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
|
||||
fsl_spi_cpm_free(mpc8xxx_spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:mpc8xxx_spi");
|
||||
|
|
|
@ -105,6 +105,10 @@ struct img_spfi {
|
|||
bool rx_dma_busy;
|
||||
};
|
||||
|
||||
struct img_spfi_device_data {
|
||||
bool gpio_requested;
|
||||
};
|
||||
|
||||
static inline u32 spfi_readl(struct img_spfi *spfi, u32 reg)
|
||||
{
|
||||
return readl(spfi->regs + reg);
|
||||
|
@ -267,15 +271,15 @@ static int img_spfi_start_pio(struct spi_master *master,
|
|||
cpu_relax();
|
||||
}
|
||||
|
||||
ret = spfi_wait_all_done(spfi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (rx_bytes > 0 || tx_bytes > 0) {
|
||||
dev_err(spfi->dev, "PIO transfer timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = spfi_wait_all_done(spfi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -440,21 +444,50 @@ static int img_spfi_unprepare(struct spi_master *master,
|
|||
|
||||
static int img_spfi_setup(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
int ret = -EINVAL;
|
||||
struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
|
||||
|
||||
ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ?
|
||||
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "can't request chipselect gpio %d\n",
|
||||
if (!spfi_data) {
|
||||
spfi_data = kzalloc(sizeof(*spfi_data), GFP_KERNEL);
|
||||
if (!spfi_data)
|
||||
return -ENOMEM;
|
||||
spfi_data->gpio_requested = false;
|
||||
spi_set_ctldata(spi, spfi_data);
|
||||
}
|
||||
if (!spfi_data->gpio_requested) {
|
||||
ret = gpio_request_one(spi->cs_gpio,
|
||||
(spi->mode & SPI_CS_HIGH) ?
|
||||
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "can't request chipselect gpio %d\n",
|
||||
spi->cs_gpio);
|
||||
else
|
||||
spfi_data->gpio_requested = true;
|
||||
} else {
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
int mode = ((spi->mode & SPI_CS_HIGH) ?
|
||||
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH);
|
||||
|
||||
ret = gpio_direction_output(spi->cs_gpio, mode);
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "chipselect gpio %d setup failed (%d)\n",
|
||||
spi->cs_gpio, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void img_spfi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
gpio_free(spi->cs_gpio);
|
||||
struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
|
||||
|
||||
if (spfi_data) {
|
||||
if (spfi_data->gpio_requested)
|
||||
gpio_free(spi->cs_gpio);
|
||||
kfree(spfi_data);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||
|
@ -548,6 +581,7 @@ static int img_spfi_probe(struct platform_device *pdev)
|
|||
struct img_spfi *spfi;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
u32 max_speed_hz;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*spfi));
|
||||
if (!master)
|
||||
|
@ -612,6 +646,19 @@ static int img_spfi_probe(struct platform_device *pdev)
|
|||
master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4;
|
||||
master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512;
|
||||
|
||||
/*
|
||||
* Maximum speed supported by spfi is limited to the lower value
|
||||
* between 1/4 of the SPFI clock or to "spfi-max-frequency"
|
||||
* defined in the device tree.
|
||||
* If no value is defined in the device tree assume the maximum
|
||||
* speed supported to be 1/4 of the SPFI clock.
|
||||
*/
|
||||
if (!of_property_read_u32(spfi->dev->of_node, "spfi-max-frequency",
|
||||
&max_speed_hz)) {
|
||||
if (master->max_speed_hz > max_speed_hz)
|
||||
master->max_speed_hz = max_speed_hz;
|
||||
}
|
||||
|
||||
master->setup = img_spfi_setup;
|
||||
master->cleanup = img_spfi_cleanup;
|
||||
master->transfer_one = img_spfi_transfer_one;
|
||||
|
|
|
@ -30,11 +30,37 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <asm/mpc52xx_psc.h>
|
||||
|
||||
enum {
|
||||
TYPE_MPC5121,
|
||||
TYPE_MPC5125,
|
||||
};
|
||||
|
||||
/*
|
||||
* This macro abstracts the differences in the PSC register layout between
|
||||
* MPC5121 (which uses a struct mpc52xx_psc) and MPC5125 (using mpc5125_psc).
|
||||
*/
|
||||
#define psc_addr(mps, regname) ({ \
|
||||
void *__ret = NULL; \
|
||||
switch (mps->type) { \
|
||||
case TYPE_MPC5121: { \
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc; \
|
||||
__ret = &psc->regname; \
|
||||
}; \
|
||||
break; \
|
||||
case TYPE_MPC5125: { \
|
||||
struct mpc5125_psc __iomem *psc = mps->psc; \
|
||||
__ret = &psc->regname; \
|
||||
}; \
|
||||
break; \
|
||||
} \
|
||||
__ret; })
|
||||
|
||||
struct mpc512x_psc_spi {
|
||||
void (*cs_control)(struct spi_device *spi, bool on);
|
||||
|
||||
/* driver internal data */
|
||||
struct mpc52xx_psc __iomem *psc;
|
||||
int type;
|
||||
void __iomem *psc;
|
||||
struct mpc512x_psc_fifo __iomem *fifo;
|
||||
unsigned int irq;
|
||||
u8 bits_per_word;
|
||||
|
@ -71,13 +97,12 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
|||
{
|
||||
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
u32 sicr;
|
||||
u32 ccr;
|
||||
int speed;
|
||||
u16 bclkdiv;
|
||||
|
||||
sicr = in_be32(&psc->sicr);
|
||||
sicr = in_be32(psc_addr(mps, sicr));
|
||||
|
||||
/* Set clock phase and polarity */
|
||||
if (spi->mode & SPI_CPHA)
|
||||
|
@ -94,9 +119,9 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
|||
sicr |= 0x10000000;
|
||||
else
|
||||
sicr &= ~0x10000000;
|
||||
out_be32(&psc->sicr, sicr);
|
||||
out_be32(psc_addr(mps, sicr), sicr);
|
||||
|
||||
ccr = in_be32(&psc->ccr);
|
||||
ccr = in_be32(psc_addr(mps, ccr));
|
||||
ccr &= 0xFF000000;
|
||||
speed = cs->speed_hz;
|
||||
if (!speed)
|
||||
|
@ -104,7 +129,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
|||
bclkdiv = (mps->mclk_rate / speed) - 1;
|
||||
|
||||
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
|
||||
out_be32(&psc->ccr, ccr);
|
||||
out_be32(psc_addr(mps, ccr), ccr);
|
||||
mps->bits_per_word = cs->bits_per_word;
|
||||
|
||||
if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
|
||||
|
@ -315,16 +340,15 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master,
|
|||
static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master)
|
||||
{
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
|
||||
dev_dbg(&master->dev, "%s()\n", __func__);
|
||||
|
||||
/* Zero MR2 */
|
||||
in_8(&psc->mode);
|
||||
out_8(&psc->mode, 0x0);
|
||||
in_8(psc_addr(mps, mr2));
|
||||
out_8(psc_addr(mps, mr2), 0x0);
|
||||
|
||||
/* enable transmitter/receiver */
|
||||
out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
|
||||
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -332,13 +356,12 @@ static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master)
|
|||
static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master)
|
||||
{
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
|
||||
|
||||
dev_dbg(&master->dev, "%s()\n", __func__);
|
||||
|
||||
/* disable transmitter/receiver and fifo interrupt */
|
||||
out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
out_be32(&fifo->tximr, 0);
|
||||
|
||||
return 0;
|
||||
|
@ -388,7 +411,6 @@ static void mpc512x_psc_spi_cleanup(struct spi_device *spi)
|
|||
static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
||||
struct mpc512x_psc_spi *mps)
|
||||
{
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
|
||||
u32 sicr;
|
||||
u32 ccr;
|
||||
|
@ -396,12 +418,12 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
|||
u16 bclkdiv;
|
||||
|
||||
/* Reset the PSC into a known state */
|
||||
out_8(&psc->command, MPC52xx_PSC_RST_RX);
|
||||
out_8(&psc->command, MPC52xx_PSC_RST_TX);
|
||||
out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
out_8(psc_addr(mps, command), MPC52xx_PSC_RST_RX);
|
||||
out_8(psc_addr(mps, command), MPC52xx_PSC_RST_TX);
|
||||
out_8(psc_addr(mps, command), MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
|
||||
/* Disable psc interrupts all useful interrupts are in fifo */
|
||||
out_be16(&psc->isr_imr.imr, 0);
|
||||
out_be16(psc_addr(mps, isr_imr.imr), 0);
|
||||
|
||||
/* Disable fifo interrupts, will be enabled later */
|
||||
out_be32(&fifo->tximr, 0);
|
||||
|
@ -417,18 +439,18 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
|||
0x00004000 | /* MSTR = 1 -- SPI master */
|
||||
0x00000800; /* UseEOF = 1 -- SS low until EOF */
|
||||
|
||||
out_be32(&psc->sicr, sicr);
|
||||
out_be32(psc_addr(mps, sicr), sicr);
|
||||
|
||||
ccr = in_be32(&psc->ccr);
|
||||
ccr = in_be32(psc_addr(mps, ccr));
|
||||
ccr &= 0xFF000000;
|
||||
speed = 1000000; /* default 1MHz */
|
||||
bclkdiv = (mps->mclk_rate / speed) - 1;
|
||||
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
|
||||
out_be32(&psc->ccr, ccr);
|
||||
out_be32(psc_addr(mps, ccr), ccr);
|
||||
|
||||
/* Set 2ms DTL delay */
|
||||
out_8(&psc->ctur, 0x00);
|
||||
out_8(&psc->ctlr, 0x82);
|
||||
out_8(psc_addr(mps, ctur), 0x00);
|
||||
out_8(psc_addr(mps, ctlr), 0x82);
|
||||
|
||||
/* we don't use the alarms */
|
||||
out_be32(&fifo->rxalarm, 0xfff);
|
||||
|
@ -482,6 +504,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
|||
|
||||
dev_set_drvdata(dev, master);
|
||||
mps = spi_master_get_devdata(master);
|
||||
mps->type = (int)of_device_get_match_data(dev);
|
||||
mps->irq = irq;
|
||||
|
||||
if (pdata == NULL) {
|
||||
|
@ -589,7 +612,8 @@ static int mpc512x_psc_spi_of_remove(struct platform_device *op)
|
|||
}
|
||||
|
||||
static const struct of_device_id mpc512x_psc_spi_of_match[] = {
|
||||
{ .compatible = "fsl,mpc5121-psc-spi", },
|
||||
{ .compatible = "fsl,mpc5121-psc-spi", .data = (void *)TYPE_MPC5121 },
|
||||
{ .compatible = "fsl,mpc5125-psc-spi", .data = (void *)TYPE_MPC5125 },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,726 @@
|
|||
/*
|
||||
* Copyright (c) 2015 MediaTek Inc.
|
||||
* Author: Leilk Liu <leilk.liu@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/spi-mt65xx.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SPI_CFG0_REG 0x0000
|
||||
#define SPI_CFG1_REG 0x0004
|
||||
#define SPI_TX_SRC_REG 0x0008
|
||||
#define SPI_RX_DST_REG 0x000c
|
||||
#define SPI_TX_DATA_REG 0x0010
|
||||
#define SPI_RX_DATA_REG 0x0014
|
||||
#define SPI_CMD_REG 0x0018
|
||||
#define SPI_STATUS0_REG 0x001c
|
||||
#define SPI_PAD_SEL_REG 0x0024
|
||||
|
||||
#define SPI_CFG0_SCK_HIGH_OFFSET 0
|
||||
#define SPI_CFG0_SCK_LOW_OFFSET 8
|
||||
#define SPI_CFG0_CS_HOLD_OFFSET 16
|
||||
#define SPI_CFG0_CS_SETUP_OFFSET 24
|
||||
|
||||
#define SPI_CFG1_CS_IDLE_OFFSET 0
|
||||
#define SPI_CFG1_PACKET_LOOP_OFFSET 8
|
||||
#define SPI_CFG1_PACKET_LENGTH_OFFSET 16
|
||||
#define SPI_CFG1_GET_TICK_DLY_OFFSET 30
|
||||
|
||||
#define SPI_CFG1_CS_IDLE_MASK 0xff
|
||||
#define SPI_CFG1_PACKET_LOOP_MASK 0xff00
|
||||
#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000
|
||||
|
||||
#define SPI_CMD_ACT BIT(0)
|
||||
#define SPI_CMD_RESUME BIT(1)
|
||||
#define SPI_CMD_RST BIT(2)
|
||||
#define SPI_CMD_PAUSE_EN BIT(4)
|
||||
#define SPI_CMD_DEASSERT BIT(5)
|
||||
#define SPI_CMD_CPHA BIT(8)
|
||||
#define SPI_CMD_CPOL BIT(9)
|
||||
#define SPI_CMD_RX_DMA BIT(10)
|
||||
#define SPI_CMD_TX_DMA BIT(11)
|
||||
#define SPI_CMD_TXMSBF BIT(12)
|
||||
#define SPI_CMD_RXMSBF BIT(13)
|
||||
#define SPI_CMD_RX_ENDIAN BIT(14)
|
||||
#define SPI_CMD_TX_ENDIAN BIT(15)
|
||||
#define SPI_CMD_FINISH_IE BIT(16)
|
||||
#define SPI_CMD_PAUSE_IE BIT(17)
|
||||
|
||||
#define MT8173_SPI_MAX_PAD_SEL 3
|
||||
|
||||
#define MTK_SPI_PAUSE_INT_STATUS 0x2
|
||||
|
||||
#define MTK_SPI_IDLE 0
|
||||
#define MTK_SPI_PAUSED 1
|
||||
|
||||
#define MTK_SPI_MAX_FIFO_SIZE 32
|
||||
#define MTK_SPI_PACKET_SIZE 1024
|
||||
|
||||
struct mtk_spi_compatible {
|
||||
bool need_pad_sel;
|
||||
/* Must explicitly send dummy Tx bytes to do Rx only transfer */
|
||||
bool must_tx;
|
||||
};
|
||||
|
||||
struct mtk_spi {
|
||||
void __iomem *base;
|
||||
u32 state;
|
||||
u32 pad_sel;
|
||||
struct clk *spi_clk, *parent_clk;
|
||||
struct spi_transfer *cur_transfer;
|
||||
u32 xfer_len;
|
||||
struct scatterlist *tx_sgl, *rx_sgl;
|
||||
u32 tx_sgl_len, rx_sgl_len;
|
||||
const struct mtk_spi_compatible *dev_comp;
|
||||
};
|
||||
|
||||
static const struct mtk_spi_compatible mt6589_compat;
|
||||
static const struct mtk_spi_compatible mt8135_compat;
|
||||
static const struct mtk_spi_compatible mt8173_compat = {
|
||||
.need_pad_sel = true,
|
||||
.must_tx = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* A piece of default chip info unless the platform
|
||||
* supplies it.
|
||||
*/
|
||||
static const struct mtk_chip_config mtk_default_chip_info = {
|
||||
.rx_mlsb = 1,
|
||||
.tx_mlsb = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_spi_of_match[] = {
|
||||
{ .compatible = "mediatek,mt6589-spi", .data = (void *)&mt6589_compat },
|
||||
{ .compatible = "mediatek,mt8135-spi", .data = (void *)&mt8135_compat },
|
||||
{ .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
|
||||
|
||||
static void mtk_spi_reset(struct mtk_spi *mdata)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
/* set the software reset bit in SPI_CMD_REG. */
|
||||
reg_val = readl(mdata->base + SPI_CMD_REG);
|
||||
reg_val |= SPI_CMD_RST;
|
||||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CMD_REG);
|
||||
reg_val &= ~SPI_CMD_RST;
|
||||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
}
|
||||
|
||||
static void mtk_spi_config(struct mtk_spi *mdata,
|
||||
struct mtk_chip_config *chip_config)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CMD_REG);
|
||||
|
||||
/* set the mlsbx and mlsbtx */
|
||||
if (chip_config->tx_mlsb)
|
||||
reg_val |= SPI_CMD_TXMSBF;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_TXMSBF;
|
||||
if (chip_config->rx_mlsb)
|
||||
reg_val |= SPI_CMD_RXMSBF;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_RXMSBF;
|
||||
|
||||
/* set the tx/rx endian */
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
reg_val &= ~SPI_CMD_TX_ENDIAN;
|
||||
reg_val &= ~SPI_CMD_RX_ENDIAN;
|
||||
#else
|
||||
reg_val |= SPI_CMD_TX_ENDIAN;
|
||||
reg_val |= SPI_CMD_RX_ENDIAN;
|
||||
#endif
|
||||
|
||||
/* set finish and pause interrupt always enable */
|
||||
reg_val |= SPI_CMD_FINISH_IE | SPI_CMD_PAUSE_IE;
|
||||
|
||||
/* disable dma mode */
|
||||
reg_val &= ~(SPI_CMD_TX_DMA | SPI_CMD_RX_DMA);
|
||||
|
||||
/* disable deassert mode */
|
||||
reg_val &= ~SPI_CMD_DEASSERT;
|
||||
|
||||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
|
||||
/* pad select */
|
||||
if (mdata->dev_comp->need_pad_sel)
|
||||
writel(mdata->pad_sel, mdata->base + SPI_PAD_SEL_REG);
|
||||
}
|
||||
|
||||
static int mtk_spi_prepare_hardware(struct spi_master *master)
|
||||
{
|
||||
struct spi_transfer *trans;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
struct spi_message *msg = master->cur_msg;
|
||||
|
||||
trans = list_first_entry(&msg->transfers, struct spi_transfer,
|
||||
transfer_list);
|
||||
if (!trans->cs_change) {
|
||||
mdata->state = MTK_SPI_IDLE;
|
||||
mtk_spi_reset(mdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
u32 reg_val;
|
||||
u8 cpha, cpol;
|
||||
struct mtk_chip_config *chip_config;
|
||||
struct spi_device *spi = msg->spi;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
cpha = spi->mode & SPI_CPHA ? 1 : 0;
|
||||
cpol = spi->mode & SPI_CPOL ? 1 : 0;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CMD_REG);
|
||||
if (cpha)
|
||||
reg_val |= SPI_CMD_CPHA;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_CPHA;
|
||||
if (cpol)
|
||||
reg_val |= SPI_CMD_CPOL;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_CPOL;
|
||||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
|
||||
chip_config = spi->controller_data;
|
||||
if (!chip_config) {
|
||||
chip_config = (void *)&mtk_default_chip_info;
|
||||
spi->controller_data = chip_config;
|
||||
}
|
||||
mtk_spi_config(mdata, chip_config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
u32 reg_val;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CMD_REG);
|
||||
if (!enable)
|
||||
reg_val |= SPI_CMD_PAUSE_EN;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_PAUSE_EN;
|
||||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
}
|
||||
|
||||
static void mtk_spi_prepare_transfer(struct spi_master *master,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
u32 spi_clk_hz, div, sck_time, cs_time, reg_val = 0;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
spi_clk_hz = clk_get_rate(mdata->spi_clk);
|
||||
if (xfer->speed_hz < spi_clk_hz / 2)
|
||||
div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz);
|
||||
else
|
||||
div = 1;
|
||||
|
||||
sck_time = (div + 1) / 2;
|
||||
cs_time = sck_time * 2;
|
||||
|
||||
reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_HIGH_OFFSET);
|
||||
reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET);
|
||||
reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_SETUP_OFFSET);
|
||||
writel(reg_val, mdata->base + SPI_CFG0_REG);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
||||
reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
|
||||
reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
|
||||
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
||||
}
|
||||
|
||||
static void mtk_spi_setup_packet(struct spi_master *master)
|
||||
{
|
||||
u32 packet_size, packet_loop, reg_val;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE);
|
||||
packet_loop = mdata->xfer_len / packet_size;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
||||
reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK);
|
||||
reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET;
|
||||
reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET;
|
||||
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
||||
}
|
||||
|
||||
static void mtk_spi_enable_transfer(struct spi_master *master)
|
||||
{
|
||||
u32 cmd;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
cmd = readl(mdata->base + SPI_CMD_REG);
|
||||
if (mdata->state == MTK_SPI_IDLE)
|
||||
cmd |= SPI_CMD_ACT;
|
||||
else
|
||||
cmd |= SPI_CMD_RESUME;
|
||||
writel(cmd, mdata->base + SPI_CMD_REG);
|
||||
}
|
||||
|
||||
static int mtk_spi_get_mult_delta(u32 xfer_len)
|
||||
{
|
||||
u32 mult_delta;
|
||||
|
||||
if (xfer_len > MTK_SPI_PACKET_SIZE)
|
||||
mult_delta = xfer_len % MTK_SPI_PACKET_SIZE;
|
||||
else
|
||||
mult_delta = 0;
|
||||
|
||||
return mult_delta;
|
||||
}
|
||||
|
||||
static void mtk_spi_update_mdata_len(struct spi_master *master)
|
||||
{
|
||||
int mult_delta;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
if (mdata->tx_sgl_len && mdata->rx_sgl_len) {
|
||||
if (mdata->tx_sgl_len > mdata->rx_sgl_len) {
|
||||
mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len);
|
||||
mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
|
||||
mdata->rx_sgl_len = mult_delta;
|
||||
mdata->tx_sgl_len -= mdata->xfer_len;
|
||||
} else {
|
||||
mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len);
|
||||
mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
|
||||
mdata->tx_sgl_len = mult_delta;
|
||||
mdata->rx_sgl_len -= mdata->xfer_len;
|
||||
}
|
||||
} else if (mdata->tx_sgl_len) {
|
||||
mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len);
|
||||
mdata->xfer_len = mdata->tx_sgl_len - mult_delta;
|
||||
mdata->tx_sgl_len = mult_delta;
|
||||
} else if (mdata->rx_sgl_len) {
|
||||
mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len);
|
||||
mdata->xfer_len = mdata->rx_sgl_len - mult_delta;
|
||||
mdata->rx_sgl_len = mult_delta;
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_spi_setup_dma_addr(struct spi_master *master,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
if (mdata->tx_sgl)
|
||||
writel(xfer->tx_dma, mdata->base + SPI_TX_SRC_REG);
|
||||
if (mdata->rx_sgl)
|
||||
writel(xfer->rx_dma, mdata->base + SPI_RX_DST_REG);
|
||||
}
|
||||
|
||||
static int mtk_spi_fifo_transfer(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
int cnt;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
mdata->cur_transfer = xfer;
|
||||
mdata->xfer_len = xfer->len;
|
||||
mtk_spi_prepare_transfer(master, xfer);
|
||||
mtk_spi_setup_packet(master);
|
||||
|
||||
if (xfer->len % 4)
|
||||
cnt = xfer->len / 4 + 1;
|
||||
else
|
||||
cnt = xfer->len / 4;
|
||||
iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt);
|
||||
|
||||
mtk_spi_enable_transfer(master);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mtk_spi_dma_transfer(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
int cmd;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
mdata->tx_sgl = NULL;
|
||||
mdata->rx_sgl = NULL;
|
||||
mdata->tx_sgl_len = 0;
|
||||
mdata->rx_sgl_len = 0;
|
||||
mdata->cur_transfer = xfer;
|
||||
|
||||
mtk_spi_prepare_transfer(master, xfer);
|
||||
|
||||
cmd = readl(mdata->base + SPI_CMD_REG);
|
||||
if (xfer->tx_buf)
|
||||
cmd |= SPI_CMD_TX_DMA;
|
||||
if (xfer->rx_buf)
|
||||
cmd |= SPI_CMD_RX_DMA;
|
||||
writel(cmd, mdata->base + SPI_CMD_REG);
|
||||
|
||||
if (xfer->tx_buf)
|
||||
mdata->tx_sgl = xfer->tx_sg.sgl;
|
||||
if (xfer->rx_buf)
|
||||
mdata->rx_sgl = xfer->rx_sg.sgl;
|
||||
|
||||
if (mdata->tx_sgl) {
|
||||
xfer->tx_dma = sg_dma_address(mdata->tx_sgl);
|
||||
mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
|
||||
}
|
||||
if (mdata->rx_sgl) {
|
||||
xfer->rx_dma = sg_dma_address(mdata->rx_sgl);
|
||||
mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
|
||||
}
|
||||
|
||||
mtk_spi_update_mdata_len(master);
|
||||
mtk_spi_setup_packet(master);
|
||||
mtk_spi_setup_dma_addr(master, xfer);
|
||||
mtk_spi_enable_transfer(master);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mtk_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
if (master->can_dma(master, spi, xfer))
|
||||
return mtk_spi_dma_transfer(master, spi, xfer);
|
||||
else
|
||||
return mtk_spi_fifo_transfer(master, spi, xfer);
|
||||
}
|
||||
|
||||
static bool mtk_spi_can_dma(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
return xfer->len > MTK_SPI_MAX_FIFO_SIZE;
|
||||
}
|
||||
|
||||
static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
u32 cmd, reg_val, cnt;
|
||||
struct spi_master *master = dev_id;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
struct spi_transfer *trans = mdata->cur_transfer;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_STATUS0_REG);
|
||||
if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
|
||||
mdata->state = MTK_SPI_PAUSED;
|
||||
else
|
||||
mdata->state = MTK_SPI_IDLE;
|
||||
|
||||
if (!master->can_dma(master, master->cur_msg->spi, trans)) {
|
||||
if (trans->rx_buf) {
|
||||
if (mdata->xfer_len % 4)
|
||||
cnt = mdata->xfer_len / 4 + 1;
|
||||
else
|
||||
cnt = mdata->xfer_len / 4;
|
||||
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
|
||||
trans->rx_buf, cnt);
|
||||
}
|
||||
spi_finalize_current_transfer(master);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (mdata->tx_sgl)
|
||||
trans->tx_dma += mdata->xfer_len;
|
||||
if (mdata->rx_sgl)
|
||||
trans->rx_dma += mdata->xfer_len;
|
||||
|
||||
if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) {
|
||||
mdata->tx_sgl = sg_next(mdata->tx_sgl);
|
||||
if (mdata->tx_sgl) {
|
||||
trans->tx_dma = sg_dma_address(mdata->tx_sgl);
|
||||
mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
|
||||
}
|
||||
}
|
||||
if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) {
|
||||
mdata->rx_sgl = sg_next(mdata->rx_sgl);
|
||||
if (mdata->rx_sgl) {
|
||||
trans->rx_dma = sg_dma_address(mdata->rx_sgl);
|
||||
mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mdata->tx_sgl && !mdata->rx_sgl) {
|
||||
/* spi disable dma */
|
||||
cmd = readl(mdata->base + SPI_CMD_REG);
|
||||
cmd &= ~SPI_CMD_TX_DMA;
|
||||
cmd &= ~SPI_CMD_RX_DMA;
|
||||
writel(cmd, mdata->base + SPI_CMD_REG);
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mtk_spi_update_mdata_len(master);
|
||||
mtk_spi_setup_packet(master);
|
||||
mtk_spi_setup_dma_addr(master, trans);
|
||||
mtk_spi_enable_transfer(master);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mtk_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct mtk_spi *mdata;
|
||||
const struct of_device_id *of_id;
|
||||
struct resource *res;
|
||||
int irq, ret;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "failed to alloc spi master\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
master->auto_runtime_pm = true;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
|
||||
master->set_cs = mtk_spi_set_cs;
|
||||
master->prepare_transfer_hardware = mtk_spi_prepare_hardware;
|
||||
master->prepare_message = mtk_spi_prepare_message;
|
||||
master->transfer_one = mtk_spi_transfer_one;
|
||||
master->can_dma = mtk_spi_can_dma;
|
||||
|
||||
of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
|
||||
if (!of_id) {
|
||||
dev_err(&pdev->dev, "failed to probe of_node\n");
|
||||
ret = -EINVAL;
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
mdata = spi_master_get_devdata(master);
|
||||
mdata->dev_comp = of_id->data;
|
||||
if (mdata->dev_comp->must_tx)
|
||||
master->flags = SPI_MASTER_MUST_TX;
|
||||
|
||||
if (mdata->dev_comp->need_pad_sel) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"mediatek,pad-select",
|
||||
&mdata->pad_sel);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to read pad select: %d\n",
|
||||
ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
if (mdata->pad_sel > MT8173_SPI_MAX_PAD_SEL) {
|
||||
dev_err(&pdev->dev, "wrong pad-select: %u\n",
|
||||
mdata->pad_sel);
|
||||
ret = -EINVAL;
|
||||
goto err_put_master;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&pdev->dev, "failed to determine base address\n");
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
mdata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mdata->base)) {
|
||||
ret = PTR_ERR(mdata->base);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq (%d)\n", irq);
|
||||
ret = irq;
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
if (!pdev->dev.dma_mask)
|
||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, mtk_spi_interrupt,
|
||||
IRQF_TRIGGER_NONE, dev_name(&pdev->dev), master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register irq (%d)\n", ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk");
|
||||
if (IS_ERR(mdata->spi_clk)) {
|
||||
ret = PTR_ERR(mdata->spi_clk);
|
||||
dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk");
|
||||
if (IS_ERR(mdata->parent_clk)) {
|
||||
ret = PTR_ERR(mdata->parent_clk);
|
||||
dev_err(&pdev->dev, "failed to get parent-clk: %d\n", ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(mdata->spi_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
ret = clk_set_parent(mdata->spi_clk, mdata->parent_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
err_put_master:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
mtk_spi_reset(mdata);
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_spi_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
ret = spi_master_suspend(master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_spi_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
ret = clk_prepare_enable(mdata->spi_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spi_master_resume(master);
|
||||
if (ret < 0)
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mtk_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mdata->spi_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops mtk_spi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend,
|
||||
mtk_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mtk-spi",
|
||||
.pm = &mtk_spi_pm,
|
||||
.of_match_table = mtk_spi_of_match,
|
||||
},
|
||||
.probe = mtk_spi_probe,
|
||||
.remove = mtk_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MTK SPI Controller driver");
|
||||
MODULE_AUTHOR("Leilk Liu <leilk.liu@mediatek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:mtk-spi");
|
|
@ -245,6 +245,7 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
|
|||
|
||||
static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
|
||||
u32 l;
|
||||
|
||||
/* The controller handles the inverted chip selects
|
||||
|
@ -255,6 +256,12 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
|
|||
enable = !enable;
|
||||
|
||||
if (spi->controller_state) {
|
||||
int err = pm_runtime_get_sync(mcspi->dev);
|
||||
if (err < 0) {
|
||||
dev_err(mcspi->dev, "failed to get sync: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
l = mcspi_cached_chconf0(spi);
|
||||
|
||||
if (enable)
|
||||
|
@ -263,6 +270,9 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
|
|||
l |= OMAP2_MCSPI_CHCONF_FORCE;
|
||||
|
||||
mcspi_write_chconf0(spi, l);
|
||||
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@
|
|||
#define ORION_SPI_DATA_OUT_REG 0x08
|
||||
#define ORION_SPI_DATA_IN_REG 0x0c
|
||||
#define ORION_SPI_INT_CAUSE_REG 0x10
|
||||
#define ORION_SPI_TIMING_PARAMS_REG 0x18
|
||||
|
||||
#define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6)
|
||||
#define ORION_SPI_TMISO_SAMPLE_1 (1 << 6)
|
||||
#define ORION_SPI_TMISO_SAMPLE_2 (2 << 6)
|
||||
|
||||
#define ORION_SPI_MODE_CPOL (1 << 11)
|
||||
#define ORION_SPI_MODE_CPHA (1 << 12)
|
||||
|
@ -70,6 +75,7 @@ struct orion_spi_dev {
|
|||
unsigned int min_divisor;
|
||||
unsigned int max_divisor;
|
||||
u32 prescale_mask;
|
||||
bool is_errata_50mhz_ac;
|
||||
};
|
||||
|
||||
struct orion_spi {
|
||||
|
@ -195,6 +201,41 @@ orion_spi_mode_set(struct spi_device *spi)
|
|||
writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
||||
}
|
||||
|
||||
static void
|
||||
orion_spi_50mhz_ac_timing_erratum(struct spi_device *spi, unsigned int speed)
|
||||
{
|
||||
u32 reg;
|
||||
struct orion_spi *orion_spi;
|
||||
|
||||
orion_spi = spi_master_get_devdata(spi->master);
|
||||
|
||||
/*
|
||||
* Erratum description: (Erratum NO. FE-9144572) The device
|
||||
* SPI interface supports frequencies of up to 50 MHz.
|
||||
* However, due to this erratum, when the device core clock is
|
||||
* 250 MHz and the SPI interfaces is configured for 50MHz SPI
|
||||
* clock and CPOL=CPHA=1 there might occur data corruption on
|
||||
* reads from the SPI device.
|
||||
* Erratum Workaround:
|
||||
* Work in one of the following configurations:
|
||||
* 1. Set CPOL=CPHA=0 in "SPI Interface Configuration
|
||||
* Register".
|
||||
* 2. Set TMISO_SAMPLE value to 0x2 in "SPI Timing Parameters 1
|
||||
* Register" before setting the interface.
|
||||
*/
|
||||
reg = readl(spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG));
|
||||
reg &= ~ORION_SPI_TMISO_SAMPLE_MASK;
|
||||
|
||||
if (clk_get_rate(orion_spi->clk) == 250000000 &&
|
||||
speed == 50000000 && spi->mode & SPI_CPOL &&
|
||||
spi->mode & SPI_CPHA)
|
||||
reg |= ORION_SPI_TMISO_SAMPLE_2;
|
||||
else
|
||||
reg |= ORION_SPI_TMISO_SAMPLE_1; /* This is the default value */
|
||||
|
||||
writel(reg, spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG));
|
||||
}
|
||||
|
||||
/*
|
||||
* called only when no transfer is active on the bus
|
||||
*/
|
||||
|
@ -216,6 +257,9 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
|
||||
orion_spi_mode_set(spi);
|
||||
|
||||
if (orion_spi->devdata->is_errata_50mhz_ac)
|
||||
orion_spi_50mhz_ac_timing_erratum(spi, speed);
|
||||
|
||||
rc = orion_spi_baudrate_set(spi, speed);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -413,6 +457,14 @@ static const struct orion_spi_dev armada_375_spi_dev_data = {
|
|||
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
|
||||
};
|
||||
|
||||
static const struct orion_spi_dev armada_380_spi_dev_data = {
|
||||
.typ = ARMADA_SPI,
|
||||
.max_hz = 50000000,
|
||||
.max_divisor = 1920,
|
||||
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
|
||||
.is_errata_50mhz_ac = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id orion_spi_of_match_table[] = {
|
||||
{
|
||||
.compatible = "marvell,orion-spi",
|
||||
|
@ -428,7 +480,7 @@ static const struct of_device_id orion_spi_of_match_table[] = {
|
|||
},
|
||||
{
|
||||
.compatible = "marvell,armada-380-spi",
|
||||
.data = &armada_xp_spi_dev_data,
|
||||
.data = &armada_380_spi_dev_data,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,armada-390-spi",
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -97,6 +98,15 @@ static const struct lpss_config lpss_platforms[] = {
|
|||
.tx_threshold_lo = 160,
|
||||
.tx_threshold_hi = 224,
|
||||
},
|
||||
{ /* LPSS_SPT_SSP */
|
||||
.offset = 0x200,
|
||||
.reg_general = -1,
|
||||
.reg_ssp = 0x20,
|
||||
.reg_cs_ctrl = 0x24,
|
||||
.rx_threshold = 1,
|
||||
.tx_threshold_lo = 32,
|
||||
.tx_threshold_hi = 56,
|
||||
},
|
||||
};
|
||||
|
||||
static inline const struct lpss_config
|
||||
|
@ -110,6 +120,7 @@ static bool is_lpss_ssp(const struct driver_data *drv_data)
|
|||
switch (drv_data->ssp_type) {
|
||||
case LPSS_LPT_SSP:
|
||||
case LPSS_BYT_SSP:
|
||||
case LPSS_SPT_SSP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -1107,6 +1118,7 @@ static int setup(struct spi_device *spi)
|
|||
break;
|
||||
case LPSS_LPT_SSP:
|
||||
case LPSS_BYT_SSP:
|
||||
case LPSS_SPT_SSP:
|
||||
config = lpss_get_config(drv_data);
|
||||
tx_thres = config->tx_threshold_lo;
|
||||
tx_hi_thres = config->tx_threshold_hi;
|
||||
|
@ -1276,6 +1288,31 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
||||
|
||||
/*
|
||||
* PCI IDs of compound devices that integrate both host controller and private
|
||||
* integrated DMA engine. Please note these are not used in module
|
||||
* autoloading and probing in this module but matching the LPSS SSP type.
|
||||
*/
|
||||
static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
|
||||
/* SPT-LP */
|
||||
{ PCI_VDEVICE(INTEL, 0x9d29), LPSS_SPT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x9d2a), LPSS_SPT_SSP },
|
||||
/* SPT-H */
|
||||
{ PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP },
|
||||
{ },
|
||||
};
|
||||
|
||||
static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct device *dev = param;
|
||||
|
||||
if (dev != chan->device->dev->parent)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct pxa2xx_spi_master *
|
||||
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -1283,16 +1320,25 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
|
|||
struct acpi_device *adev;
|
||||
struct ssp_device *ssp;
|
||||
struct resource *res;
|
||||
const struct acpi_device_id *id;
|
||||
const struct acpi_device_id *adev_id = NULL;
|
||||
const struct pci_device_id *pcidev_id = NULL;
|
||||
int devid, type;
|
||||
|
||||
if (!ACPI_HANDLE(&pdev->dev) ||
|
||||
acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
|
||||
return NULL;
|
||||
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
if (id)
|
||||
type = (int)id->driver_data;
|
||||
if (dev_is_pci(pdev->dev.parent))
|
||||
pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match,
|
||||
to_pci_dev(pdev->dev.parent));
|
||||
else
|
||||
adev_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
|
||||
&pdev->dev);
|
||||
|
||||
if (adev_id)
|
||||
type = (int)adev_id->driver_data;
|
||||
else if (pcidev_id)
|
||||
type = (int)pcidev_id->driver_data;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
|
@ -1311,6 +1357,12 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
|
|||
if (IS_ERR(ssp->mmio_base))
|
||||
return NULL;
|
||||
|
||||
if (pcidev_id) {
|
||||
pdata->tx_param = pdev->dev.parent;
|
||||
pdata->rx_param = pdev->dev.parent;
|
||||
pdata->dma_filter = pxa2xx_spi_idma_filter;
|
||||
}
|
||||
|
||||
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
ssp->irq = platform_get_irq(pdev, 0);
|
||||
ssp->type = type;
|
||||
|
@ -1362,8 +1414,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate master with space for drv_data and null dma buffer */
|
||||
master = spi_alloc_master(dev, sizeof(struct driver_data) + 16);
|
||||
master = spi_alloc_master(dev, sizeof(struct driver_data));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "cannot alloc spi_master\n");
|
||||
pxa_ssp_free(ssp);
|
||||
|
@ -1390,7 +1441,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
master->auto_runtime_pm = true;
|
||||
|
||||
drv_data->ssp_type = ssp->type;
|
||||
drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
|
||||
|
||||
drv_data->ioaddr = ssp->mmio_base;
|
||||
drv_data->ssdr_physical = ssp->phys_base + SSDR;
|
||||
|
@ -1424,8 +1474,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Setup DMA if requested */
|
||||
drv_data->tx_channel = -1;
|
||||
drv_data->rx_channel = -1;
|
||||
if (platform_info->enable_dma) {
|
||||
status = pxa2xx_spi_dma_setup(drv_data);
|
||||
if (status) {
|
||||
|
|
|
@ -36,11 +36,6 @@ struct driver_data {
|
|||
/* PXA hookup */
|
||||
struct pxa2xx_spi_master *master_info;
|
||||
|
||||
/* PXA private DMA setup stuff */
|
||||
int rx_channel;
|
||||
int tx_channel;
|
||||
u32 *null_dma_buf;
|
||||
|
||||
/* SSP register addresses */
|
||||
void __iomem *ioaddr;
|
||||
u32 ssdr_physical;
|
||||
|
|
|
@ -645,7 +645,6 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, master);
|
||||
|
||||
rs = spi_master_get_devdata(master);
|
||||
memset(rs, 0, sizeof(struct rockchip_spi));
|
||||
|
||||
/* Get basic io resource and map it */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
|
@ -383,7 +383,8 @@ static void qspi_update(const struct rspi_data *rspi, u8 mask, u8 val, u8 reg)
|
|||
rspi_write8(rspi, data, reg);
|
||||
}
|
||||
|
||||
static int qspi_set_send_trigger(struct rspi_data *rspi, unsigned int len)
|
||||
static unsigned int qspi_set_send_trigger(struct rspi_data *rspi,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int n;
|
||||
|
||||
|
@ -724,25 +725,25 @@ static int rspi_rz_transfer_one(struct spi_master *master,
|
|||
static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
|
||||
u8 *rx, unsigned int len)
|
||||
{
|
||||
int i, n, ret;
|
||||
int error;
|
||||
unsigned int i, n;
|
||||
int ret;
|
||||
|
||||
while (len > 0) {
|
||||
n = qspi_set_send_trigger(rspi, len);
|
||||
qspi_set_receive_trigger(rspi, len);
|
||||
if (n == QSPI_BUFFER_SIZE) {
|
||||
error = rspi_wait_for_tx_empty(rspi);
|
||||
if (error < 0) {
|
||||
ret = rspi_wait_for_tx_empty(rspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&rspi->master->dev, "transmit timeout\n");
|
||||
return error;
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
rspi_write_data(rspi, *tx++);
|
||||
|
||||
error = rspi_wait_for_rx_full(rspi);
|
||||
if (error < 0) {
|
||||
ret = rspi_wait_for_rx_full(rspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&rspi->master->dev, "receive timeout\n");
|
||||
return error;
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
*rx++ = rspi_read_data(rspi);
|
||||
|
|
|
@ -501,7 +501,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
memset(hw, 0, sizeof(struct s3c24xx_spi));
|
||||
|
||||
hw->master = master;
|
||||
hw->pdata = pdata = dev_get_platdata(&pdev->dev);
|
||||
|
|
|
@ -1191,8 +1191,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
|
||||
sdd->port_id, master->num_chipselect);
|
||||
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n",
|
||||
mem_res,
|
||||
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n",
|
||||
mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1,
|
||||
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -48,8 +48,8 @@ struct sh_msiof_spi_priv {
|
|||
const struct sh_msiof_chipdata *chipdata;
|
||||
struct sh_msiof_spi_info *info;
|
||||
struct completion done;
|
||||
int tx_fifo_size;
|
||||
int rx_fifo_size;
|
||||
unsigned int tx_fifo_size;
|
||||
unsigned int rx_fifo_size;
|
||||
void *tx_dma_page;
|
||||
void *rx_dma_page;
|
||||
dma_addr_t tx_dma_addr;
|
||||
|
@ -95,8 +95,6 @@ struct sh_msiof_spi_priv {
|
|||
#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
||||
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||
|
||||
#define MAX_WDLEN 256U
|
||||
|
||||
/* TSCR and RSCR */
|
||||
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||
#define SCR_BRPS(i) (((i) - 1) << 8)
|
||||
|
@ -850,7 +848,12 @@ static int sh_msiof_transfer_one(struct spi_master *master,
|
|||
* DMA supports 32-bit words only, hence pack 8-bit and 16-bit
|
||||
* words, with byte resp. word swapping.
|
||||
*/
|
||||
unsigned int l = min(len, MAX_WDLEN * 4);
|
||||
unsigned int l = 0;
|
||||
|
||||
if (tx_buf)
|
||||
l = min(len, p->tx_fifo_size * 4);
|
||||
if (rx_buf)
|
||||
l = min(len, p->rx_fifo_size * 4);
|
||||
|
||||
if (bits <= 8) {
|
||||
if (l & 3)
|
||||
|
@ -963,7 +966,7 @@ static const struct sh_msiof_chipdata sh_data = {
|
|||
|
||||
static const struct sh_msiof_chipdata r8a779x_data = {
|
||||
.tx_fifo_size = 64,
|
||||
.rx_fifo_size = 256,
|
||||
.rx_fifo_size = 64,
|
||||
.master_flags = SPI_MASTER_MUST_TX,
|
||||
};
|
||||
|
||||
|
@ -1265,11 +1268,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
|
|||
|
||||
static const struct platform_device_id spi_driver_ids[] = {
|
||||
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
|
||||
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7792_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7793_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7794_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||
|
|
|
@ -99,6 +99,8 @@ struct ti_qspi {
|
|||
#define QSPI_INVAL (4 << 16)
|
||||
#define QSPI_WC_CMD_INT_EN (1 << 14)
|
||||
#define QSPI_FLEN(n) ((n - 1) << 0)
|
||||
#define QSPI_WLEN_MAX_BITS 128
|
||||
#define QSPI_WLEN_MAX_BYTES 16
|
||||
|
||||
/* STATUS REGISTER */
|
||||
#define BUSY 0x01
|
||||
|
@ -217,14 +219,16 @@ static inline u32 qspi_is_busy(struct ti_qspi *qspi)
|
|||
|
||||
static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||
{
|
||||
int wlen, count;
|
||||
int wlen, count, xfer_len;
|
||||
unsigned int cmd;
|
||||
const u8 *txbuf;
|
||||
u32 data;
|
||||
|
||||
txbuf = t->tx_buf;
|
||||
cmd = qspi->cmd | QSPI_WR_SNGL;
|
||||
count = t->len;
|
||||
wlen = t->bits_per_word >> 3; /* in bytes */
|
||||
xfer_len = wlen;
|
||||
|
||||
while (count) {
|
||||
if (qspi_is_busy(qspi))
|
||||
|
@ -234,7 +238,29 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
|||
case 1:
|
||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
|
||||
cmd, qspi->dc, *txbuf);
|
||||
writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
|
||||
if (count >= QSPI_WLEN_MAX_BYTES) {
|
||||
u32 *txp = (u32 *)txbuf;
|
||||
|
||||
data = cpu_to_be32(*txp++);
|
||||
writel(data, qspi->base +
|
||||
QSPI_SPI_DATA_REG_3);
|
||||
data = cpu_to_be32(*txp++);
|
||||
writel(data, qspi->base +
|
||||
QSPI_SPI_DATA_REG_2);
|
||||
data = cpu_to_be32(*txp++);
|
||||
writel(data, qspi->base +
|
||||
QSPI_SPI_DATA_REG_1);
|
||||
data = cpu_to_be32(*txp++);
|
||||
writel(data, qspi->base +
|
||||
QSPI_SPI_DATA_REG);
|
||||
xfer_len = QSPI_WLEN_MAX_BYTES;
|
||||
cmd |= QSPI_WLEN(QSPI_WLEN_MAX_BITS);
|
||||
} else {
|
||||
writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
|
||||
cmd = qspi->cmd | QSPI_WR_SNGL;
|
||||
xfer_len = wlen;
|
||||
cmd |= QSPI_WLEN(wlen);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n",
|
||||
|
@ -254,8 +280,8 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
|||
dev_err(qspi->dev, "write timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
txbuf += wlen;
|
||||
count -= wlen;
|
||||
txbuf += xfer_len;
|
||||
count -= xfer_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -237,11 +237,11 @@ static const struct i2c_device_id spi_xcomm_ids[] = {
|
|||
{ "spi-xcomm" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids);
|
||||
|
||||
static struct i2c_driver spi_xcomm_driver = {
|
||||
.driver = {
|
||||
.name = "spi-xcomm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = spi_xcomm_ids,
|
||||
.probe = spi_xcomm_probe,
|
||||
|
|
|
@ -249,19 +249,23 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
|||
xspi->tx_ptr = t->tx_buf;
|
||||
xspi->rx_ptr = t->rx_buf;
|
||||
remaining_words = t->len / xspi->bytes_per_word;
|
||||
reinit_completion(&xspi->done);
|
||||
|
||||
if (xspi->irq >= 0 && remaining_words > xspi->buffer_size) {
|
||||
u32 isr;
|
||||
use_irq = true;
|
||||
xspi->write_fn(XSPI_INTR_TX_EMPTY,
|
||||
xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
||||
/* Enable the global IPIF interrupt */
|
||||
xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
|
||||
xspi->regs + XIPIF_V123B_DGIER_OFFSET);
|
||||
/* Inhibit irq to avoid spurious irqs on tx_empty*/
|
||||
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
|
||||
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
|
||||
xspi->regs + XSPI_CR_OFFSET);
|
||||
/* ACK old irqs (if any) */
|
||||
isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
||||
if (isr)
|
||||
xspi->write_fn(isr,
|
||||
xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
||||
/* Enable the global IPIF interrupt */
|
||||
xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
|
||||
xspi->regs + XIPIF_V123B_DGIER_OFFSET);
|
||||
reinit_completion(&xspi->done);
|
||||
}
|
||||
|
||||
while (remaining_words) {
|
||||
|
@ -302,8 +306,10 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
|||
remaining_words -= n_words;
|
||||
}
|
||||
|
||||
if (use_irq)
|
||||
if (use_irq) {
|
||||
xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET);
|
||||
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
|
||||
}
|
||||
|
||||
return t->len;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2015 Broadcom Corporation
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 (GPL v2)
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* SPI Configuration Register */
|
||||
#define XLP_SPI_CONFIG 0x00
|
||||
#define XLP_SPI_CPHA BIT(0)
|
||||
#define XLP_SPI_CPOL BIT(1)
|
||||
#define XLP_SPI_CS_POL BIT(2)
|
||||
#define XLP_SPI_TXMISO_EN BIT(3)
|
||||
#define XLP_SPI_TXMOSI_EN BIT(4)
|
||||
#define XLP_SPI_RXMISO_EN BIT(5)
|
||||
#define XLP_SPI_CS_LSBFE BIT(10)
|
||||
#define XLP_SPI_RXCAP_EN BIT(11)
|
||||
|
||||
/* SPI Frequency Divider Register */
|
||||
#define XLP_SPI_FDIV 0x04
|
||||
|
||||
/* SPI Command Register */
|
||||
#define XLP_SPI_CMD 0x08
|
||||
#define XLP_SPI_CMD_IDLE_MASK 0x0
|
||||
#define XLP_SPI_CMD_TX_MASK 0x1
|
||||
#define XLP_SPI_CMD_RX_MASK 0x2
|
||||
#define XLP_SPI_CMD_TXRX_MASK 0x3
|
||||
#define XLP_SPI_CMD_CONT BIT(4)
|
||||
#define XLP_SPI_XFR_BITCNT_SHIFT 16
|
||||
|
||||
/* SPI Status Register */
|
||||
#define XLP_SPI_STATUS 0x0c
|
||||
#define XLP_SPI_XFR_PENDING BIT(0)
|
||||
#define XLP_SPI_XFR_DONE BIT(1)
|
||||
#define XLP_SPI_TX_INT BIT(2)
|
||||
#define XLP_SPI_RX_INT BIT(3)
|
||||
#define XLP_SPI_TX_UF BIT(4)
|
||||
#define XLP_SPI_RX_OF BIT(5)
|
||||
#define XLP_SPI_STAT_MASK 0x3f
|
||||
|
||||
/* SPI Interrupt Enable Register */
|
||||
#define XLP_SPI_INTR_EN 0x10
|
||||
#define XLP_SPI_INTR_DONE BIT(0)
|
||||
#define XLP_SPI_INTR_TXTH BIT(1)
|
||||
#define XLP_SPI_INTR_RXTH BIT(2)
|
||||
#define XLP_SPI_INTR_TXUF BIT(3)
|
||||
#define XLP_SPI_INTR_RXOF BIT(4)
|
||||
|
||||
/* SPI FIFO Threshold Register */
|
||||
#define XLP_SPI_FIFO_THRESH 0x14
|
||||
|
||||
/* SPI FIFO Word Count Register */
|
||||
#define XLP_SPI_FIFO_WCNT 0x18
|
||||
#define XLP_SPI_RXFIFO_WCNT_MASK 0xf
|
||||
#define XLP_SPI_TXFIFO_WCNT_MASK 0xf0
|
||||
#define XLP_SPI_TXFIFO_WCNT_SHIFT 4
|
||||
|
||||
/* SPI Transmit Data FIFO Register */
|
||||
#define XLP_SPI_TXDATA_FIFO 0x1c
|
||||
|
||||
/* SPI Receive Data FIFO Register */
|
||||
#define XLP_SPI_RXDATA_FIFO 0x20
|
||||
|
||||
/* SPI System Control Register */
|
||||
#define XLP_SPI_SYSCTRL 0x100
|
||||
#define XLP_SPI_SYS_RESET BIT(0)
|
||||
#define XLP_SPI_SYS_CLKDIS BIT(1)
|
||||
#define XLP_SPI_SYS_PMEN BIT(8)
|
||||
|
||||
#define SPI_CS_OFFSET 0x40
|
||||
#define XLP_SPI_TXRXTH 0x80
|
||||
#define XLP_SPI_FIFO_SIZE 8
|
||||
#define XLP_SPI_MAX_CS 4
|
||||
#define XLP_SPI_DEFAULT_FREQ 133333333
|
||||
#define XLP_SPI_FDIV_MIN 4
|
||||
#define XLP_SPI_FDIV_MAX 65535
|
||||
/*
|
||||
* SPI can transfer only 28 bytes properly at a time. So split the
|
||||
* transfer into 28 bytes size.
|
||||
*/
|
||||
#define XLP_SPI_XFER_SIZE 28
|
||||
|
||||
struct xlp_spi_priv {
|
||||
struct device dev; /* device structure */
|
||||
void __iomem *base; /* spi registers base address */
|
||||
const u8 *tx_buf; /* tx data buffer */
|
||||
u8 *rx_buf; /* rx data buffer */
|
||||
int tx_len; /* tx xfer length */
|
||||
int rx_len; /* rx xfer length */
|
||||
int txerrors; /* TXFIFO underflow count */
|
||||
int rxerrors; /* RXFIFO overflow count */
|
||||
int cs; /* slave device chip select */
|
||||
u32 spi_clk; /* spi clock frequency */
|
||||
bool cmd_cont; /* cs active */
|
||||
struct completion done; /* completion notification */
|
||||
};
|
||||
|
||||
static inline u32 xlp_spi_reg_read(struct xlp_spi_priv *priv,
|
||||
int cs, int regoff)
|
||||
{
|
||||
return readl(priv->base + regoff + cs * SPI_CS_OFFSET);
|
||||
}
|
||||
|
||||
static inline void xlp_spi_reg_write(struct xlp_spi_priv *priv, int cs,
|
||||
int regoff, u32 val)
|
||||
{
|
||||
writel(val, priv->base + regoff + cs * SPI_CS_OFFSET);
|
||||
}
|
||||
|
||||
static inline void xlp_spi_sysctl_write(struct xlp_spi_priv *priv,
|
||||
int regoff, u32 val)
|
||||
{
|
||||
writel(val, priv->base + regoff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup global SPI_SYSCTRL register for all SPI channels.
|
||||
*/
|
||||
static void xlp_spi_sysctl_setup(struct xlp_spi_priv *xspi)
|
||||
{
|
||||
int cs;
|
||||
|
||||
for (cs = 0; cs < XLP_SPI_MAX_CS; cs++)
|
||||
xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL,
|
||||
XLP_SPI_SYS_RESET << cs);
|
||||
xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL, XLP_SPI_SYS_PMEN);
|
||||
}
|
||||
|
||||
static int xlp_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct xlp_spi_priv *xspi;
|
||||
u32 fdiv, cfg;
|
||||
int cs;
|
||||
|
||||
xspi = spi_master_get_devdata(spi->master);
|
||||
cs = spi->chip_select;
|
||||
/*
|
||||
* The value of fdiv must be between 4 and 65535.
|
||||
*/
|
||||
fdiv = DIV_ROUND_UP(xspi->spi_clk, spi->max_speed_hz);
|
||||
if (fdiv > XLP_SPI_FDIV_MAX)
|
||||
fdiv = XLP_SPI_FDIV_MAX;
|
||||
else if (fdiv < XLP_SPI_FDIV_MIN)
|
||||
fdiv = XLP_SPI_FDIV_MIN;
|
||||
|
||||
xlp_spi_reg_write(xspi, cs, XLP_SPI_FDIV, fdiv);
|
||||
xlp_spi_reg_write(xspi, cs, XLP_SPI_FIFO_THRESH, XLP_SPI_TXRXTH);
|
||||
cfg = xlp_spi_reg_read(xspi, cs, XLP_SPI_CONFIG);
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cfg |= XLP_SPI_CPHA;
|
||||
else
|
||||
cfg &= ~XLP_SPI_CPHA;
|
||||
if (spi->mode & SPI_CPOL)
|
||||
cfg |= XLP_SPI_CPOL;
|
||||
else
|
||||
cfg &= ~XLP_SPI_CPOL;
|
||||
if (!(spi->mode & SPI_CS_HIGH))
|
||||
cfg |= XLP_SPI_CS_POL;
|
||||
else
|
||||
cfg &= ~XLP_SPI_CS_POL;
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
cfg |= XLP_SPI_CS_LSBFE;
|
||||
else
|
||||
cfg &= ~XLP_SPI_CS_LSBFE;
|
||||
|
||||
cfg |= XLP_SPI_TXMOSI_EN | XLP_SPI_RXMISO_EN;
|
||||
if (fdiv == 4)
|
||||
cfg |= XLP_SPI_RXCAP_EN;
|
||||
xlp_spi_reg_write(xspi, cs, XLP_SPI_CONFIG, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xlp_spi_read_rxfifo(struct xlp_spi_priv *xspi)
|
||||
{
|
||||
u32 rx_data, rxfifo_cnt;
|
||||
int i, j, nbytes;
|
||||
|
||||
rxfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT);
|
||||
rxfifo_cnt &= XLP_SPI_RXFIFO_WCNT_MASK;
|
||||
while (rxfifo_cnt) {
|
||||
rx_data = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_RXDATA_FIFO);
|
||||
j = 0;
|
||||
nbytes = min(xspi->rx_len, 4);
|
||||
for (i = nbytes - 1; i >= 0; i--, j++)
|
||||
xspi->rx_buf[i] = (rx_data >> (j * 8)) & 0xff;
|
||||
|
||||
xspi->rx_len -= nbytes;
|
||||
xspi->rx_buf += nbytes;
|
||||
rxfifo_cnt--;
|
||||
}
|
||||
}
|
||||
|
||||
static void xlp_spi_fill_txfifo(struct xlp_spi_priv *xspi)
|
||||
{
|
||||
u32 tx_data, txfifo_cnt;
|
||||
int i, j, nbytes;
|
||||
|
||||
txfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT);
|
||||
txfifo_cnt &= XLP_SPI_TXFIFO_WCNT_MASK;
|
||||
txfifo_cnt >>= XLP_SPI_TXFIFO_WCNT_SHIFT;
|
||||
while (xspi->tx_len && (txfifo_cnt < XLP_SPI_FIFO_SIZE)) {
|
||||
j = 0;
|
||||
tx_data = 0;
|
||||
nbytes = min(xspi->tx_len, 4);
|
||||
for (i = nbytes - 1; i >= 0; i--, j++)
|
||||
tx_data |= xspi->tx_buf[i] << (j * 8);
|
||||
|
||||
xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_TXDATA_FIFO, tx_data);
|
||||
xspi->tx_len -= nbytes;
|
||||
xspi->tx_buf += nbytes;
|
||||
txfifo_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t xlp_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct xlp_spi_priv *xspi = dev_id;
|
||||
u32 stat;
|
||||
|
||||
stat = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_STATUS) &
|
||||
XLP_SPI_STAT_MASK;
|
||||
if (!stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (stat & XLP_SPI_TX_INT) {
|
||||
if (xspi->tx_len)
|
||||
xlp_spi_fill_txfifo(xspi);
|
||||
if (stat & XLP_SPI_TX_UF)
|
||||
xspi->txerrors++;
|
||||
}
|
||||
|
||||
if (stat & XLP_SPI_RX_INT) {
|
||||
if (xspi->rx_len)
|
||||
xlp_spi_read_rxfifo(xspi);
|
||||
if (stat & XLP_SPI_RX_OF)
|
||||
xspi->rxerrors++;
|
||||
}
|
||||
|
||||
/* write status back to clear interrupts */
|
||||
xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_STATUS, stat);
|
||||
if (stat & XLP_SPI_XFR_DONE)
|
||||
complete(&xspi->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void xlp_spi_send_cmd(struct xlp_spi_priv *xspi, int xfer_len,
|
||||
int cmd_cont)
|
||||
{
|
||||
u32 cmd = 0;
|
||||
|
||||
if (xspi->tx_buf)
|
||||
cmd |= XLP_SPI_CMD_TX_MASK;
|
||||
if (xspi->rx_buf)
|
||||
cmd |= XLP_SPI_CMD_RX_MASK;
|
||||
if (cmd_cont)
|
||||
cmd |= XLP_SPI_CMD_CONT;
|
||||
cmd |= ((xfer_len * 8 - 1) << XLP_SPI_XFR_BITCNT_SHIFT);
|
||||
xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_CMD, cmd);
|
||||
}
|
||||
|
||||
static int xlp_spi_xfer_block(struct xlp_spi_priv *xs,
|
||||
const unsigned char *tx_buf,
|
||||
unsigned char *rx_buf, int xfer_len, int cmd_cont)
|
||||
{
|
||||
int timeout;
|
||||
u32 intr_mask = 0;
|
||||
|
||||
xs->tx_buf = tx_buf;
|
||||
xs->rx_buf = rx_buf;
|
||||
xs->tx_len = (xs->tx_buf == NULL) ? 0 : xfer_len;
|
||||
xs->rx_len = (xs->rx_buf == NULL) ? 0 : xfer_len;
|
||||
xs->txerrors = xs->rxerrors = 0;
|
||||
|
||||
/* fill TXDATA_FIFO, then send the CMD */
|
||||
if (xs->tx_len)
|
||||
xlp_spi_fill_txfifo(xs);
|
||||
|
||||
xlp_spi_send_cmd(xs, xfer_len, cmd_cont);
|
||||
|
||||
/*
|
||||
* We are getting some spurious tx interrupts, so avoid enabling
|
||||
* tx interrupts when only rx is in process.
|
||||
* Enable all the interrupts in tx case.
|
||||
*/
|
||||
if (xs->tx_len)
|
||||
intr_mask |= XLP_SPI_INTR_TXTH | XLP_SPI_INTR_TXUF |
|
||||
XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF;
|
||||
else
|
||||
intr_mask |= XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF;
|
||||
|
||||
intr_mask |= XLP_SPI_INTR_DONE;
|
||||
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, intr_mask);
|
||||
|
||||
timeout = wait_for_completion_timeout(&xs->done,
|
||||
msecs_to_jiffies(1000));
|
||||
/* Disable interrupts */
|
||||
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, 0x0);
|
||||
if (!timeout) {
|
||||
dev_err(&xs->dev, "xfer timedout!\n");
|
||||
goto out;
|
||||
}
|
||||
if (xs->txerrors || xs->rxerrors)
|
||||
dev_err(&xs->dev, "Over/Underflow rx %d tx %d xfer %d!\n",
|
||||
xs->rxerrors, xs->txerrors, xfer_len);
|
||||
|
||||
return xfer_len;
|
||||
out:
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int xlp_spi_txrx_bufs(struct xlp_spi_priv *xs, struct spi_transfer *t)
|
||||
{
|
||||
int bytesleft, sz;
|
||||
unsigned char *rx_buf;
|
||||
const unsigned char *tx_buf;
|
||||
|
||||
tx_buf = t->tx_buf;
|
||||
rx_buf = t->rx_buf;
|
||||
bytesleft = t->len;
|
||||
while (bytesleft) {
|
||||
if (bytesleft > XLP_SPI_XFER_SIZE)
|
||||
sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf,
|
||||
XLP_SPI_XFER_SIZE, 1);
|
||||
else
|
||||
sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf,
|
||||
bytesleft, xs->cmd_cont);
|
||||
if (sz < 0)
|
||||
return sz;
|
||||
bytesleft -= sz;
|
||||
if (tx_buf)
|
||||
tx_buf += sz;
|
||||
if (rx_buf)
|
||||
rx_buf += sz;
|
||||
}
|
||||
return bytesleft;
|
||||
}
|
||||
|
||||
static int xlp_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct xlp_spi_priv *xspi = spi_master_get_devdata(master);
|
||||
int ret = 0;
|
||||
|
||||
xspi->cs = spi->chip_select;
|
||||
xspi->dev = spi->dev;
|
||||
|
||||
if (spi_transfer_is_last(master, t))
|
||||
xspi->cmd_cont = 0;
|
||||
else
|
||||
xspi->cmd_cont = 1;
|
||||
|
||||
if (xlp_spi_txrx_bufs(xspi, t))
|
||||
ret = -EIO;
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xlp_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct xlp_spi_priv *xspi;
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
int irq, err;
|
||||
|
||||
xspi = devm_kzalloc(&pdev->dev, sizeof(*xspi), GFP_KERNEL);
|
||||
if (!xspi)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
xspi->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(xspi->base))
|
||||
return PTR_ERR(xspi->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no IRQ resource found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = devm_request_irq(&pdev->dev, irq, xlp_spi_interrupt, 0,
|
||||
pdev->name, xspi);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to request irq %d\n", irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "could not get spi clock\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
xspi->spi_clk = clk_get_rate(clk);
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, 0);
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "could not alloc master\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
master->bus_num = 0;
|
||||
master->num_chipselect = XLP_SPI_MAX_CS;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->setup = xlp_spi_setup;
|
||||
master->transfer_one = xlp_spi_transfer_one;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
init_completion(&xspi->done);
|
||||
spi_master_set_devdata(master, xspi);
|
||||
xlp_spi_sysctl_setup(xspi);
|
||||
|
||||
/* register spi controller */
|
||||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "spi register master failed!\n");
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id xlp_spi_dt_id[] = {
|
||||
{ .compatible = "netlogic,xlp832-spi" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver xlp_spi_driver = {
|
||||
.probe = xlp_spi_probe,
|
||||
.driver = {
|
||||
.name = "xlp-spi",
|
||||
.of_match_table = xlp_spi_dt_id,
|
||||
},
|
||||
};
|
||||
module_platform_driver(xlp_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
|
||||
MODULE_DESCRIPTION("Netlogic XLP SPI controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
|
|||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
#define SPI_STATISTICS_ATTRS(field, file) \
|
||||
static ssize_t spi_master_##field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct spi_master *master = container_of(dev, \
|
||||
struct spi_master, dev); \
|
||||
return spi_statistics_##field##_show(&master->statistics, buf); \
|
||||
} \
|
||||
static struct device_attribute dev_attr_spi_master_##field = { \
|
||||
.attr = { .name = file, .mode = S_IRUGO }, \
|
||||
.show = spi_master_##field##_show, \
|
||||
}; \
|
||||
static ssize_t spi_device_##field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct spi_device *spi = container_of(dev, \
|
||||
struct spi_device, dev); \
|
||||
return spi_statistics_##field##_show(&spi->statistics, buf); \
|
||||
} \
|
||||
static struct device_attribute dev_attr_spi_device_##field = { \
|
||||
.attr = { .name = file, .mode = S_IRUGO }, \
|
||||
.show = spi_device_##field##_show, \
|
||||
}
|
||||
|
||||
#define SPI_STATISTICS_SHOW_NAME(name, file, field, format_string) \
|
||||
static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
|
||||
char *buf) \
|
||||
{ \
|
||||
unsigned long flags; \
|
||||
ssize_t len; \
|
||||
spin_lock_irqsave(&stat->lock, flags); \
|
||||
len = sprintf(buf, format_string, stat->field); \
|
||||
spin_unlock_irqrestore(&stat->lock, flags); \
|
||||
return len; \
|
||||
} \
|
||||
SPI_STATISTICS_ATTRS(name, file)
|
||||
|
||||
#define SPI_STATISTICS_SHOW(field, format_string) \
|
||||
SPI_STATISTICS_SHOW_NAME(field, __stringify(field), \
|
||||
field, format_string)
|
||||
|
||||
SPI_STATISTICS_SHOW(messages, "%lu");
|
||||
SPI_STATISTICS_SHOW(transfers, "%lu");
|
||||
SPI_STATISTICS_SHOW(errors, "%lu");
|
||||
SPI_STATISTICS_SHOW(timedout, "%lu");
|
||||
|
||||
SPI_STATISTICS_SHOW(spi_sync, "%lu");
|
||||
SPI_STATISTICS_SHOW(spi_sync_immediate, "%lu");
|
||||
SPI_STATISTICS_SHOW(spi_async, "%lu");
|
||||
|
||||
SPI_STATISTICS_SHOW(bytes, "%llu");
|
||||
SPI_STATISTICS_SHOW(bytes_rx, "%llu");
|
||||
SPI_STATISTICS_SHOW(bytes_tx, "%llu");
|
||||
|
||||
static struct attribute *spi_dev_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(spi_dev);
|
||||
|
||||
static const struct attribute_group spi_dev_group = {
|
||||
.attrs = spi_dev_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *spi_device_statistics_attrs[] = {
|
||||
&dev_attr_spi_device_messages.attr,
|
||||
&dev_attr_spi_device_transfers.attr,
|
||||
&dev_attr_spi_device_errors.attr,
|
||||
&dev_attr_spi_device_timedout.attr,
|
||||
&dev_attr_spi_device_spi_sync.attr,
|
||||
&dev_attr_spi_device_spi_sync_immediate.attr,
|
||||
&dev_attr_spi_device_spi_async.attr,
|
||||
&dev_attr_spi_device_bytes.attr,
|
||||
&dev_attr_spi_device_bytes_rx.attr,
|
||||
&dev_attr_spi_device_bytes_tx.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group spi_device_statistics_group = {
|
||||
.name = "statistics",
|
||||
.attrs = spi_device_statistics_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *spi_dev_groups[] = {
|
||||
&spi_dev_group,
|
||||
&spi_device_statistics_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *spi_master_statistics_attrs[] = {
|
||||
&dev_attr_spi_master_messages.attr,
|
||||
&dev_attr_spi_master_transfers.attr,
|
||||
&dev_attr_spi_master_errors.attr,
|
||||
&dev_attr_spi_master_timedout.attr,
|
||||
&dev_attr_spi_master_spi_sync.attr,
|
||||
&dev_attr_spi_master_spi_sync_immediate.attr,
|
||||
&dev_attr_spi_master_spi_async.attr,
|
||||
&dev_attr_spi_master_bytes.attr,
|
||||
&dev_attr_spi_master_bytes_rx.attr,
|
||||
&dev_attr_spi_master_bytes_tx.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group spi_master_statistics_group = {
|
||||
.name = "statistics",
|
||||
.attrs = spi_master_statistics_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *spi_master_groups[] = {
|
||||
&spi_master_statistics_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
|
||||
struct spi_transfer *xfer,
|
||||
struct spi_master *master)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&stats->lock, flags);
|
||||
|
||||
stats->transfers++;
|
||||
|
||||
stats->bytes += xfer->len;
|
||||
if ((xfer->tx_buf) &&
|
||||
(xfer->tx_buf != master->dummy_tx))
|
||||
stats->bytes_tx += xfer->len;
|
||||
if ((xfer->rx_buf) &&
|
||||
(xfer->rx_buf != master->dummy_rx))
|
||||
stats->bytes_rx += xfer->len;
|
||||
|
||||
spin_unlock_irqrestore(&stats->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats);
|
||||
|
||||
/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
|
||||
* and the sysfs version makes coldplug work too.
|
||||
|
@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
|
|||
spi->dev.bus = &spi_bus_type;
|
||||
spi->dev.release = spidev_release;
|
||||
spi->cs_gpio = -ENOENT;
|
||||
|
||||
spin_lock_init(&spi->statistics.lock);
|
||||
|
||||
device_initialize(&spi->dev);
|
||||
return spi;
|
||||
}
|
||||
|
@ -476,21 +609,30 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
|
|||
enum dma_data_direction dir)
|
||||
{
|
||||
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
||||
const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len;
|
||||
const int sgs = DIV_ROUND_UP(len, desc_len);
|
||||
int desc_len;
|
||||
int sgs;
|
||||
struct page *vm_page;
|
||||
void *sg_buf;
|
||||
size_t min;
|
||||
int i, ret;
|
||||
|
||||
if (vmalloced_buf) {
|
||||
desc_len = PAGE_SIZE;
|
||||
sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);
|
||||
} else {
|
||||
desc_len = master->max_dma_len;
|
||||
sgs = DIV_ROUND_UP(len, desc_len);
|
||||
}
|
||||
|
||||
ret = sg_alloc_table(sgt, sgs, GFP_KERNEL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < sgs; i++) {
|
||||
min = min_t(size_t, len, desc_len);
|
||||
|
||||
if (vmalloced_buf) {
|
||||
min = min_t(size_t,
|
||||
len, desc_len - offset_in_page(buf));
|
||||
vm_page = vmalloc_to_page(buf);
|
||||
if (!vm_page) {
|
||||
sg_free_table(sgt);
|
||||
|
@ -499,6 +641,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
|
|||
sg_set_page(&sgt->sgl[i], vm_page,
|
||||
min, offset_in_page(buf));
|
||||
} else {
|
||||
min = min_t(size_t, len, desc_len);
|
||||
sg_buf = buf;
|
||||
sg_set_buf(&sgt->sgl[i], sg_buf, min);
|
||||
}
|
||||
|
@ -539,8 +682,15 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
|
|||
if (!master->can_dma)
|
||||
return 0;
|
||||
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
if (master->dma_tx)
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
else
|
||||
tx_dev = &master->dev;
|
||||
|
||||
if (master->dma_rx)
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
else
|
||||
rx_dev = &master->dev;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
if (!master->can_dma(master, msg->spi, xfer))
|
||||
|
@ -579,8 +729,15 @@ static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
|
|||
if (!master->cur_msg_mapped || !master->can_dma)
|
||||
return 0;
|
||||
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
if (master->dma_tx)
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
else
|
||||
tx_dev = &master->dev;
|
||||
|
||||
if (master->dma_rx)
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
else
|
||||
rx_dev = &master->dev;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
if (!master->can_dma(master, msg->spi, xfer))
|
||||
|
@ -689,17 +846,29 @@ static int spi_transfer_one_message(struct spi_master *master,
|
|||
bool keep_cs = false;
|
||||
int ret = 0;
|
||||
unsigned long ms = 1;
|
||||
struct spi_statistics *statm = &master->statistics;
|
||||
struct spi_statistics *stats = &msg->spi->statistics;
|
||||
|
||||
spi_set_cs(msg->spi, true);
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
trace_spi_transfer_start(msg, xfer);
|
||||
|
||||
spi_statistics_add_transfer_stats(statm, xfer, master);
|
||||
spi_statistics_add_transfer_stats(stats, xfer, master);
|
||||
|
||||
if (xfer->tx_buf || xfer->rx_buf) {
|
||||
reinit_completion(&master->xfer_completion);
|
||||
|
||||
ret = master->transfer_one(master, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm,
|
||||
errors);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats,
|
||||
errors);
|
||||
dev_err(&msg->spi->dev,
|
||||
"SPI transfer failed: %d\n", ret);
|
||||
goto out;
|
||||
|
@ -715,6 +884,10 @@ static int spi_transfer_one_message(struct spi_master *master,
|
|||
}
|
||||
|
||||
if (ms == 0) {
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm,
|
||||
timedout);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats,
|
||||
timedout);
|
||||
dev_err(&msg->spi->dev,
|
||||
"SPI transfer timed out\n");
|
||||
msg->status = -ETIMEDOUT;
|
||||
|
@ -1416,10 +1589,10 @@ static struct class spi_master_class = {
|
|||
.name = "spi_master",
|
||||
.owner = THIS_MODULE,
|
||||
.dev_release = spi_master_release,
|
||||
.dev_groups = spi_master_groups,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* spi_alloc_master - allocate SPI master controller
|
||||
* @dev: the controller, possibly using the platform_bus
|
||||
|
@ -1585,6 +1758,8 @@ int spi_register_master(struct spi_master *master)
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
/* add statistics */
|
||||
spin_lock_init(&master->statistics.lock);
|
||||
|
||||
mutex_lock(&board_lock);
|
||||
list_add_tail(&master->list, &spi_master_list);
|
||||
|
@ -1740,6 +1915,20 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master);
|
|||
* other core methods are currently defined as inline functions.
|
||||
*/
|
||||
|
||||
static int __spi_validate_bits_per_word(struct spi_master *master, u8 bits_per_word)
|
||||
{
|
||||
if (master->bits_per_word_mask) {
|
||||
/* Only 32 bits fit in the mask */
|
||||
if (bits_per_word > 32)
|
||||
return -EINVAL;
|
||||
if (!(master->bits_per_word_mask &
|
||||
SPI_BPW_MASK(bits_per_word)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_setup - setup SPI mode and clock rate
|
||||
* @spi: the device whose settings are being modified
|
||||
|
@ -1798,6 +1987,9 @@ int spi_setup(struct spi_device *spi)
|
|||
if (!spi->bits_per_word)
|
||||
spi->bits_per_word = 8;
|
||||
|
||||
if (__spi_validate_bits_per_word(spi->master, spi->bits_per_word))
|
||||
return -EINVAL;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
spi->max_speed_hz = spi->master->max_speed_hz;
|
||||
|
||||
|
@ -1860,19 +2052,15 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
|||
|
||||
if (!xfer->speed_hz)
|
||||
xfer->speed_hz = spi->max_speed_hz;
|
||||
if (!xfer->speed_hz)
|
||||
xfer->speed_hz = master->max_speed_hz;
|
||||
|
||||
if (master->max_speed_hz &&
|
||||
xfer->speed_hz > master->max_speed_hz)
|
||||
xfer->speed_hz = master->max_speed_hz;
|
||||
|
||||
if (master->bits_per_word_mask) {
|
||||
/* Only 32 bits fit in the mask */
|
||||
if (xfer->bits_per_word > 32)
|
||||
return -EINVAL;
|
||||
if (!(master->bits_per_word_mask &
|
||||
BIT(xfer->bits_per_word - 1)))
|
||||
return -EINVAL;
|
||||
}
|
||||
if (__spi_validate_bits_per_word(master, xfer->bits_per_word))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* SPI transfer length should be multiple of SPI word size
|
||||
|
@ -1939,6 +2127,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
|||
|
||||
message->spi = spi;
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);
|
||||
|
||||
trace_spi_message_submit(message);
|
||||
|
||||
return master->transfer(spi, message);
|
||||
|
@ -2075,6 +2266,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message,
|
|||
message->context = &done;
|
||||
message->spi = spi;
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
|
||||
|
||||
if (!bus_locked)
|
||||
mutex_lock(&master->bus_lock_mutex);
|
||||
|
||||
|
@ -2102,8 +2296,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message,
|
|||
/* Push out the messages in the calling context if we
|
||||
* can.
|
||||
*/
|
||||
if (master->transfer == spi_queued_transfer)
|
||||
if (master->transfer == spi_queued_transfer) {
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
|
||||
spi_sync_immediate);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
|
||||
spi_sync_immediate);
|
||||
__spi_pump_messages(master, false);
|
||||
}
|
||||
|
||||
wait_for_completion(&done);
|
||||
status = message->status;
|
||||
|
|
|
@ -602,11 +602,11 @@ static int spidev_open(struct inode *inode, struct file *filp)
|
|||
if (!spidev->tx_buffer) {
|
||||
spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||
if (!spidev->tx_buffer) {
|
||||
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
||||
status = -ENOMEM;
|
||||
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
||||
status = -ENOMEM;
|
||||
goto err_find_dev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spidev->rx_buffer) {
|
||||
spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||
|
@ -709,7 +709,7 @@ static int spidev_probe(struct spi_device *spi)
|
|||
|
||||
/*
|
||||
* spidev should never be referenced in DT without a specific
|
||||
* compatbile string, it is a Linux implementation thing
|
||||
* compatible string, it is a Linux implementation thing
|
||||
* rather than a description of the hardware.
|
||||
*/
|
||||
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
|
||||
|
|
|
@ -49,6 +49,7 @@ struct davinci_spi_platform_data {
|
|||
u8 num_chipselect;
|
||||
u8 intr_line;
|
||||
u8 *chip_sel;
|
||||
u8 prescaler_limit;
|
||||
bool cshold_bug;
|
||||
enum dma_event_q dma_event_q;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* MTK SPI bus driver definitions
|
||||
*
|
||||
* Copyright (c) 2015 MediaTek Inc.
|
||||
* Author: Leilk Liu <leilk.liu@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef ____LINUX_PLATFORM_DATA_SPI_MTK_H
|
||||
#define ____LINUX_PLATFORM_DATA_SPI_MTK_H
|
||||
|
||||
/* Board specific platform_data */
|
||||
struct mtk_chip_config {
|
||||
u32 tx_mlsb;
|
||||
u32 rx_mlsb;
|
||||
};
|
||||
#endif
|
|
@ -197,6 +197,7 @@ enum pxa_ssp_type {
|
|||
QUARK_X1000_SSP,
|
||||
LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */
|
||||
LPSS_BYT_SSP,
|
||||
LPSS_SPT_SSP,
|
||||
};
|
||||
|
||||
struct ssp_device {
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <linux/scatterlist.h>
|
||||
|
||||
struct dma_chan;
|
||||
struct spi_master;
|
||||
struct spi_transfer;
|
||||
|
||||
/*
|
||||
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
||||
|
@ -30,6 +32,59 @@ struct dma_chan;
|
|||
*/
|
||||
extern struct bus_type spi_bus_type;
|
||||
|
||||
/**
|
||||
* struct spi_statistics - statistics for spi transfers
|
||||
* @clock: lock protecting this structure
|
||||
*
|
||||
* @messages: number of spi-messages handled
|
||||
* @transfers: number of spi_transfers handled
|
||||
* @errors: number of errors during spi_transfer
|
||||
* @timedout: number of timeouts during spi_transfer
|
||||
*
|
||||
* @spi_sync: number of times spi_sync is used
|
||||
* @spi_sync_immediate:
|
||||
* number of times spi_sync is executed immediately
|
||||
* in calling context without queuing and scheduling
|
||||
* @spi_async: number of times spi_async is used
|
||||
*
|
||||
* @bytes: number of bytes transferred to/from device
|
||||
* @bytes_tx: number of bytes sent to device
|
||||
* @bytes_rx: number of bytes received from device
|
||||
*
|
||||
*/
|
||||
struct spi_statistics {
|
||||
spinlock_t lock; /* lock for the whole structure */
|
||||
|
||||
unsigned long messages;
|
||||
unsigned long transfers;
|
||||
unsigned long errors;
|
||||
unsigned long timedout;
|
||||
|
||||
unsigned long spi_sync;
|
||||
unsigned long spi_sync_immediate;
|
||||
unsigned long spi_async;
|
||||
|
||||
unsigned long long bytes;
|
||||
unsigned long long bytes_rx;
|
||||
unsigned long long bytes_tx;
|
||||
|
||||
};
|
||||
|
||||
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
|
||||
struct spi_transfer *xfer,
|
||||
struct spi_master *master);
|
||||
|
||||
#define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \
|
||||
do { \
|
||||
unsigned long flags; \
|
||||
spin_lock_irqsave(&(stats)->lock, flags); \
|
||||
(stats)->field += count; \
|
||||
spin_unlock_irqrestore(&(stats)->lock, flags); \
|
||||
} while (0)
|
||||
|
||||
#define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \
|
||||
SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1)
|
||||
|
||||
/**
|
||||
* struct spi_device - Master side proxy for an SPI slave device
|
||||
* @dev: Driver model representation of the device.
|
||||
|
@ -60,6 +115,8 @@ extern struct bus_type spi_bus_type;
|
|||
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
|
||||
* when not using a GPIO line)
|
||||
*
|
||||
* @statistics: statistics for the spi_device
|
||||
*
|
||||
* A @spi_device is used to interchange data between an SPI slave
|
||||
* (usually a discrete chip) and CPU memory.
|
||||
*
|
||||
|
@ -98,6 +155,9 @@ struct spi_device {
|
|||
char modalias[SPI_NAME_SIZE];
|
||||
int cs_gpio; /* chip select gpio */
|
||||
|
||||
/* the statistics */
|
||||
struct spi_statistics statistics;
|
||||
|
||||
/*
|
||||
* likely need more hooks for more protocol options affecting how
|
||||
* the controller talks to each chip, like:
|
||||
|
@ -296,6 +356,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
|
||||
* number. Any individual value may be -ENOENT for CS lines that
|
||||
* are not GPIOs (driven by the SPI controller itself).
|
||||
* @statistics: statistics for the spi_master
|
||||
* @dma_tx: DMA transmit channel
|
||||
* @dma_rx: DMA receive channel
|
||||
* @dummy_rx: dummy receive buffer for full-duplex devices
|
||||
|
@ -452,6 +513,9 @@ struct spi_master {
|
|||
/* gpio chip select */
|
||||
int *cs_gpios;
|
||||
|
||||
/* statistics */
|
||||
struct spi_statistics statistics;
|
||||
|
||||
/* DMA channels for use with core dmaengine helpers */
|
||||
struct dma_chan *dma_tx;
|
||||
struct dma_chan *dma_rx;
|
||||
|
|
Loading…
Reference in New Issue