1
0
Fork 0

spi: Updates for v4.9

The SPI subsystem has also had quite a quiet release, though with a
 fairly large set of per-driver changes and several new drivers.  The
 bulk of the changes are:
 
  - Lots and lots of cleanups and improvements for the fsl-espi driver.
  - New drivers for Broadcom MSPI/iProc/STB, Cavium ThunderX and J-Core.
 -----BEGIN PGP SIGNATURE-----
 
 iQEwBAABCAAaBQJX84MLExxicm9vbmllQGtlcm5lbC5vcmcACgkQJNaLcl1Uh9CD
 Ywf/bCrjKApw6Yvfww3HmoR91LoDhax56ypS8H45H6UNWNqEcP0egBhbj0WGTSs6
 AVhNlebullti+FlqKbCBaldDZehEGHDvBLIfj7fVIqPS+RCfBfEVjGIESE5MRx+Q
 l0hjD3IwrwB74cMpZ1R3K7ecnsPbK7vouoFwyESw3cmW8I7YfjO2sKtQfzCyxlej
 qFfKwbhGqU6xkUCgx3+x4y6g7x5TRdwbWeDEVFJEeyKJ+e3BQRCg8YNs+vWWMM6P
 qjXI1Sd3AmlfONQb/vUr0MtlRPbOmDKX/Ibu/OSWCUI4QoPQdxCP+OsE0Wz9OIOp
 5a7IqQrWatUAXZ4AhojmLAjLLA==
 =zKMh
 -----END PGP SIGNATURE-----

Merge tag 'spi-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "The SPI subsystem has also had quite a quiet release, though with a
  fairly large set of per-driver changes and several new drivers. The
  bulk of the changes are:

   - lots and lots of cleanups and improvements for the fsl-espi driver

   - new drivers for Broadcom MSPI/iProc/STB, Cavium ThunderX and
     J-Core"

* tag 'spi-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (80 commits)
  spi: sc18is602: Change gpiod_set_value to gpiod_set_value_cansleep
  spi: pxa2xx: Fix build error because of missing header
  spi: imx: fix error return code in spi_imx_probe()
  spi: pxa2xx: Add support for GPIO descriptor chip selects
  spi: imx: Gracefully handle NULL master->cs_gpios
  spi: iproc-qspi: Add Broadcom iProc SoCs support
  spi: fsl-espi: improve return value handling in fsl_espi_probe
  spi: fsl-espi: simplify of_fsl_espi_probe
  spi: fsl-espi: remove unused variable in fsl_espi_setup
  spi: bcm-qspi: Fix error return code in bcm_qspi_probe()
  spi: bcm-qspi: Fix return value check in bcm_qspi_probe()
  spi: bcm-qspi: fix suspend/resume #ifdef
  spi: bcm-qspi: don't include linux/mtd/cfi.h
  spi: core: Use spi_sync_transfer() in spi_write()/spi_read()
  spi: fsl-espi: improve and extend register bit definitions
  spi: fsl-espi: align register access with other drivers
  spi: fsl-espi: improve and simplify interrupt handler
  spi: fsl-espi: simplify fsl_espi_setup_transfer
  spi: imx: support loopback mode on imx35
  spi: imx: set spi_bus_clk for mx1, mx31 and mx35
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-10-04 11:20:10 -07:00
commit 58e4411b2d
37 changed files with 3066 additions and 557 deletions

View File

@ -0,0 +1,233 @@
Broadcom SPI controller
The Broadcom SPI controller is a SPI master found on various SOCs, including
BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits
of :
MSPI : SPI master controller can read and write to a SPI slave device
BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration
for flash reads and be configured to do single, double, quad lane
io with 3-byte and 4-byte addressing support.
Supported Broadcom SoCs have one instance of MSPI+BSPI controller IP.
MSPI master can be used wihout BSPI. BRCMSTB SoCs have an additional instance
of a MSPI master without the BSPI to use with non flash slave devices that
use SPI protocol.
Required properties:
- #address-cells:
Must be <1>, as required by generic SPI binding.
- #size-cells:
Must be <0>, also as required by generic SPI binding.
- compatible:
Must be one of :
"brcm,spi-bcm-qspi", "brcm,spi-brcmstb-qspi" : MSPI+BSPI on BRCMSTB SoCs
"brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
BRCMSTB SoCs
"brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi" : MSPI+BSPI on Cygnus, NSP
"brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi" : NS2 SoCs
- reg:
Define the bases and ranges of the associated I/O address spaces.
The required range is MSPI controller registers.
- reg-names:
First name does not matter, but must be reserved for the MSPI controller
register range as mentioned in 'reg' above, and will typically contain
- "bspi_regs": BSPI register range, not required with compatible
"spi-brcmstb-mspi"
- "mspi_regs": MSPI register range is required for compatible strings
- "intr_regs", "intr_status_reg" : Interrupt and status register for
NSP, NS2, Cygnus SoC
- interrupts
The interrupts used by the MSPI and/or BSPI controller.
- interrupt-names:
Names of interrupts associated with MSPI
- "mspi_halted" :
- "mspi_done": Indicates that the requested SPI operation is complete.
- "spi_lr_fullness_reached" : Linear read BSPI pipe full
- "spi_lr_session_aborted" : Linear read BSPI pipe aborted
- "spi_lr_impatient" : Linear read BSPI requested when pipe empty
- "spi_lr_session_done" : Linear read BSPI session done
- clocks:
A phandle to the reference clock for this block.
Optional properties:
- native-endian
Defined when using BE SoC and device uses BE register read/write
Recommended optional m25p80 properties:
- spi-rx-bus-width: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Examples:
BRCMSTB SoC Example:
SPI Master (MSPI+BSPI) for SPI-NOR access:
spi@f03e3400 {
#address-cells = <0x1>;
#size-cells = <0x0>;
compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-qspi";
reg = <0xf03e0920 0x4 0xf03e3400 0x188 0xf03e3200 0x50>;
reg-names = "cs_reg", "mspi", "bspi";
interrupts = <0x6 0x5 0x4 0x3 0x2 0x1 0x0>;
interrupt-parent = <0x1c>;
interrupt-names = "mspi_halted",
"mspi_done",
"spi_lr_overread",
"spi_lr_session_done",
"spi_lr_impatient",
"spi_lr_session_aborted",
"spi_lr_fullness_reached";
clocks = <&hif_spi>;
clock-names = "sw_spi";
m25p80@0 {
#size-cells = <0x2>;
#address-cells = <0x2>;
compatible = "m25p80";
reg = <0x0>;
spi-max-frequency = <0x2625a00>;
spi-cpol;
spi-cpha;
m25p,fast-read;
flash0.bolt@0 {
reg = <0x0 0x0 0x0 0x100000>;
};
flash0.macadr@100000 {
reg = <0x0 0x100000 0x0 0x10000>;
};
flash0.nvram@110000 {
reg = <0x0 0x110000 0x0 0x10000>;
};
flash0.kernel@120000 {
reg = <0x0 0x120000 0x0 0x400000>;
};
flash0.devtree@520000 {
reg = <0x0 0x520000 0x0 0x10000>;
};
flash0.splash@530000 {
reg = <0x0 0x530000 0x0 0x80000>;
};
flash0@0 {
reg = <0x0 0x0 0x0 0x4000000>;
};
};
};
MSPI master for any SPI device :
spi@f0416000 {
#address-cells = <1>;
#size-cells = <0>;
clocks = <&upg_fixed>;
compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-mspi";
reg = <0xf0416000 0x180>;
reg-names = "mspi";
interrupts = <0x14>;
interrupt-parent = <&irq0_aon_intc>;
interrupt-names = "mspi_done";
};
iProc SoC Example:
qspi: spi@18027200 {
compatible = "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi";
reg = <0x18027200 0x184>,
<0x18027000 0x124>,
<0x1811c408 0x004>,
<0x180273a0 0x01c>;
reg-names = "mspi_regs", "bspi_regs", "intr_regs", "intr_status_reg";
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names =
"spi_lr_fullness_reached",
"spi_lr_session_aborted",
"spi_lr_impatient",
"spi_lr_session_done",
"mspi_done",
"mspi_halted";
clocks = <&iprocmed>;
clock-names = "iprocmed";
num-cs = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
NS2 SoC Example:
qspi: spi@66470200 {
compatible = "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi";
reg = <0x66470200 0x184>,
<0x66470000 0x124>,
<0x67017408 0x004>,
<0x664703a0 0x01c>;
reg-names = "mspi", "bspi", "intr_regs",
"intr_status_reg";
interrupts = <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "spi_l1_intr";
clocks = <&iprocmed>;
clock-names = "iprocmed";
num-cs = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
m25p80 node for NSP, NS2
&qspi {
flash: m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "m25p80";
reg = <0x0>;
spi-max-frequency = <12500000>;
m25p,fast-read;
spi-cpol;
spi-cpha;
partition@0 {
label = "boot";
reg = <0x00000000 0x000a0000>;
};
partition@a0000 {
label = "env";
reg = <0x000a0000 0x00060000>;
};
partition@100000 {
label = "system";
reg = <0x00100000 0x00600000>;
};
partition@700000 {
label = "rootfs";
reg = <0x00700000 0x01900000>;
};
};

View File

@ -0,0 +1,34 @@
J-Core SPI master
Required properties:
- compatible: Must be "jcore,spi2".
- reg: Memory region for registers.
- #address-cells: Must be 1.
- #size-cells: Must be 0.
Optional properties:
- clocks: If a phandle named "ref_clk" is present, SPI clock speed
programming is relative to the frequency of the indicated clock.
Necessary only if the input clock rate is something other than a
fixed 50 MHz.
- clock-names: Clock names, one for each phandle in clocks.
See spi-bus.txt for additional properties not specific to this device.
Example:
spi@40 {
compatible = "jcore,spi2";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x40 0x8>;
spi-max-frequency = <25000000>;
clocks = <&bus_clk>;
clock-names = "ref_clk";
}

View File

@ -7,7 +7,7 @@ NOR memories, without DMA support and a 64-byte unified transmit /
receive buffer.
Required properties:
- compatible: should be "amlogic,meson6-spifc"
- compatible: should be "amlogic,meson6-spifc" or "amlogic,meson-gxbb-spifc"
- reg: physical base address and length of the controller registers
- clocks: phandle of the input clock for the baud rate generator
- #address-cells: should be 1

View File

@ -11165,6 +11165,7 @@ F: Documentation/spi/
F: drivers/spi/
F: include/linux/spi/
F: include/uapi/linux/spi/
F: tools/spi/
SPIDERNET NETWORK DRIVER for CELL
M: Ishizaki Kou <kou.ishizaki@toshiba.co.jp>

View File

@ -153,6 +153,16 @@ config SPI_BCM63XX_HSSPI
This enables support for the High Speed SPI controller present on
newer Broadcom BCM63XX SoCs.
config SPI_BCM_QSPI
tristate "Broadcom BSPI and MSPI controller support"
depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || COMPILE_TEST
default ARCH_BCM_IPROC
help
Enables support for the Broadcom SPI flash and MSPI controller.
Select this option for any one of BRCMSTB, iProc NSP and NS2 SoCs
based platforms. This driver works for both SPI master for spi-nor
flash device as well as MSPI device.
config SPI_BITBANG
tristate "Utilities for Bitbanging SPI masters"
help
@ -285,6 +295,13 @@ config SPI_IMX
This enables using the Freescale i.MX SPI controllers in master
mode.
config SPI_JCORE
tristate "J-Core SPI Master"
depends on OF && (SUPERH || COMPILE_TEST)
help
This enables support for the SPI master controller in the J-Core
synthesizable, open source SoC.
config SPI_LM70_LLP
tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
depends on PARPORT
@ -549,7 +566,7 @@ config SPI_SC18IS602
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on HAVE_CLK && HAS_DMA
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
@ -631,6 +648,13 @@ config SPI_TEGRA20_SLINK
help
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
config SPI_THUNDERX
tristate "Cavium ThunderX SPI controller"
depends on PCI && 64BIT && (ARM64 || COMPILE_TEST)
help
SPI host driver for the hardware found on Cavium ThunderX
SOCs.
config SPI_TOPCLIFF_PCH
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)

View File

@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o
obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.o
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
@ -46,6 +47,7 @@ obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
@ -91,6 +93,8 @@ obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o
obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
/*
* Copyright 2016 Broadcom
*
* 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 (the "GPL").
*
* 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 version 2 (GPLv2) for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 (GPLv2) along with this source code.
*/
#ifndef __SPI_BCM_QSPI_H__
#define __SPI_BCM_QSPI_H__
#include <linux/types.h>
#include <linux/io.h>
/* BSPI interrupt masks */
#define INTR_BSPI_LR_OVERREAD_MASK BIT(4)
#define INTR_BSPI_LR_SESSION_DONE_MASK BIT(3)
#define INTR_BSPI_LR_IMPATIENT_MASK BIT(2)
#define INTR_BSPI_LR_SESSION_ABORTED_MASK BIT(1)
#define INTR_BSPI_LR_FULLNESS_REACHED_MASK BIT(0)
#define BSPI_LR_INTERRUPTS_DATA \
(INTR_BSPI_LR_SESSION_DONE_MASK | \
INTR_BSPI_LR_FULLNESS_REACHED_MASK)
#define BSPI_LR_INTERRUPTS_ERROR \
(INTR_BSPI_LR_OVERREAD_MASK | \
INTR_BSPI_LR_IMPATIENT_MASK | \
INTR_BSPI_LR_SESSION_ABORTED_MASK)
#define BSPI_LR_INTERRUPTS_ALL \
(BSPI_LR_INTERRUPTS_ERROR | \
BSPI_LR_INTERRUPTS_DATA)
/* MSPI Interrupt masks */
#define INTR_MSPI_HALTED_MASK BIT(6)
#define INTR_MSPI_DONE_MASK BIT(5)
#define MSPI_INTERRUPTS_ALL \
(INTR_MSPI_DONE_MASK | \
INTR_MSPI_HALTED_MASK)
#define QSPI_INTERRUPTS_ALL \
(MSPI_INTERRUPTS_ALL | \
BSPI_LR_INTERRUPTS_ALL)
struct platform_device;
struct dev_pm_ops;
enum {
MSPI_DONE = 0x1,
BSPI_DONE = 0x2,
BSPI_ERR = 0x4,
MSPI_BSPI_DONE = 0x7
};
struct bcm_qspi_soc_intc {
void (*bcm_qspi_int_ack)(struct bcm_qspi_soc_intc *soc_intc, int type);
void (*bcm_qspi_int_set)(struct bcm_qspi_soc_intc *soc_intc, int type,
bool en);
u32 (*bcm_qspi_get_int_status)(struct bcm_qspi_soc_intc *soc_intc);
};
/* Read controller register*/
static inline u32 bcm_qspi_readl(bool be, void __iomem *addr)
{
if (be)
return ioread32be(addr);
else
return readl_relaxed(addr);
}
/* Write controller register*/
static inline void bcm_qspi_writel(bool be,
unsigned int data, void __iomem *addr)
{
if (be)
iowrite32be(data, addr);
else
writel_relaxed(data, addr);
}
static inline u32 get_qspi_mask(int type)
{
switch (type) {
case MSPI_DONE:
return INTR_MSPI_DONE_MASK;
case BSPI_DONE:
return BSPI_LR_INTERRUPTS_ALL;
case MSPI_BSPI_DONE:
return QSPI_INTERRUPTS_ALL;
case BSPI_ERR:
return BSPI_LR_INTERRUPTS_ERROR;
}
return 0;
}
/* The common driver functions to be called by the SoC platform driver */
int bcm_qspi_probe(struct platform_device *pdev,
struct bcm_qspi_soc_intc *soc_intc);
int bcm_qspi_remove(struct platform_device *pdev);
/* pm_ops used by the SoC platform driver called on PM suspend/resume */
extern const struct dev_pm_ops bcm_qspi_pm_ops;
#endif /* __SPI_BCM_QSPI_H__ */

View File

@ -0,0 +1,53 @@
/*
* Copyright 2016 Broadcom
*
* 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 (the "GPL").
*
* 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 version 2 (GPLv2) for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 (GPLv2) along with this source code.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include "spi-bcm-qspi.h"
static const struct of_device_id brcmstb_qspi_of_match[] = {
{ .compatible = "brcm,spi-brcmstb-qspi" },
{ .compatible = "brcm,spi-brcmstb-mspi" },
{},
};
MODULE_DEVICE_TABLE(of, brcmstb_qspi_of_match);
static int brcmstb_qspi_probe(struct platform_device *pdev)
{
return bcm_qspi_probe(pdev, NULL);
}
static int brcmstb_qspi_remove(struct platform_device *pdev)
{
return bcm_qspi_remove(pdev);
}
static struct platform_driver brcmstb_qspi_driver = {
.probe = brcmstb_qspi_probe,
.remove = brcmstb_qspi_remove,
.driver = {
.name = "brcmstb_qspi",
.pm = &bcm_qspi_pm_ops,
.of_match_table = brcmstb_qspi_of_match,
}
};
module_platform_driver(brcmstb_qspi_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Kamal Dasu");
MODULE_DESCRIPTION("Broadcom SPI driver for settop SoC");

View File

@ -0,0 +1,120 @@
/*
* Cavium ThunderX SPI driver.
*
* Copyright (C) 2016 Cavium Inc.
* Authors: Jan Glauber <jglauber@cavium.com>
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/spi/spi.h>
#include "spi-cavium.h"
#define DRV_NAME "spi-thunderx"
#define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */
static int thunderx_spi_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct spi_master *master;
struct octeon_spi *p;
int ret;
master = spi_alloc_master(dev, sizeof(struct octeon_spi));
if (!master)
return -ENOMEM;
p = spi_master_get_devdata(master);
ret = pcim_enable_device(pdev);
if (ret)
goto error;
ret = pci_request_regions(pdev, DRV_NAME);
if (ret)
goto error;
p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
if (!p->register_base) {
ret = -EINVAL;
goto error;
}
p->regs.config = 0x1000;
p->regs.status = 0x1008;
p->regs.tx = 0x1010;
p->regs.data = 0x1080;
p->clk = devm_clk_get(dev, NULL);
if (IS_ERR(p->clk)) {
ret = PTR_ERR(p->clk);
goto error;
}
ret = clk_prepare_enable(p->clk);
if (ret)
goto error;
p->sys_freq = clk_get_rate(p->clk);
if (!p->sys_freq)
p->sys_freq = SYS_FREQ_DEFAULT;
dev_info(dev, "Set system clock to %u\n", p->sys_freq);
master->num_chipselect = 4;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
SPI_LSB_FIRST | SPI_3WIRE;
master->transfer_one_message = octeon_spi_transfer_one_message;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
master->dev.of_node = pdev->dev.of_node;
pci_set_drvdata(pdev, master);
ret = devm_spi_register_master(dev, master);
if (ret)
goto error;
return 0;
error:
clk_disable_unprepare(p->clk);
spi_master_put(master);
return ret;
}
static void thunderx_spi_remove(struct pci_dev *pdev)
{
struct spi_master *master = pci_get_drvdata(pdev);
struct octeon_spi *p;
p = spi_master_get_devdata(master);
if (!p)
return;
clk_disable_unprepare(p->clk);
/* Put everything in a known state. */
writeq(0, p->register_base + OCTEON_SPI_CFG(p));
}
static const struct pci_device_id thunderx_spi_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, thunderx_spi_pci_id_table);
static struct pci_driver thunderx_spi_driver = {
.name = DRV_NAME,
.id_table = thunderx_spi_pci_id_table,
.probe = thunderx_spi_probe,
.remove = thunderx_spi_remove,
};
module_pci_driver(thunderx_spi_driver);
MODULE_DESCRIPTION("Cavium, Inc. ThunderX SPI bus driver");
MODULE_AUTHOR("Jan Glauber");
MODULE_LICENSE("GPL");

View File

@ -1,6 +1,8 @@
#ifndef __SPI_CAVIUM_H
#define __SPI_CAVIUM_H
#include <linux/clk.h>
#define OCTEON_SPI_MAX_BYTES 9
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
@ -17,6 +19,7 @@ struct octeon_spi {
u64 cs_enax;
int sys_freq;
struct octeon_spi_regs regs;
struct clk *clk;
};
#define OCTEON_SPI_CFG(x) (x->regs.config)

View File

@ -283,7 +283,6 @@ static int dw_spi_transfer_one(struct spi_master *master,
struct chip_data *chip = spi_get_ctldata(spi);
u8 imask = 0;
u16 txlevel = 0;
u16 clk_div;
u32 cr0;
int ret;
@ -298,13 +297,13 @@ static int dw_spi_transfer_one(struct spi_master *master,
spi_enable_chip(dws, 0);
/* Handle per transfer options for bpw and speed */
if (transfer->speed_hz != chip->speed_hz) {
/* clk_div doesn't support odd number */
clk_div = (dws->max_freq / transfer->speed_hz + 1) & 0xfffe;
chip->speed_hz = transfer->speed_hz;
chip->clk_div = clk_div;
if (transfer->speed_hz != dws->current_freq) {
if (transfer->speed_hz != chip->speed_hz) {
/* clk_div doesn't support odd number */
chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
chip->speed_hz = transfer->speed_hz;
}
dws->current_freq = transfer->speed_hz;
spi_set_clk(dws, chip->clk_div);
}
if (transfer->bits_per_word == 8) {

View File

@ -123,6 +123,7 @@ struct dw_spi {
u8 n_bytes; /* current is a 1/2 bytes op */
u32 dma_width;
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
u32 current_freq; /* frequency in hz */
/* DMA info */
int dma_inited;

View File

@ -159,7 +159,7 @@ struct fsl_dspi {
u8 cs;
u16 void_write_data;
u32 cs_change;
struct fsl_dspi_devtype_data *devtype_data;
const struct fsl_dspi_devtype_data *devtype_data;
wait_queue_head_t waitq;
u32 waitflags;
@ -624,10 +624,13 @@ static int dspi_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct fsl_dspi *dspi = spi_master_get_devdata(master);
int ret;
pinctrl_pm_select_default_state(dev);
clk_prepare_enable(dspi->clk);
ret = clk_prepare_enable(dspi->clk);
if (ret)
return ret;
spi_master_resume(master);
return 0;
@ -651,8 +654,6 @@ static int dspi_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
int ret = 0, cs_num, bus_num;
const struct of_device_id *of_id =
of_match_device(fsl_dspi_dt_ids, &pdev->dev);
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
if (!master)
@ -686,7 +687,7 @@ static int dspi_probe(struct platform_device *pdev)
}
master->bus_num = bus_num;
dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
dspi->devtype_data = of_device_get_match_data(&pdev->dev);
if (!dspi->devtype_data) {
dev_err(&pdev->dev, "can't get devtype_data\n");
ret = -EFAULT;
@ -728,7 +729,9 @@ static int dspi_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "unable to get clock\n");
goto out_master_put;
}
clk_prepare_enable(dspi->clk);
ret = clk_prepare_enable(dspi->clk);
if (ret)
goto out_master_put;
master->max_speed_hz =
clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor;
@ -760,7 +763,6 @@ static int dspi_remove(struct platform_device *pdev)
/* Disconnect from the SPI framework */
clk_disable_unprepare(dspi->clk);
spi_unregister_master(dspi->master);
spi_master_put(dspi->master);
return 0;
}

View File

@ -12,7 +12,6 @@
#include <linux/err.h>
#include <linux/fsl_devices.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/of.h>
@ -27,40 +26,29 @@
#include "spi-fsl-lib.h"
/* eSPI Controller registers */
struct fsl_espi_reg {
__be32 mode; /* 0x000 - eSPI mode register */
__be32 event; /* 0x004 - eSPI event register */
__be32 mask; /* 0x008 - eSPI mask register */
__be32 command; /* 0x00c - eSPI command register */
__be32 transmit; /* 0x010 - eSPI transmit FIFO access register*/
__be32 receive; /* 0x014 - eSPI receive FIFO access register*/
u8 res[8]; /* 0x018 - 0x01c reserved */
__be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */
};
#define ESPI_SPMODE 0x00 /* eSPI mode register */
#define ESPI_SPIE 0x04 /* eSPI event register */
#define ESPI_SPIM 0x08 /* eSPI mask register */
#define ESPI_SPCOM 0x0c /* eSPI command register */
#define ESPI_SPITF 0x10 /* eSPI transmit FIFO access register*/
#define ESPI_SPIRF 0x14 /* eSPI receive FIFO access register*/
#define ESPI_SPMODE0 0x20 /* eSPI cs0 mode register */
struct fsl_espi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
unsigned n_tx;
unsigned n_rx;
unsigned actual_length;
int status;
};
#define ESPI_SPMODEx(x) (ESPI_SPMODE0 + (x) * 4)
/* eSPI Controller mode register definitions */
#define SPMODE_ENABLE (1 << 31)
#define SPMODE_LOOP (1 << 30)
#define SPMODE_ENABLE BIT(31)
#define SPMODE_LOOP BIT(30)
#define SPMODE_TXTHR(x) ((x) << 8)
#define SPMODE_RXTHR(x) ((x) << 0)
/* eSPI Controller CS mode register definitions */
#define CSMODE_CI_INACTIVEHIGH (1 << 31)
#define CSMODE_CP_BEGIN_EDGECLK (1 << 30)
#define CSMODE_REV (1 << 29)
#define CSMODE_DIV16 (1 << 28)
#define CSMODE_CI_INACTIVEHIGH BIT(31)
#define CSMODE_CP_BEGIN_EDGECLK BIT(30)
#define CSMODE_REV BIT(29)
#define CSMODE_DIV16 BIT(28)
#define CSMODE_PM(x) ((x) << 24)
#define CSMODE_POL_1 (1 << 20)
#define CSMODE_POL_1 BIT(20)
#define CSMODE_LEN(x) ((x) << 16)
#define CSMODE_BEF(x) ((x) << 12)
#define CSMODE_AFT(x) ((x) << 8)
@ -72,29 +60,114 @@ struct fsl_espi_transfer {
| CSMODE_AFT(0) | CSMODE_CG(1))
/* SPIE register values */
#define SPIE_NE 0x00000200 /* Not empty */
#define SPIE_NF 0x00000100 /* Not full */
/* SPIM register values */
#define SPIM_NE 0x00000200 /* Not empty */
#define SPIM_NF 0x00000100 /* Not full */
#define SPIE_RXCNT(reg) ((reg >> 24) & 0x3F)
#define SPIE_TXCNT(reg) ((reg >> 16) & 0x3F)
#define SPIE_TXE BIT(15) /* TX FIFO empty */
#define SPIE_DON BIT(14) /* TX done */
#define SPIE_RXT BIT(13) /* RX FIFO threshold */
#define SPIE_RXF BIT(12) /* RX FIFO full */
#define SPIE_TXT BIT(11) /* TX FIFO threshold*/
#define SPIE_RNE BIT(9) /* RX FIFO not empty */
#define SPIE_TNF BIT(8) /* TX FIFO not full */
/* SPIM register values */
#define SPIM_TXE BIT(15) /* TX FIFO empty */
#define SPIM_DON BIT(14) /* TX done */
#define SPIM_RXT BIT(13) /* RX FIFO threshold */
#define SPIM_RXF BIT(12) /* RX FIFO full */
#define SPIM_TXT BIT(11) /* TX FIFO threshold*/
#define SPIM_RNE BIT(9) /* RX FIFO not empty */
#define SPIM_TNF BIT(8) /* TX FIFO not full */
/* SPCOM register values */
#define SPCOM_CS(x) ((x) << 30)
#define SPCOM_DO BIT(28) /* Dual output */
#define SPCOM_TO BIT(27) /* TX only */
#define SPCOM_RXSKIP(x) ((x) << 16)
#define SPCOM_TRANLEN(x) ((x) << 0)
#define SPCOM_TRANLEN_MAX 0x10000 /* Max transaction length */
#define AUTOSUSPEND_TIMEOUT 2000
static inline u32 fsl_espi_read_reg(struct mpc8xxx_spi *mspi, int offset)
{
return ioread32be(mspi->reg_base + offset);
}
static inline u8 fsl_espi_read_reg8(struct mpc8xxx_spi *mspi, int offset)
{
return ioread8(mspi->reg_base + offset);
}
static inline void fsl_espi_write_reg(struct mpc8xxx_spi *mspi, int offset,
u32 val)
{
iowrite32be(val, mspi->reg_base + offset);
}
static inline void fsl_espi_write_reg8(struct mpc8xxx_spi *mspi, int offset,
u8 val)
{
iowrite8(val, mspi->reg_base + offset);
}
static void fsl_espi_copy_to_buf(struct spi_message *m,
struct mpc8xxx_spi *mspi)
{
struct spi_transfer *t;
u8 *buf = mspi->local_buf;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf)
memcpy(buf, t->tx_buf, t->len);
else
memset(buf, 0, t->len);
buf += t->len;
}
}
static void fsl_espi_copy_from_buf(struct spi_message *m,
struct mpc8xxx_spi *mspi)
{
struct spi_transfer *t;
u8 *buf = mspi->local_buf;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->rx_buf)
memcpy(t->rx_buf, buf, t->len);
buf += t->len;
}
}
static int fsl_espi_check_message(struct spi_message *m)
{
struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master);
struct spi_transfer *t, *first;
if (m->frame_length > SPCOM_TRANLEN_MAX) {
dev_err(mspi->dev, "message too long, size is %u bytes\n",
m->frame_length);
return -EMSGSIZE;
}
first = list_first_entry(&m->transfers, struct spi_transfer,
transfer_list);
list_for_each_entry(t, &m->transfers, transfer_list) {
if (first->bits_per_word != t->bits_per_word ||
first->speed_hz != t->speed_hz) {
dev_err(mspi->dev, "bits_per_word/speed_hz should be the same for all transfers\n");
return -EINVAL;
}
}
return 0;
}
static void fsl_espi_change_mode(struct spi_device *spi)
{
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
struct spi_mpc8xxx_cs *cs = spi->controller_state;
struct fsl_espi_reg *reg_base = mspi->reg_base;
__be32 __iomem *mode = &reg_base->csmode[spi->chip_select];
__be32 __iomem *espi_mode = &reg_base->mode;
u32 tmp;
unsigned long flags;
@ -102,10 +175,11 @@ static void fsl_espi_change_mode(struct spi_device *spi)
local_irq_save(flags);
/* Turn off SPI unit prior changing mode */
tmp = mpc8xxx_spi_read_reg(espi_mode);
mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
mpc8xxx_spi_write_reg(mode, cs->hw_mode);
mpc8xxx_spi_write_reg(espi_mode, tmp);
tmp = fsl_espi_read_reg(mspi, ESPI_SPMODE);
fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp & ~SPMODE_ENABLE);
fsl_espi_write_reg(mspi, ESPI_SPMODEx(spi->chip_select),
cs->hw_mode);
fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp);
local_irq_restore(flags);
}
@ -131,27 +205,15 @@ static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
return data;
}
static int fsl_espi_setup_transfer(struct spi_device *spi,
static void fsl_espi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
int bits_per_word = 0;
int bits_per_word = t ? t->bits_per_word : spi->bits_per_word;
u32 hz = t ? t->speed_hz : spi->max_speed_hz;
u8 pm;
u32 hz = 0;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
}
/* spi_transfer level calls that work per-word */
if (!bits_per_word)
bits_per_word = spi->bits_per_word;
if (!hz)
hz = spi->max_speed_hz;
cs->rx_shift = 0;
cs->tx_shift = 0;
cs->get_rx = mpc8xxx_spi_rx_buf_u32;
@ -169,12 +231,10 @@ static int fsl_espi_setup_transfer(struct spi_device *spi,
mpc8xxx_spi->get_rx = cs->get_rx;
mpc8xxx_spi->get_tx = cs->get_tx;
bits_per_word = bits_per_word - 1;
/* mask out bits we are going to set */
cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF));
cs->hw_mode |= CSMODE_LEN(bits_per_word);
cs->hw_mode |= CSMODE_LEN(bits_per_word - 1);
if ((mpc8xxx_spi->spibrg / hz) > 64) {
cs->hw_mode |= CSMODE_DIV16;
@ -196,36 +256,16 @@ static int fsl_espi_setup_transfer(struct spi_device *spi,
cs->hw_mode |= CSMODE_PM(pm);
fsl_espi_change_mode(spi);
return 0;
}
static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
unsigned int len)
{
u32 word;
struct fsl_espi_reg *reg_base = mspi->reg_base;
mspi->count = len;
/* enable rx ints */
mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
/* transmit word */
word = mspi->get_tx(mspi);
mpc8xxx_spi_write_reg(&reg_base->transmit, word);
return 0;
}
static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
unsigned int len = t->len;
u32 word;
int ret;
mpc8xxx_spi->len = t->len;
len = roundup(len, 4) / 4;
mpc8xxx_spi->count = roundup(t->len, 4) / 4;
mpc8xxx_spi->tx = t->tx_buf;
mpc8xxx_spi->rx = t->rx_buf;
@ -233,17 +273,15 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
reinit_completion(&mpc8xxx_spi->done);
/* Set SPCOM[CS] and SPCOM[TRANLEN] field */
if (t->len > SPCOM_TRANLEN_MAX) {
dev_err(mpc8xxx_spi->dev, "Transaction length (%d)"
" beyond the SPCOM[TRANLEN] field\n", t->len);
return -EINVAL;
}
mpc8xxx_spi_write_reg(&reg_base->command,
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM,
(SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1)));
ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len);
if (ret)
return ret;
/* enable rx ints */
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_RNE);
/* transmit word */
word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPITF, word);
/* Won't hang up forever, SPI bus sometimes got lost interrupts... */
ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ);
@ -253,230 +291,76 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
mpc8xxx_spi->count);
/* disable rx ints */
mpc8xxx_spi_write_reg(&reg_base->mask, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0);
return mpc8xxx_spi->count;
return mpc8xxx_spi->count > 0 ? -EMSGSIZE : 0;
}
static inline void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd)
{
if (cmd) {
cmd[1] = (u8)(addr >> 16);
cmd[2] = (u8)(addr >> 8);
cmd[3] = (u8)(addr >> 0);
}
}
static inline unsigned int fsl_espi_cmd2addr(u8 *cmd)
{
if (cmd)
return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0;
return 0;
}
static void fsl_espi_do_trans(struct spi_message *m,
struct fsl_espi_transfer *tr)
static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans)
{
struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master);
struct spi_device *spi = m->spi;
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
struct fsl_espi_transfer *espi_trans = tr;
struct spi_message message;
struct spi_transfer *t, *first, trans;
int status = 0;
int ret;
spi_message_init(&message);
memset(&trans, 0, sizeof(trans));
fsl_espi_copy_to_buf(m, mspi);
fsl_espi_setup_transfer(spi, trans);
first = list_first_entry(&m->transfers, struct spi_transfer,
transfer_list);
list_for_each_entry(t, &m->transfers, transfer_list) {
if ((first->bits_per_word != t->bits_per_word) ||
(first->speed_hz != t->speed_hz)) {
espi_trans->status = -EINVAL;
dev_err(mspi->dev,
"bits_per_word/speed_hz should be same for the same SPI transfer\n");
return;
}
ret = fsl_espi_bufs(spi, trans);
trans.speed_hz = t->speed_hz;
trans.bits_per_word = t->bits_per_word;
trans.delay_usecs = max(first->delay_usecs, t->delay_usecs);
}
if (trans->delay_usecs)
udelay(trans->delay_usecs);
trans.len = espi_trans->len;
trans.tx_buf = espi_trans->tx_buf;
trans.rx_buf = espi_trans->rx_buf;
spi_message_add_tail(&trans, &message);
list_for_each_entry(t, &message.transfers, transfer_list) {
if (t->bits_per_word || t->speed_hz) {
status = -EINVAL;
status = fsl_espi_setup_transfer(spi, t);
if (status < 0)
break;
}
if (t->len)
status = fsl_espi_bufs(spi, t);
if (status) {
status = -EMSGSIZE;
break;
}
if (t->delay_usecs)
udelay(t->delay_usecs);
}
espi_trans->status = status;
fsl_espi_setup_transfer(spi, NULL);
}
static void fsl_espi_cmd_trans(struct spi_message *m,
struct fsl_espi_transfer *trans, u8 *rx_buff)
{
struct spi_transfer *t;
u8 *local_buf;
int i = 0;
struct fsl_espi_transfer *espi_trans = trans;
if (!ret)
fsl_espi_copy_from_buf(m, mspi);
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
if (!local_buf) {
espi_trans->status = -ENOMEM;
return;
}
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf) {
memcpy(local_buf + i, t->tx_buf, t->len);
i += t->len;
}
}
espi_trans->tx_buf = local_buf;
espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
espi_trans->actual_length = espi_trans->len;
kfree(local_buf);
}
static void fsl_espi_rw_trans(struct spi_message *m,
struct fsl_espi_transfer *trans, u8 *rx_buff)
{
struct fsl_espi_transfer *espi_trans = trans;
unsigned int total_len = espi_trans->len;
struct spi_transfer *t;
u8 *local_buf;
u8 *rx_buf = rx_buff;
unsigned int trans_len;
unsigned int addr;
unsigned int tx_only;
unsigned int rx_pos = 0;
unsigned int pos;
int i, loop;
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
if (!local_buf) {
espi_trans->status = -ENOMEM;
return;
}
for (pos = 0, loop = 0; pos < total_len; pos += trans_len, loop++) {
trans_len = total_len - pos;
i = 0;
tx_only = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf) {
memcpy(local_buf + i, t->tx_buf, t->len);
i += t->len;
if (!t->rx_buf)
tx_only += t->len;
}
}
/* Add additional TX bytes to compensate SPCOM_TRANLEN_MAX */
if (loop > 0)
trans_len += tx_only;
if (trans_len > SPCOM_TRANLEN_MAX)
trans_len = SPCOM_TRANLEN_MAX;
/* Update device offset */
if (pos > 0) {
addr = fsl_espi_cmd2addr(local_buf);
addr += rx_pos;
fsl_espi_addr2cmd(addr, local_buf);
}
espi_trans->len = trans_len;
espi_trans->tx_buf = local_buf;
espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
/* If there is at least one RX byte then copy it to rx_buf */
if (tx_only < SPCOM_TRANLEN_MAX)
memcpy(rx_buf + rx_pos, espi_trans->rx_buf + tx_only,
trans_len - tx_only);
rx_pos += trans_len - tx_only;
if (loop > 0)
espi_trans->actual_length += espi_trans->len - tx_only;
else
espi_trans->actual_length += espi_trans->len;
}
kfree(local_buf);
return ret;
}
static int fsl_espi_do_one_msg(struct spi_master *master,
struct spi_message *m)
{
struct spi_transfer *t;
u8 *rx_buf = NULL;
unsigned int n_tx = 0;
unsigned int n_rx = 0;
unsigned int xfer_len = 0;
struct fsl_espi_transfer espi_trans;
struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master);
unsigned int delay_usecs = 0;
struct spi_transfer *t, trans = {};
int ret;
ret = fsl_espi_check_message(m);
if (ret)
goto out;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf)
n_tx += t->len;
if (t->rx_buf) {
n_rx += t->len;
rx_buf = t->rx_buf;
}
if ((t->tx_buf) || (t->rx_buf))
xfer_len += t->len;
if (t->delay_usecs > delay_usecs)
delay_usecs = t->delay_usecs;
}
espi_trans.n_tx = n_tx;
espi_trans.n_rx = n_rx;
espi_trans.len = xfer_len;
espi_trans.actual_length = 0;
espi_trans.status = 0;
t = list_first_entry(&m->transfers, struct spi_transfer,
transfer_list);
if (!rx_buf)
fsl_espi_cmd_trans(m, &espi_trans, NULL);
else
fsl_espi_rw_trans(m, &espi_trans, rx_buf);
trans.len = m->frame_length;
trans.speed_hz = t->speed_hz;
trans.bits_per_word = t->bits_per_word;
trans.delay_usecs = delay_usecs;
trans.tx_buf = mspi->local_buf;
trans.rx_buf = mspi->local_buf;
if (trans.len)
ret = fsl_espi_trans(m, &trans);
m->actual_length = ret ? 0 : trans.len;
out:
if (m->status == -EINPROGRESS)
m->status = ret;
m->actual_length = espi_trans.actual_length;
m->status = espi_trans.status;
spi_finalize_current_message(master);
return 0;
return ret;
}
static int fsl_espi_setup(struct spi_device *spi)
{
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
int retval;
u32 hw_mode;
u32 loop_mode;
struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi);
@ -491,13 +375,11 @@ 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(
&reg_base->csmode[spi->chip_select]);
cs->hw_mode = fsl_espi_read_reg(mpc8xxx_spi,
ESPI_SPMODEx(spi->chip_select));
/* mask out bits we are going to set */
cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH
| CSMODE_REV);
@ -510,21 +392,17 @@ static int fsl_espi_setup(struct spi_device *spi)
cs->hw_mode |= CSMODE_REV;
/* Handle the loop mode */
loop_mode = mpc8xxx_spi_read_reg(&reg_base->mode);
loop_mode = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE);
loop_mode &= ~SPMODE_LOOP;
if (spi->mode & SPI_LOOP)
loop_mode |= SPMODE_LOOP;
mpc8xxx_spi_write_reg(&reg_base->mode, loop_mode);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, loop_mode);
retval = fsl_espi_setup_transfer(spi, NULL);
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;
}
return 0;
}
@ -536,12 +414,10 @@ static void fsl_espi_cleanup(struct spi_device *spi)
spi_set_ctldata(spi, NULL);
}
void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
{
struct fsl_espi_reg *reg_base = mspi->reg_base;
/* We need handle RX first */
if (events & SPIE_NE) {
if (events & SPIE_RNE) {
u32 rx_data, tmp;
u8 rx_data_8;
int rx_nr_bytes = 4;
@ -551,7 +427,7 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
if (SPIE_RXCNT(events) < min(4, mspi->len)) {
ret = spin_event_timeout(
!(SPIE_RXCNT(events =
mpc8xxx_spi_read_reg(&reg_base->event)) <
fsl_espi_read_reg(mspi, ESPI_SPIE)) <
min(4, mspi->len)),
10000, 0); /* 10 msec */
if (!ret)
@ -560,10 +436,10 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
}
if (mspi->len >= 4) {
rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
rx_data = fsl_espi_read_reg(mspi, ESPI_SPIRF);
} else if (mspi->len <= 0) {
dev_err(mspi->dev,
"unexpected RX(SPIE_NE) interrupt occurred,\n"
"unexpected RX(SPIE_RNE) interrupt occurred,\n"
"(local rxlen %d bytes, reg rxlen %d bytes)\n",
min(4, mspi->len), SPIE_RXCNT(events));
rx_nr_bytes = 0;
@ -572,7 +448,8 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
tmp = mspi->len;
rx_data = 0;
while (tmp--) {
rx_data_8 = in_8((u8 *)&reg_base->receive);
rx_data_8 = fsl_espi_read_reg8(mspi,
ESPI_SPIRF);
rx_data |= (rx_data_8 << (tmp * 8));
}
@ -585,30 +462,24 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
mspi->get_rx(rx_data, mspi);
}
if (!(events & SPIE_NF)) {
if (!(events & SPIE_TNF)) {
int ret;
/* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
&reg_base->event)) & SPIE_NF), 1000, 0);
ret = spin_event_timeout(((events = fsl_espi_read_reg(
mspi, ESPI_SPIE)) & SPIE_TNF), 1000, 0);
if (!ret) {
dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
/* Clear the SPIE bits */
mpc8xxx_spi_write_reg(&reg_base->event, events);
dev_err(mspi->dev, "tired waiting for SPIE_TNF\n");
complete(&mspi->done);
return;
}
}
/* Clear the events */
mpc8xxx_spi_write_reg(&reg_base->event, events);
mspi->count -= 1;
if (mspi->count) {
u32 word = mspi->get_tx(mspi);
mpc8xxx_spi_write_reg(&reg_base->transmit, word);
fsl_espi_write_reg(mspi, ESPI_SPITF, word);
} else {
complete(&mspi->done);
}
@ -617,20 +488,21 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
{
struct mpc8xxx_spi *mspi = context_data;
struct fsl_espi_reg *reg_base = mspi->reg_base;
irqreturn_t ret = IRQ_NONE;
u32 events;
/* Get interrupt events(tx/rx) */
events = mpc8xxx_spi_read_reg(&reg_base->event);
if (events)
ret = IRQ_HANDLED;
events = fsl_espi_read_reg(mspi, ESPI_SPIE);
if (!events)
return IRQ_NONE;
dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events);
fsl_espi_cpu_irq(mspi, events);
return ret;
/* Clear the events */
fsl_espi_write_reg(mspi, ESPI_SPIE, events);
return IRQ_HANDLED;
}
#ifdef CONFIG_PM
@ -638,12 +510,11 @@ static int fsl_espi_runtime_suspend(struct device *dev)
{
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;
regval = mpc8xxx_spi_read_reg(&reg_base->mode);
regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE);
regval &= ~SPMODE_ENABLE;
mpc8xxx_spi_write_reg(&reg_base->mode, regval);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval);
return 0;
}
@ -652,39 +523,35 @@ static int fsl_espi_runtime_resume(struct device *dev)
{
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;
regval = mpc8xxx_spi_read_reg(&reg_base->mode);
regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE);
regval |= SPMODE_ENABLE;
mpc8xxx_spi_write_reg(&reg_base->mode, regval);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval);
return 0;
}
#endif
static size_t fsl_espi_max_transfer_size(struct spi_device *spi)
static size_t fsl_espi_max_message_size(struct spi_device *spi)
{
return SPCOM_TRANLEN_MAX;
}
static struct spi_master * fsl_espi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
static int fsl_espi_probe(struct device *dev, struct resource *mem,
unsigned int irq)
{
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
struct device_node *nc;
const __be32 *prop;
u32 regval, csmode;
int i, len, ret = 0;
int i, len, ret;
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
ret = -ENOMEM;
goto err;
}
if (!master)
return -ENOMEM;
dev_set_drvdata(dev, master);
@ -695,18 +562,23 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
master->cleanup = fsl_espi_cleanup;
master->transfer_one_message = fsl_espi_do_one_msg;
master->auto_runtime_pm = true;
master->max_transfer_size = fsl_espi_max_transfer_size;
master->max_message_size = fsl_espi_max_message_size;
mpc8xxx_spi = spi_master_get_devdata(master);
mpc8xxx_spi->local_buf =
devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL);
if (!mpc8xxx_spi->local_buf) {
ret = -ENOMEM;
goto err_probe;
}
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 = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_espi_irq,
0, "fsl_espi", mpc8xxx_spi);
@ -719,10 +591,10 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
}
/* SPI controller initializations */
mpc8xxx_spi_write_reg(&reg_base->mode, 0);
mpc8xxx_spi_write_reg(&reg_base->mask, 0);
mpc8xxx_spi_write_reg(&reg_base->command, 0);
mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff);
/* Init eSPI CS mode register */
for_each_available_child_of_node(master->dev.of_node, nc) {
@ -747,7 +619,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
csmode &= ~(CSMODE_AFT(0xf));
csmode |= CSMODE_AFT(be32_to_cpup(prop));
}
mpc8xxx_spi_write_reg(&reg_base->csmode[i], csmode);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), csmode);
dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode);
}
@ -755,7 +627,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
mpc8xxx_spi_write_reg(&reg_base->mode, regval);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval);
pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
@ -767,12 +639,13 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
if (ret < 0)
goto err_pm;
dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq);
dev_info(dev, "at 0x%p (irq = %d)\n", mpc8xxx_spi->reg_base,
mpc8xxx_spi->irq);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return master;
return 0;
err_pm:
pm_runtime_put_noidle(dev);
@ -780,8 +653,7 @@ err_pm:
pm_runtime_set_suspended(dev);
err_probe:
spi_master_put(master);
err:
return ERR_PTR(ret);
return ret;
}
static int of_fsl_espi_get_chipselects(struct device *dev)
@ -807,10 +679,9 @@ static int of_fsl_espi_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
struct spi_master *master;
struct resource mem;
unsigned int irq;
int ret = -ENOMEM;
int ret;
ret = of_mpc8xxx_spi_probe(ofdev);
if (ret)
@ -818,28 +689,17 @@ static int of_fsl_espi_probe(struct platform_device *ofdev)
ret = of_fsl_espi_get_chipselects(dev);
if (ret)
goto err;
return ret;
ret = of_address_to_resource(np, 0, &mem);
if (ret)
goto err;
return ret;
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -EINVAL;
goto err;
}
if (!irq)
return -EINVAL;
master = fsl_espi_probe(dev, &mem, irq);
if (IS_ERR(master)) {
ret = PTR_ERR(master);
goto err;
}
return 0;
err:
return ret;
return fsl_espi_probe(dev, &mem, irq);
}
static int of_fsl_espi_remove(struct platform_device *dev)
@ -873,27 +733,26 @@ static int of_fsl_espi_resume(struct device *dev)
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master = dev_get_drvdata(dev);
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
u32 regval;
int i, ret;
mpc8xxx_spi = spi_master_get_devdata(master);
reg_base = mpc8xxx_spi->reg_base;
/* SPI controller initializations */
mpc8xxx_spi_write_reg(&reg_base->mode, 0);
mpc8xxx_spi_write_reg(&reg_base->mask, 0);
mpc8xxx_spi_write_reg(&reg_base->command, 0);
mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff);
/* Init eSPI CS mode register */
for (i = 0; i < pdata->max_chipselect; i++)
mpc8xxx_spi_write_reg(&reg_base->csmode[i], CSMODE_INIT_VAL);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i),
CSMODE_INIT_VAL);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
mpc8xxx_spi_write_reg(&reg_base->mode, regval);
fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval);
ret = pm_runtime_force_resume(dev);
if (ret < 0)

View File

@ -23,13 +23,14 @@
/* SPI/eSPI Controller driver's private data. */
struct mpc8xxx_spi {
struct device *dev;
void *reg_base;
void __iomem *reg_base;
/* rx & tx bufs from the spi_transfer */
const void *tx;
void *rx;
#if IS_ENABLED(CONFIG_SPI_FSL_ESPI)
int len;
u8 *local_buf;
#endif
int subblock;

View File

@ -186,17 +186,19 @@ static unsigned int spi_imx_clkdiv_1(unsigned int fin,
/* MX1, MX31, MX35, MX51 CSPI */
static unsigned int spi_imx_clkdiv_2(unsigned int fin,
unsigned int fspi)
unsigned int fspi, unsigned int *fres)
{
int i, div = 4;
for (i = 0; i < 7; i++) {
if (fspi * div >= fin)
return i;
goto out;
div <<= 1;
}
return 7;
out:
*fres = fin / div;
return i;
}
static int spi_imx_bytes_per_word(const int bpw)
@ -453,6 +455,9 @@ static void mx51_ecspi_reset(struct spi_imx_data *spi_imx)
#define MX31_CSPISTATUS 0x14
#define MX31_STATUS_RR (1 << 3)
#define MX31_CSPI_TESTREG 0x1C
#define MX31_TEST_LBC (1 << 14)
/* These functions also work for the i.MX35, but be aware that
* the i.MX35 has a slightly different register layout for bits
* we do not use here.
@ -482,9 +487,11 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER;
unsigned int clk;
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) <<
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) <<
MX31_CSPICTRL_DR_SHIFT;
spi_imx->spi_bus_clk = clk;
if (is_imx35_cspi(spi_imx)) {
reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
@ -506,6 +513,13 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config)
writel(reg, spi_imx->base + MXC_CSPICTRL);
reg = readl(spi_imx->base + MX31_CSPI_TESTREG);
if (spi->mode & SPI_LOOP)
reg |= MX31_TEST_LBC;
else
reg &= ~MX31_TEST_LBC;
writel(reg, spi_imx->base + MX31_CSPI_TESTREG);
return 0;
}
@ -625,9 +639,12 @@ static int mx1_config(struct spi_device *spi, struct spi_imx_config *config)
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER;
unsigned int clk;
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) <<
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) <<
MX1_CSPICTRL_DR_SHIFT;
spi_imx->spi_bus_clk = clk;
reg |= config->bpw - 1;
if (spi->mode & SPI_CPHA)
@ -1179,7 +1196,7 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if (is_imx51_ecspi(spi_imx))
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx))
spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
init_completion(&spi_imx->xfer_done);
@ -1251,6 +1268,12 @@ static int spi_imx_probe(struct platform_device *pdev)
goto out_clk_put;
}
if (!master->cs_gpios) {
dev_err(&pdev->dev, "No CS GPIOs available\n");
ret = -EINVAL;
goto out_clk_put;
}
for (i = 0; i < master->num_chipselect; i++) {
if (!gpio_is_valid(master->cs_gpios[i]))
continue;

View File

@ -0,0 +1,163 @@
/*
* Copyright 2016 Broadcom Limited
*
* 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/device.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "spi-bcm-qspi.h"
#define INTR_BASE_BIT_SHIFT 0x02
#define INTR_COUNT 0x07
struct bcm_iproc_intc {
struct bcm_qspi_soc_intc soc_intc;
struct platform_device *pdev;
void __iomem *int_reg;
void __iomem *int_status_reg;
spinlock_t soclock;
bool big_endian;
};
static u32 bcm_iproc_qspi_get_l2_int_status(struct bcm_qspi_soc_intc *soc_intc)
{
struct bcm_iproc_intc *priv =
container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
void __iomem *mmio = priv->int_status_reg;
int i;
u32 val = 0, sts = 0;
for (i = 0; i < INTR_COUNT; i++) {
if (bcm_qspi_readl(priv->big_endian, mmio + (i * 4)))
val |= 1UL << i;
}
if (val & INTR_MSPI_DONE_MASK)
sts |= MSPI_DONE;
if (val & BSPI_LR_INTERRUPTS_ALL)
sts |= BSPI_DONE;
if (val & BSPI_LR_INTERRUPTS_ERROR)
sts |= BSPI_ERR;
return sts;
}
static void bcm_iproc_qspi_int_ack(struct bcm_qspi_soc_intc *soc_intc, int type)
{
struct bcm_iproc_intc *priv =
container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
void __iomem *mmio = priv->int_status_reg;
u32 mask = get_qspi_mask(type);
int i;
for (i = 0; i < INTR_COUNT; i++) {
if (mask & (1UL << i))
bcm_qspi_writel(priv->big_endian, 1, mmio + (i * 4));
}
}
static void bcm_iproc_qspi_int_set(struct bcm_qspi_soc_intc *soc_intc, int type,
bool en)
{
struct bcm_iproc_intc *priv =
container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
void __iomem *mmio = priv->int_reg;
u32 mask = get_qspi_mask(type);
u32 val;
unsigned long flags;
spin_lock_irqsave(&priv->soclock, flags);
val = bcm_qspi_readl(priv->big_endian, mmio);
if (en)
val = val | (mask << INTR_BASE_BIT_SHIFT);
else
val = val & ~(mask << INTR_BASE_BIT_SHIFT);
bcm_qspi_writel(priv->big_endian, val, mmio);
spin_unlock_irqrestore(&priv->soclock, flags);
}
static int bcm_iproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm_iproc_intc *priv;
struct bcm_qspi_soc_intc *soc_intc;
struct resource *res;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
soc_intc = &priv->soc_intc;
priv->pdev = pdev;
spin_lock_init(&priv->soclock);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr_regs");
priv->int_reg = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->int_reg))
return PTR_ERR(priv->int_reg);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"intr_status_reg");
priv->int_status_reg = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->int_status_reg))
return PTR_ERR(priv->int_status_reg);
priv->big_endian = of_device_is_big_endian(dev->of_node);
bcm_iproc_qspi_int_ack(soc_intc, MSPI_BSPI_DONE);
bcm_iproc_qspi_int_set(soc_intc, MSPI_BSPI_DONE, false);
soc_intc->bcm_qspi_int_ack = bcm_iproc_qspi_int_ack;
soc_intc->bcm_qspi_int_set = bcm_iproc_qspi_int_set;
soc_intc->bcm_qspi_get_int_status = bcm_iproc_qspi_get_l2_int_status;
return bcm_qspi_probe(pdev, soc_intc);
}
static int bcm_iproc_remove(struct platform_device *pdev)
{
return bcm_qspi_remove(pdev);
}
static const struct of_device_id bcm_iproc_of_match[] = {
{ .compatible = "brcm,spi-nsp-qspi" },
{ .compatible = "brcm,spi-ns2-qspi" },
{},
};
MODULE_DEVICE_TABLE(of, bcm_iproc_of_match);
static struct platform_driver bcm_iproc_driver = {
.probe = bcm_iproc_probe,
.remove = bcm_iproc_remove,
.driver = {
.name = "bcm_iproc",
.pm = &bcm_qspi_pm_ops,
.of_match_table = bcm_iproc_of_match,
}
};
module_platform_driver(bcm_iproc_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Kamal Dasu");
MODULE_DESCRIPTION("SPI flash driver for Broadcom iProc SoCs");

View File

@ -0,0 +1,231 @@
/*
* J-Core SPI controller driver
*
* Copyright (C) 2012-2016 Smart Energy Instruments, Inc.
*
* Current version by Rich Felker
* Based loosely on initial version by Oleksandr G Zhadan
*
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
#define DRV_NAME "jcore_spi"
#define CTRL_REG 0x0
#define DATA_REG 0x4
#define JCORE_SPI_CTRL_XMIT 0x02
#define JCORE_SPI_STAT_BUSY 0x02
#define JCORE_SPI_CTRL_LOOP 0x08
#define JCORE_SPI_CTRL_CS_BITS 0x15
#define JCORE_SPI_WAIT_RDY_MAX_LOOP 2000000
struct jcore_spi {
struct spi_master *master;
void __iomem *base;
unsigned int cs_reg;
unsigned int speed_reg;
unsigned int speed_hz;
unsigned int clock_freq;
};
static int jcore_spi_wait(void __iomem *ctrl_reg)
{
unsigned timeout = JCORE_SPI_WAIT_RDY_MAX_LOOP;
do {
if (!(readl(ctrl_reg) & JCORE_SPI_STAT_BUSY))
return 0;
cpu_relax();
} while (--timeout);
return -EBUSY;
}
static void jcore_spi_program(struct jcore_spi *hw)
{
void __iomem *ctrl_reg = hw->base + CTRL_REG;
if (jcore_spi_wait(ctrl_reg))
dev_err(hw->master->dev.parent,
"timeout waiting to program ctrl reg.\n");
writel(hw->cs_reg | hw->speed_reg, ctrl_reg);
}
static void jcore_spi_chipsel(struct spi_device *spi, bool value)
{
struct jcore_spi *hw = spi_master_get_devdata(spi->master);
u32 csbit = 1U << (2 * spi->chip_select);
dev_dbg(hw->master->dev.parent, "chipselect %d\n", spi->chip_select);
if (value)
hw->cs_reg |= csbit;
else
hw->cs_reg &= ~csbit;
jcore_spi_program(hw);
}
static void jcore_spi_baudrate(struct jcore_spi *hw, int speed)
{
if (speed == hw->speed_hz) return;
hw->speed_hz = speed;
if (speed >= hw->clock_freq / 2)
hw->speed_reg = 0;
else
hw->speed_reg = ((hw->clock_freq / 2 / speed) - 1) << 27;
jcore_spi_program(hw);
dev_dbg(hw->master->dev.parent, "speed=%d reg=0x%x\n",
speed, hw->speed_reg);
}
static int jcore_spi_txrx(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *t)
{
struct jcore_spi *hw = spi_master_get_devdata(master);
void __iomem *ctrl_reg = hw->base + CTRL_REG;
void __iomem *data_reg = hw->base + DATA_REG;
u32 xmit;
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
unsigned int len;
unsigned int count;
jcore_spi_baudrate(hw, t->speed_hz);
xmit = hw->cs_reg | hw->speed_reg | JCORE_SPI_CTRL_XMIT;
tx = t->tx_buf;
rx = t->rx_buf;
len = t->len;
for (count = 0; count < len; count++) {
if (jcore_spi_wait(ctrl_reg))
break;
writel(tx ? *tx++ : 0, data_reg);
writel(xmit, ctrl_reg);
if (jcore_spi_wait(ctrl_reg))
break;
if (rx)
*rx++ = readl(data_reg);
}
spi_finalize_current_transfer(master);
if (count < len)
return -EREMOTEIO;
return 0;
}
static int jcore_spi_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct jcore_spi *hw;
struct spi_master *master;
struct resource *res;
u32 clock_freq;
struct clk *clk;
int err = -ENODEV;
master = spi_alloc_master(&pdev->dev, sizeof(struct jcore_spi));
if (!master)
return err;
/* Setup the master state. */
master->num_chipselect = 3;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->transfer_one = jcore_spi_txrx;
master->set_cs = jcore_spi_chipsel;
master->dev.of_node = node;
master->bus_num = pdev->id;
hw = spi_master_get_devdata(master);
hw->master = master;
platform_set_drvdata(pdev, hw);
/* Find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&pdev->dev, res->start,
resource_size(res), pdev->name))
goto exit_busy;
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
goto exit_busy;
/*
* The SPI clock rate controlled via a configurable clock divider
* which is applied to the reference clock. A 50 MHz reference is
* most suitable for obtaining standard SPI clock rates, but some
* designs may have a different reference clock, and the DT must
* make the driver aware so that it can properly program the
* requested rate. If the clock is omitted, 50 MHz is assumed.
*/
clock_freq = 50000000;
clk = devm_clk_get(&pdev->dev, "ref_clk");
if (!IS_ERR_OR_NULL(clk)) {
if (clk_enable(clk) == 0)
clock_freq = clk_get_rate(clk);
else
dev_warn(&pdev->dev, "could not enable ref_clk\n");
}
hw->clock_freq = clock_freq;
/* Initialize all CS bits to high. */
hw->cs_reg = JCORE_SPI_CTRL_CS_BITS;
jcore_spi_baudrate(hw, 400000);
/* Register our spi controller */
err = devm_spi_register_master(&pdev->dev, master);
if (err)
goto exit;
return 0;
exit_busy:
err = -EBUSY;
exit:
spi_master_put(master);
return err;
}
static const struct of_device_id jcore_spi_of_match[] = {
{ .compatible = "jcore,spi2" },
{},
};
static struct platform_driver jcore_spi_driver = {
.probe = jcore_spi_probe,
.driver = {
.name = DRV_NAME,
.of_match_table = jcore_spi_of_match,
},
};
module_platform_driver(jcore_spi_driver);
MODULE_DESCRIPTION("J-Core SPI driver");
MODULE_AUTHOR("Rich Felker <dalias@libc.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@ -405,7 +405,7 @@ struct rx_ranges {
u8 *end;
};
int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b)
static int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b)
{
struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list);
struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list);

View File

@ -442,6 +442,7 @@ static const struct dev_pm_ops meson_spifc_pm_ops = {
static const struct of_device_id meson_spifc_dt_match[] = {
{ .compatible = "amlogic,meson6-spifc", },
{ .compatible = "amlogic,meson-gxbb-spifc", },
{ },
};
MODULE_DEVICE_TABLE(of, meson_spifc_dt_match);

View File

@ -253,15 +253,13 @@ static struct ring_desc *ring_desc_get(struct pic32_sqi *sqi)
return NULL;
rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list);
list_del(&rdesc->list);
list_add_tail(&rdesc->list, &sqi->bd_list_used);
list_move_tail(&rdesc->list, &sqi->bd_list_used);
return rdesc;
}
static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc)
{
list_del(&rdesc->list);
list_add(&rdesc->list, &sqi->bd_list_free);
list_move(&rdesc->list, &sqi->bd_list_free);
}
static int pic32_sqi_one_transfer(struct pic32_sqi *sqi,

View File

@ -23,7 +23,7 @@
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
bool error)
{
struct spi_message *msg = drv_data->cur_msg;
struct spi_message *msg = drv_data->master->cur_msg;
/*
* It is possible that one CPU is handling ROR interrupt and other
@ -76,7 +76,8 @@ static struct dma_async_tx_descriptor *
pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
enum dma_transfer_direction dir)
{
struct chip_data *chip = drv_data->cur_chip;
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
struct spi_transfer *xfer = drv_data->cur_transfer;
enum dma_slave_buswidth width;
struct dma_slave_config cfg;
@ -146,7 +147,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
{
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
int err = 0;
int err;
tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV);
if (!tx_desc) {

View File

@ -28,6 +28,7 @@
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
@ -62,6 +63,13 @@ MODULE_ALIAS("platform:pxa2xx-spi");
| QUARK_X1000_SSCR1_TFT \
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
#define CE4100_SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_SCFR \
| SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \
| SSCR1_SFRMDIR | SSCR1_RWOT | SSCR1_TRAIL \
| SSCR1_IFS | SSCR1_STRF | SSCR1_EFWR \
| CE4100_SSCR1_RFT | CE4100_SSCR1_TFT | SSCR1_MWDS \
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
@ -175,6 +183,8 @@ static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data)
switch (drv_data->ssp_type) {
case QUARK_X1000_SSP:
return QUARK_X1000_SSCR1_CHANGE_MASK;
case CE4100_SSP:
return CE4100_SSCR1_CHANGE_MASK;
default:
return SSCR1_CHANGE_MASK;
}
@ -186,6 +196,8 @@ pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data)
switch (drv_data->ssp_type) {
case QUARK_X1000_SSP:
return RX_THRESH_QUARK_X1000_DFLT;
case CE4100_SSP:
return RX_THRESH_CE4100_DFLT;
default:
return RX_THRESH_DFLT;
}
@ -199,6 +211,9 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data)
case QUARK_X1000_SSP:
mask = QUARK_X1000_SSSR_TFL_MASK;
break;
case CE4100_SSP:
mask = CE4100_SSSR_TFL_MASK;
break;
default:
mask = SSSR_TFL_MASK;
break;
@ -216,6 +231,9 @@ static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data,
case QUARK_X1000_SSP:
mask = QUARK_X1000_SSCR1_RFT;
break;
case CE4100_SSP:
mask = CE4100_SSCR1_RFT;
break;
default:
mask = SSCR1_RFT;
break;
@ -230,6 +248,9 @@ static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data,
case QUARK_X1000_SSP:
*sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold);
break;
case CE4100_SSP:
*sccr1_reg |= CE4100_SSCR1_RxTresh(threshold);
break;
default:
*sccr1_reg |= SSCR1_RxTresh(threshold);
break;
@ -316,7 +337,7 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data,
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
cs = drv_data->cur_msg->spi->chip_select;
cs = drv_data->master->cur_msg->spi->chip_select;
cs <<= config->cs_sel_shift;
if (cs != (value & config->cs_sel_mask)) {
/*
@ -355,10 +376,11 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
static void cs_assert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
if (drv_data->ssp_type == CE4100_SSP) {
pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm);
pxa2xx_spi_write(drv_data, SSSR, chip->frm);
return;
}
@ -378,7 +400,8 @@ static void cs_assert(struct driver_data *drv_data)
static void cs_deassert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
if (drv_data->ssp_type == CE4100_SSP)
return;
@ -508,7 +531,7 @@ static int u32_reader(struct driver_data *drv_data)
void *pxa2xx_spi_next_transfer(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
struct spi_message *msg = drv_data->master->cur_msg;
struct spi_transfer *trans = drv_data->cur_transfer;
/* Move to next transfer */
@ -529,8 +552,7 @@ static void giveback(struct driver_data *drv_data)
struct spi_message *msg;
unsigned long timeout;
msg = drv_data->cur_msg;
drv_data->cur_msg = NULL;
msg = drv_data->master->cur_msg;
drv_data->cur_transfer = NULL;
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
@ -575,13 +597,13 @@ static void giveback(struct driver_data *drv_data)
cs_deassert(drv_data);
}
drv_data->cur_chip = NULL;
spi_finalize_current_message(drv_data->master);
}
static void reset_sccr1(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
u32 sccr1_reg;
sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1;
@ -589,6 +611,9 @@ static void reset_sccr1(struct driver_data *drv_data)
case QUARK_X1000_SSP:
sccr1_reg &= ~QUARK_X1000_SSCR1_RFT;
break;
case CE4100_SSP:
sccr1_reg &= ~CE4100_SSCR1_RFT;
break;
default:
sccr1_reg &= ~SSCR1_RFT;
break;
@ -610,7 +635,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)
dev_err(&drv_data->pdev->dev, "%s\n", msg);
drv_data->cur_msg->state = ERROR_STATE;
drv_data->master->cur_msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
}
@ -623,7 +648,7 @@ static void int_transfer_complete(struct driver_data *drv_data)
pxa2xx_spi_write(drv_data, SSTO, 0);
/* Update total byte transferred return count actual bytes read */
drv_data->cur_msg->actual_length += drv_data->len -
drv_data->master->cur_msg->actual_length += drv_data->len -
(drv_data->rx_end - drv_data->rx);
/* Transfer delays and chip select release are
@ -631,7 +656,7 @@ static void int_transfer_complete(struct driver_data *drv_data)
*/
/* Move to next transfer */
drv_data->cur_msg->state = pxa2xx_spi_next_transfer(drv_data);
drv_data->master->cur_msg->state = pxa2xx_spi_next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
@ -746,7 +771,7 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
if (!(status & mask))
return IRQ_NONE;
if (!drv_data->cur_msg) {
if (!drv_data->master->cur_msg) {
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0)
@ -905,7 +930,8 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
int rate)
{
struct chip_data *chip = drv_data->cur_chip;
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
unsigned int clk_div;
switch (drv_data->ssp_type) {
@ -934,25 +960,23 @@ static void pump_transfers(unsigned long data)
{
struct driver_data *drv_data = (struct driver_data *)data;
struct spi_master *master = drv_data->master;
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
struct chip_data *chip = NULL;
u32 clk_div = 0;
u8 bits = 0;
u32 speed = 0;
struct spi_message *message = master->cur_msg;
struct chip_data *chip = spi_get_ctldata(message->spi);
u32 dma_thresh = chip->dma_threshold;
u32 dma_burst = chip->dma_burst_size;
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
struct spi_transfer *transfer;
struct spi_transfer *previous;
u32 clk_div;
u8 bits;
u32 speed;
u32 cr0;
u32 cr1;
u32 dma_thresh = drv_data->cur_chip->dma_threshold;
u32 dma_burst = drv_data->cur_chip->dma_burst_size;
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
int err;
int dma_mapped;
/* Get current state information */
message = drv_data->cur_msg;
transfer = drv_data->cur_transfer;
chip = drv_data->cur_chip;
/* Handle for abort */
if (message->state == ERROR_STATE) {
@ -1146,17 +1170,12 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
{
struct driver_data *drv_data = spi_master_get_devdata(master);
drv_data->cur_msg = msg;
/* Initial message state*/
drv_data->cur_msg->state = START_STATE;
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
msg->state = START_STATE;
drv_data->cur_transfer = list_entry(msg->transfers.next,
struct spi_transfer,
transfer_list);
/* prepare to setup the SSP, in pump_transfers, using the per
* chip configuration */
drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
/* Mark as busy and launch transfers */
tasklet_schedule(&drv_data->pump_transfers);
return 0;
@ -1176,9 +1195,26 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
static int setup_cs(struct spi_device *spi, struct chip_data *chip,
struct pxa2xx_spi_chip *chip_info)
{
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
int err = 0;
if (chip == NULL || chip_info == NULL)
if (chip == NULL)
return 0;
if (drv_data->cs_gpiods) {
struct gpio_desc *gpiod;
gpiod = drv_data->cs_gpiods[spi->chip_select];
if (gpiod) {
chip->gpio_cs = desc_to_gpio(gpiod);
chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;
gpiod_set_value(gpiod, chip->gpio_cs_inverted);
}
return 0;
}
if (chip_info == NULL)
return 0;
/* NOTE: setup() can be called multiple times, possibly with
@ -1213,7 +1249,7 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip,
static int setup(struct spi_device *spi)
{
struct pxa2xx_spi_chip *chip_info = NULL;
struct pxa2xx_spi_chip *chip_info;
struct chip_data *chip;
const struct lpss_config *config;
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
@ -1225,6 +1261,11 @@ static int setup(struct spi_device *spi)
tx_hi_thres = 0;
rx_thres = RX_THRESH_QUARK_X1000_DFLT;
break;
case CE4100_SSP:
tx_thres = TX_THRESH_CE4100_DFLT;
tx_hi_thres = 0;
rx_thres = RX_THRESH_CE4100_DFLT;
break;
case LPSS_LPT_SSP:
case LPSS_BYT_SSP:
case LPSS_BSW_SSP:
@ -1309,6 +1350,10 @@ static int setup(struct spi_device *spi)
| (QUARK_X1000_SSCR1_TxTresh(tx_thres)
& QUARK_X1000_SSCR1_TFT);
break;
case CE4100_SSP:
chip->threshold = (CE4100_SSCR1_RxTresh(rx_thres) & CE4100_SSCR1_RFT) |
(CE4100_SSCR1_TxTresh(tx_thres) & CE4100_SSCR1_TFT);
break;
default:
chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
(SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
@ -1352,7 +1397,8 @@ static void cleanup(struct spi_device *spi)
if (!chip)
return;
if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs))
if (drv_data->ssp_type != CE4100_SSP && !drv_data->cs_gpiods &&
gpio_is_valid(chip->gpio_cs))
gpio_free(chip->gpio_cs);
kfree(chip);
@ -1530,7 +1576,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
struct driver_data *drv_data;
struct ssp_device *ssp;
const struct lpss_config *config;
int status;
int status, count;
u32 tmp;
platform_info = dev_get_platdata(dev);
@ -1630,15 +1676,20 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
pxa2xx_spi_write(drv_data, SSCR0, 0);
switch (drv_data->ssp_type) {
case QUARK_X1000_SSP:
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT)
| QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) |
QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
pxa2xx_spi_write(drv_data, SSCR1, tmp);
/* using the Motorola SPI protocol and use 8 bit frame */
pxa2xx_spi_write(drv_data, SSCR0,
QUARK_X1000_SSCR0_Motorola
| QUARK_X1000_SSCR0_DataSize(8));
tmp = QUARK_X1000_SSCR0_Motorola | QUARK_X1000_SSCR0_DataSize(8);
pxa2xx_spi_write(drv_data, SSCR0, tmp);
break;
case CE4100_SSP:
tmp = CE4100_SSCR1_RxTresh(RX_THRESH_CE4100_DFLT) |
CE4100_SSCR1_TxTresh(TX_THRESH_CE4100_DFLT);
pxa2xx_spi_write(drv_data, SSCR1, tmp);
tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8);
pxa2xx_spi_write(drv_data, SSCR0, tmp);
default:
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
SSCR1_TxTresh(TX_THRESH_DFLT);
@ -1669,6 +1720,39 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
}
master->num_chipselect = platform_info->num_chipselect;
count = gpiod_count(&pdev->dev, "cs");
if (count > 0) {
int i;
master->num_chipselect = max_t(int, count,
master->num_chipselect);
drv_data->cs_gpiods = devm_kcalloc(&pdev->dev,
master->num_chipselect, sizeof(struct gpio_desc *),
GFP_KERNEL);
if (!drv_data->cs_gpiods) {
status = -ENOMEM;
goto out_error_clock_enabled;
}
for (i = 0; i < master->num_chipselect; i++) {
struct gpio_desc *gpiod;
gpiod = devm_gpiod_get_index(dev, "cs", i,
GPIOD_OUT_HIGH);
if (IS_ERR(gpiod)) {
/* Means use native chip select */
if (PTR_ERR(gpiod) == -ENOENT)
continue;
status = (int)PTR_ERR(gpiod);
goto out_error_clock_enabled;
} else {
drv_data->cs_gpiods[i] = gpiod;
}
}
}
tasklet_init(&drv_data->pump_transfers, pump_transfers,
(unsigned long)drv_data);
@ -1742,7 +1826,7 @@ static int pxa2xx_spi_suspend(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
struct ssp_device *ssp = drv_data->ssp;
int status = 0;
int status;
status = spi_master_suspend(drv_data->master);
if (status != 0)
@ -1759,7 +1843,7 @@ static int pxa2xx_spi_resume(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
struct ssp_device *ssp = drv_data->ssp;
int status = 0;
int status;
/* Enable the SSP clock */
if (!pm_runtime_suspended(dev))

View File

@ -53,9 +53,7 @@ struct driver_data {
atomic_t dma_running;
/* Current message transfer state info */
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct chip_data *cur_chip;
size_t len;
void *tx;
void *tx_end;
@ -68,6 +66,9 @@ struct driver_data {
void (*cs_control)(u32 command);
void __iomem *lpss_base;
/* GPIOs for chip selects */
struct gpio_desc **cs_gpiods;
};
struct chip_data {

View File

@ -982,8 +982,10 @@ static int spi_qup_suspend(struct device *device)
if (ret)
return ret;
clk_disable_unprepare(controller->cclk);
clk_disable_unprepare(controller->iclk);
if (!pm_runtime_suspended(device)) {
clk_disable_unprepare(controller->cclk);
clk_disable_unprepare(controller->iclk);
}
return 0;
}

View File

@ -295,14 +295,24 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size)
static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size)
{
int spbr;
int div = 0;
unsigned long clksrc;
/* Sets output mode, MOSI signal, and (optionally) loopback */
rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR);
clksrc = clk_get_rate(rspi->clk);
while (div < 3) {
if (rspi->max_speed_hz >= clksrc/4) /* 4=(CLK/2)/2 */
break;
div++;
clksrc /= 2;
}
/* Sets transfer bit rate */
spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk),
2 * rspi->max_speed_hz) - 1;
spbr = DIV_ROUND_UP(clksrc, 2 * rspi->max_speed_hz) - 1;
rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
rspi->spcmd |= div << 2;
/* Disable dummy transmission, set byte access */
rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR);

View File

@ -23,6 +23,7 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/platform_data/sc18is602.h>
#include <linux/gpio/consumer.h>
enum chips { sc18is602, sc18is602b, sc18is603 };
@ -50,6 +51,8 @@ struct sc18is602 {
u8 buffer[SC18IS602_BUFSIZ + 1];
int tlen; /* Data queued for tx in buffer */
int rindex; /* Receive data index in buffer */
struct gpio_desc *reset;
};
static int sc18is602_wait_ready(struct sc18is602 *hw, int len)
@ -257,6 +260,12 @@ static int sc18is602_probe(struct i2c_client *client,
hw = spi_master_get_devdata(master);
i2c_set_clientdata(client, hw);
/* assert reset and then release */
hw->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(hw->reset))
return PTR_ERR(hw->reset);
gpiod_set_value_cansleep(hw->reset, 0);
hw->master = master;
hw->client = client;
hw->dev = dev;

View File

@ -175,10 +175,7 @@ static int spi_st_transfer_one(struct spi_master *master,
static void spi_st_cleanup(struct spi_device *spi)
{
int cs = spi->cs_gpio;
if (gpio_is_valid(cs))
devm_gpio_free(&spi->dev, cs);
gpio_free(spi->cs_gpio);
}
/* the spi->mode bits understood by this driver: */
@ -201,14 +198,15 @@ static int spi_st_setup(struct spi_device *spi)
return -EINVAL;
}
if (devm_gpio_request(&spi->dev, cs, dev_name(&spi->dev))) {
ret = gpio_request(cs, dev_name(&spi->dev));
if (ret) {
dev_err(&spi->dev, "could not request gpio:%d\n", cs);
return -EINVAL;
return ret;
}
ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH);
if (ret)
return ret;
goto out_free_gpio;
spi_st_clk = clk_get_rate(spi_st->clk);
@ -217,7 +215,8 @@ static int spi_st_setup(struct spi_device *spi)
if (sscbrg < 0x07 || sscbrg > BIT(16)) {
dev_err(&spi->dev,
"baudrate %d outside valid range %d\n", sscbrg, hz);
return -EINVAL;
ret = -EINVAL;
goto out_free_gpio;
}
spi_st->baud = spi_st_clk / (2 * sscbrg);
@ -266,6 +265,10 @@ static int spi_st_setup(struct spi_device *spi)
readl_relaxed(spi_st->base + SSC_RBUF);
return 0;
out_free_gpio:
gpio_free(cs);
return ret;
}
/* Interrupt fired when TX shift register becomes empty */

View File

@ -41,6 +41,8 @@ struct ti_qspi_regs {
};
struct ti_qspi {
struct completion transfer_complete;
/* list synchronization */
struct mutex list_lock;
@ -54,6 +56,9 @@ struct ti_qspi {
struct ti_qspi_regs ctx_reg;
dma_addr_t mmap_phys_base;
struct dma_chan *rx_chan;
u32 spi_max_frequency;
u32 cmd;
u32 dc;
@ -379,6 +384,72 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t,
return 0;
}
static void ti_qspi_dma_callback(void *param)
{
struct ti_qspi *qspi = param;
complete(&qspi->transfer_complete);
}
static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
dma_addr_t dma_src, size_t len)
{
struct dma_chan *chan = qspi->rx_chan;
struct dma_device *dma_dev = chan->device;
dma_cookie_t cookie;
enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
struct dma_async_tx_descriptor *tx;
int ret;
tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
len, flags);
if (!tx) {
dev_err(qspi->dev, "device_prep_dma_memcpy error\n");
return -EIO;
}
tx->callback = ti_qspi_dma_callback;
tx->callback_param = qspi;
cookie = tx->tx_submit(tx);
ret = dma_submit_error(cookie);
if (ret) {
dev_err(qspi->dev, "dma_submit_error %d\n", cookie);
return -EIO;
}
dma_async_issue_pending(chan);
ret = wait_for_completion_timeout(&qspi->transfer_complete,
msecs_to_jiffies(len));
if (ret <= 0) {
dmaengine_terminate_sync(chan);
dev_err(qspi->dev, "DMA wait_for_completion_timeout\n");
return -ETIMEDOUT;
}
return 0;
}
static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg,
loff_t from)
{
struct scatterlist *sg;
dma_addr_t dma_src = qspi->mmap_phys_base + from;
dma_addr_t dma_dst;
int i, len, ret;
for_each_sg(rx_sg.sgl, sg, rx_sg.nents, i) {
dma_dst = sg_dma_address(sg);
len = sg_dma_len(sg);
ret = ti_qspi_dma_xfer(qspi, dma_dst, dma_src, len);
if (ret)
return ret;
dma_src += len;
}
return 0;
}
static void ti_qspi_enable_memory_map(struct spi_device *spi)
{
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
@ -426,7 +497,7 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
QSPI_SPI_SETUP_REG(spi->chip_select));
}
static int ti_qspi_spi_flash_read(struct spi_device *spi,
static int ti_qspi_spi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
{
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
@ -437,9 +508,23 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
if (!qspi->mmap_enabled)
ti_qspi_enable_memory_map(spi);
ti_qspi_setup_mmap_read(spi, msg);
memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
if (qspi->rx_chan) {
if (msg->cur_msg_mapped) {
ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
if (ret)
goto err_unlock;
} else {
dev_err(qspi->dev, "Invalid address for DMA\n");
ret = -EIO;
goto err_unlock;
}
} else {
memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
}
msg->retlen = msg->len;
err_unlock:
mutex_unlock(&qspi->list_lock);
return ret;
@ -536,6 +621,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
u32 max_freq;
int ret = 0, num_cs, irq;
dma_cap_mask_t mask;
master = spi_alloc_master(&pdev->dev, sizeof(*qspi));
if (!master)
@ -550,6 +636,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
master->dev.of_node = pdev->dev.of_node;
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
SPI_BPW_MASK(8);
master->spi_flash_read = ti_qspi_spi_flash_read;
if (!of_property_read_u32(np, "num-cs", &num_cs))
master->num_chipselect = num_cs;
@ -592,17 +679,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
goto free_master;
}
if (res_mmap) {
qspi->mmap_base = devm_ioremap_resource(&pdev->dev,
res_mmap);
master->spi_flash_read = ti_qspi_spi_flash_read;
if (IS_ERR(qspi->mmap_base)) {
dev_err(&pdev->dev,
"falling back to PIO mode\n");
master->spi_flash_read = NULL;
}
}
qspi->mmap_enabled = false;
if (of_property_read_bool(np, "syscon-chipselects")) {
qspi->ctrl_base =
@ -633,11 +709,37 @@ static int ti_qspi_probe(struct platform_device *pdev)
if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
qspi->spi_max_frequency = max_freq;
ret = devm_spi_register_master(&pdev->dev, master);
if (ret)
goto free_master;
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
return 0;
qspi->rx_chan = dma_request_chan_by_mask(&mask);
if (!qspi->rx_chan) {
dev_err(qspi->dev,
"No Rx DMA available, trying mmap mode\n");
ret = 0;
goto no_dma;
}
master->dma_rx = qspi->rx_chan;
init_completion(&qspi->transfer_complete);
if (res_mmap)
qspi->mmap_phys_base = (dma_addr_t)res_mmap->start;
no_dma:
if (!qspi->rx_chan && res_mmap) {
qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap);
if (IS_ERR(qspi->mmap_base)) {
dev_info(&pdev->dev,
"mmap failed with error %ld using PIO mode\n",
PTR_ERR(qspi->mmap_base));
qspi->mmap_base = NULL;
master->spi_flash_read = NULL;
}
}
qspi->mmap_enabled = false;
ret = devm_spi_register_master(&pdev->dev, master);
if (!ret)
return 0;
free_master:
spi_master_put(master);
@ -656,6 +758,9 @@ static int ti_qspi_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (qspi->rx_chan)
dma_release_channel(qspi->rx_chan);
return 0;
}

View File

@ -346,7 +346,7 @@ static int txx9spi_probe(struct platform_device *dev)
c->clk = NULL;
goto exit;
}
ret = clk_enable(c->clk);
ret = clk_prepare_enable(c->clk);
if (ret) {
c->clk = NULL;
goto exit;
@ -395,7 +395,7 @@ static int txx9spi_probe(struct platform_device *dev)
exit_busy:
ret = -EBUSY;
exit:
clk_disable(c->clk);
clk_disable_unprepare(c->clk);
spi_master_put(master);
return ret;
}
@ -406,7 +406,7 @@ static int txx9spi_remove(struct platform_device *dev)
struct txx9spi *c = spi_master_get_devdata(master);
flush_work(&c->work);
clk_disable(c->clk);
clk_disable_unprepare(c->clk);
return 0;
}

View File

@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -405,8 +406,9 @@ static int xlp_spi_probe(struct platform_device *pdev)
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "could not get spi clock\n");
return -ENODEV;
return PTR_ERR(clk);
}
xspi->spi_clk = clk_get_rate(clk);
master = spi_alloc_master(&pdev->dev, 0);
@ -437,6 +439,14 @@ static int xlp_spi_probe(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id xlp_spi_acpi_match[] = {
{ "BRCM900D", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, xlp_spi_acpi_match);
#endif
static const struct of_device_id xlp_spi_dt_id[] = {
{ .compatible = "netlogic,xlp832-spi" },
{ },
@ -447,6 +457,7 @@ static struct platform_driver xlp_spi_driver = {
.driver = {
.name = "xlp-spi",
.of_match_table = xlp_spi_dt_id,
.acpi_match_table = ACPI_PTR(xlp_spi_acpi_match),
},
};
module_platform_driver(xlp_spi_driver);

View File

@ -37,6 +37,7 @@
#include <linux/kthread.h>
#include <linux/ioport.h>
#include <linux/acpi.h>
#include <linux/highmem.h>
#define CREATE_TRACE_POINTS
#include <trace/events/spi.h>
@ -709,6 +710,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
{
const bool vmalloced_buf = is_vmalloc_addr(buf);
unsigned int max_seg_size = dma_get_max_seg_size(dev);
#ifdef CONFIG_HIGHMEM
const bool kmap_buf = ((unsigned long)buf >= PKMAP_BASE &&
(unsigned long)buf < (PKMAP_BASE +
(LAST_PKMAP * PAGE_SIZE)));
#else
const bool kmap_buf = false;
#endif
int desc_len;
int sgs;
struct page *vm_page;
@ -716,7 +724,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
size_t min;
int i, ret;
if (vmalloced_buf) {
if (vmalloced_buf || kmap_buf) {
desc_len = min_t(int, max_seg_size, PAGE_SIZE);
sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);
} else if (virt_addr_valid(buf)) {
@ -732,10 +740,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
for (i = 0; i < sgs; i++) {
if (vmalloced_buf) {
if (vmalloced_buf || kmap_buf) {
min = min_t(size_t,
len, desc_len - offset_in_page(buf));
vm_page = vmalloc_to_page(buf);
if (vmalloced_buf)
vm_page = vmalloc_to_page(buf);
else
vm_page = kmap_to_page(buf);
if (!vm_page) {
sg_free_table(sgt);
return -ENOMEM;

View File

@ -83,7 +83,6 @@
#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */
#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */
#ifdef CONFIG_ARCH_PXA
#define RX_THRESH_DFLT 8
#define TX_THRESH_DFLT 8
@ -95,19 +94,16 @@
#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
#else
#define RX_THRESH_CE4100_DFLT 2
#define TX_THRESH_CE4100_DFLT 2
#define RX_THRESH_DFLT 2
#define TX_THRESH_DFLT 2
#define CE4100_SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
#define CE4100_SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
#define SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
#define SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
#define SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
#define SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */
#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
#endif
#define CE4100_SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
#define CE4100_SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
#define CE4100_SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */
#define CE4100_SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
/* QUARK_X1000 SSCR0 bit definition */
#define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */

View File

@ -312,6 +312,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @flags: other constraints relevant to this driver
* @max_transfer_size: function that returns the max transfer size for
* a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
* @max_message_size: function that returns the max message size for
* a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
* @io_mutex: mutex for physical bus access
* @bus_lock_spinlock: spinlock for SPI bus locking
* @bus_lock_mutex: mutex for exclusion of multiple callers
@ -442,10 +444,11 @@ struct spi_master {
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/*
* on some hardware transfer size may be constrained
* on some hardware transfer / message size may be constrained
* the limit may depend on device transfer settings
*/
size_t (*max_transfer_size)(struct spi_device *spi);
size_t (*max_message_size)(struct spi_device *spi);
/* I/O mutex */
struct mutex io_mutex;
@ -904,13 +907,27 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi,
struct spi_message *message);
static inline size_t
spi_max_message_size(struct spi_device *spi)
{
struct spi_master *master = spi->master;
if (!master->max_message_size)
return SIZE_MAX;
return master->max_message_size(spi);
}
static inline size_t
spi_max_transfer_size(struct spi_device *spi)
{
struct spi_master *master = spi->master;
if (!master->max_transfer_size)
return SIZE_MAX;
return master->max_transfer_size(spi);
size_t tr_max = SIZE_MAX;
size_t msg_max = spi_max_message_size(spi);
if (master->max_transfer_size)
tr_max = master->max_transfer_size(spi);
/* transfer size limit must not be greater than messsage size limit */
return min(tr_max, msg_max);
}
/*---------------------------------------------------------------------------*/
@ -979,58 +996,6 @@ extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message);
extern int spi_bus_lock(struct spi_master *master);
extern int spi_bus_unlock(struct spi_master *master);
/**
* spi_write - SPI synchronous write
* @spi: device to which data will be written
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function writes the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
/**
* spi_read - SPI synchronous read
* @spi: device from which data will be read
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function reads the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
/**
* spi_sync_transfer - synchronous SPI data transfer
* @spi: device with which data will be exchanged
@ -1055,6 +1020,52 @@ spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
return spi_sync(spi, &msg);
}
/**
* spi_write - SPI synchronous write
* @spi: device to which data will be written
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function writes the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
/**
* spi_read - SPI synchronous read
* @spi: device from which data will be read
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function reads the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,

View File

@ -1,3 +1,5 @@
CC = $(CROSS_COMPILE)gcc
all: spidev_test spidev_fdx
clean:

View File

@ -19,6 +19,7 @@
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
@ -284,7 +285,7 @@ static void parse_opts(int argc, char *argv[])
static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str + 1);
size_t size = strlen(str);
uint8_t *tx;
uint8_t *rx;