mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-20 10:44:23 +08:00
spi: Updates for v4.15
This release is almost entirely driver changes, there's a couple of fixes in the core but otherwise it's all drivers: - Fix for mixed dynamic and static bus number assignment. - Fixes for some leaks arising from confusing lifetime rules during device unregistration and improved documentation to try to help avoid this in the future. - Fixes to make the native chip select support for i.MX usable. - Slave mode support for i.MX. - Support for Coldfire MCF5441x DSPI, Renesas R8A7443/5 and Spreadtrum ADI. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAloJi2ATHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0IehB/9cM3BMqbo6cSkKvYRcBOHyQZqtBkbc LlLcMt9hpqWPA9oYU6D6SnZrMDNz8fr8YmpP279OWFJc/l8vuMpzOCz2tOB/OZyE 9fANUhu98HioVS48vN0rlgsaW48rQDothtvwbAP8ED2LGivhruay6G+oG062r7/V nneWD8YQLmNSd4FaA2qKl0DNI7afkmnqakhX/c9cvctZvGCJmxW//Y20cOyiSL7T g0DXk5yirZGI/wO5WxOfCXT9lDCdz8XYZJmOC/Uh5X9gc4qmK8RSC/ydsmRwWDFm 4Xq542cooKFyuMQLiVqi3ukixk8rMi7htGN464m9aMAu2Hn4IuJjZaOh =wkKu -----END PGP SIGNATURE----- Merge tag 'spi-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "This release is almost entirely driver changes, there's a couple of fixes in the core but otherwise it's all drivers: - fix for mixed dynamic and static bus number assignment. - fixes for some leaks arising from confusing lifetime rules during device unregistration and improved documentation to try to help avoid this in the future. - fixes to make the native chip select support for i.MX usable. - slave mode support for i.MX. - support for Coldfire MCF5441x DSPI, Renesas R8A7443/5 and Spreadtrum ADI" * tag 'spi-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (31 commits) spi: imx: Don't require platform data chipselect array spi: imx: Fix failure path leak on GPIO request error spi: imx: GPIO based chip selects should not be required spi: sh-msiof: remove redundant pointer dev spi: s3c64xx: remove redundant pointer sci spi: spi-fsl-dspi: enabling Coldfire mcf5441x dspi spi: fix IDR collision on systems with both fixed and dynamic SPI bus numbers spi: orion: remove redundant assignment of status to zero spi: sh-msiof: Fix DMA transfer size check spi: imx: Fix failure path leak on GPIO request error spi: spi-axi: fix potential use-after-free after deregistration spi: document odd controller reference handling spi: fix use-after-free at controller deregistration spi: sprd: Fix the possible negative value of BIT() spi: sprd-adi: fix platform_no_drv_owner.cocci warnings spi: a3700: Change SPI mode before asserting chip-select spi: tegra114: correct register name in definition spi: spreadtrum adi: add hwspinlock dependency spi: sh-msiof: Use of_device_get_match_data() helper spi: rspi: Use of_device_get_match_data() helper ...
This commit is contained in:
commit
e75427c694
@ -1,7 +1,9 @@
|
||||
Renesas MSIOF spi controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "renesas,msiof-r8a7790" (R-Car H2)
|
||||
- compatible : "renesas,msiof-r8a7743" (RZ/G1M)
|
||||
"renesas,msiof-r8a7745" (RZ/G1E)
|
||||
"renesas,msiof-r8a7790" (R-Car H2)
|
||||
"renesas,msiof-r8a7791" (R-Car M2-W)
|
||||
"renesas,msiof-r8a7792" (R-Car V2H)
|
||||
"renesas,msiof-r8a7793" (R-Car M2-N)
|
||||
@ -10,7 +12,7 @@ Required properties:
|
||||
"renesas,msiof-r8a7796" (R-Car M3-W)
|
||||
"renesas,msiof-sh73a0" (SH-Mobile AG5)
|
||||
"renesas,sh-mobile-msiof" (generic SH-Mobile compatibile device)
|
||||
"renesas,rcar-gen2-msiof" (generic R-Car Gen2 compatible device)
|
||||
"renesas,rcar-gen2-msiof" (generic R-Car Gen2 and RZ/G1 compatible device)
|
||||
"renesas,rcar-gen3-msiof" (generic R-Car Gen3 compatible device)
|
||||
"renesas,sh-msiof" (deprecated)
|
||||
|
||||
|
@ -24,6 +24,16 @@ Required properties:
|
||||
based on a specific SoC configuration.
|
||||
- interrupts: interrupt number mapped to CPU.
|
||||
- clocks: spi clk phandle
|
||||
For 66AK2G this property should be set per binding,
|
||||
Documentation/devicetree/bindings/clock/ti,sci-clk.txt
|
||||
|
||||
SoC-specific Required Properties:
|
||||
|
||||
The following are mandatory properties for Keystone 2 66AK2G SoCs only:
|
||||
|
||||
- power-domains: Should contain a phandle to a PM domain provider node
|
||||
and an args specifier containing the SPI device id
|
||||
value. This property is as per the binding,
|
||||
|
||||
Optional:
|
||||
- cs-gpios: gpio chip selects
|
||||
|
@ -5,11 +5,14 @@ Required properties:
|
||||
"renesas,rspi-<soctype>", "renesas,rspi" as fallback.
|
||||
For Renesas Serial Peripheral Interface on RZ/A1H:
|
||||
"renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
|
||||
For Quad Serial Peripheral Interface on R-Car Gen2:
|
||||
For Quad Serial Peripheral Interface on R-Car Gen2 and
|
||||
RZ/G1 devices:
|
||||
"renesas,qspi-<soctype>", "renesas,qspi" as fallback.
|
||||
Examples with soctypes are:
|
||||
- "renesas,rspi-sh7757" (SH)
|
||||
- "renesas,rspi-r7s72100" (RZ/A1H)
|
||||
- "renesas,qspi-r8a7743" (RZ/G1M)
|
||||
- "renesas,qspi-r8a7745" (RZ/G1E)
|
||||
- "renesas,qspi-r8a7790" (R-Car H2)
|
||||
- "renesas,qspi-r8a7791" (R-Car M2-W)
|
||||
- "renesas,qspi-r8a7792" (R-Car V2H)
|
||||
|
58
Documentation/devicetree/bindings/spi/spi-sprd-adi.txt
Normal file
58
Documentation/devicetree/bindings/spi/spi-sprd-adi.txt
Normal file
@ -0,0 +1,58 @@
|
||||
Spreadtrum ADI controller
|
||||
|
||||
ADI is the abbreviation of Anolog-Digital interface, which is used to access
|
||||
analog chip (such as PMIC) from digital chip. ADI controller follows the SPI
|
||||
framework for its hardware implementation is alike to SPI bus and its timing
|
||||
is compatile to SPI timing.
|
||||
|
||||
ADI controller has 50 channels including 2 software read/write channels and
|
||||
48 hardware channels to access analog chip. For 2 software read/write channels,
|
||||
users should set ADI registers to access analog chip. For hardware channels,
|
||||
we can configure them to allow other hardware components to use it independently,
|
||||
which means we can just link one analog chip address to one hardware channel,
|
||||
then users can access the mapped analog chip address by this hardware channel
|
||||
triggered by hardware components instead of ADI software channels.
|
||||
|
||||
Thus we introduce one property named "sprd,hw-channels" to configure hardware
|
||||
channels, the first value specifies the hardware channel id which is used to
|
||||
transfer data triggered by hardware automatically, and the second value specifies
|
||||
the analog chip address where user want to access by hardware components.
|
||||
|
||||
Since we have multi-subsystems will use unique ADI to access analog chip, when
|
||||
one system is reading/writing data by ADI software channels, that should be under
|
||||
one hardware spinlock protection to prevent other systems from reading/writing
|
||||
data by ADI software channels at the same time, or two parallel routine of setting
|
||||
ADI registers will make ADI controller registers chaos to lead incorrect results.
|
||||
Then we need one hardware spinlock to synchronize between the multiple subsystems.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-adi".
|
||||
- reg: Offset and length of ADI-SPI controller register space.
|
||||
- hwlocks: Reference to a phandle of a hwlock provider node.
|
||||
- hwlock-names: Reference to hwlock name strings defined in the same order
|
||||
as the hwlocks, should be "adi".
|
||||
- #address-cells: Number of cells required to define a chip select address
|
||||
on the ADI-SPI bus. Should be set to 1.
|
||||
- #size-cells: Size of cells required to define a chip select address size
|
||||
on the ADI-SPI bus. Should be set to 0.
|
||||
|
||||
Optional properties:
|
||||
- sprd,hw-channels: This is an array of channel values up to 49 channels.
|
||||
The first value specifies the hardware channel id which is used to
|
||||
transfer data triggered by hardware automatically, and the second
|
||||
value specifies the analog chip address where user want to access
|
||||
by hardware components.
|
||||
|
||||
SPI slave nodes must be children of the SPI controller node and can contain
|
||||
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
|
||||
Example:
|
||||
adi_bus: spi@40030000 {
|
||||
compatible = "sprd,sc9860-adi";
|
||||
reg = <0 0x40030000 0 0x10000>;
|
||||
hwlocks = <&hwlock1 0>;
|
||||
hwlock-names = "adi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
sprd,hw-channels = <30 0x8c20>;
|
||||
};
|
@ -1,10 +1,6 @@
|
||||
#
|
||||
# SPI driver configuration
|
||||
#
|
||||
# NOTE: the reason this doesn't show SPI slave support is mostly that
|
||||
# nobody's needed a slave side API yet. The master-role API is not
|
||||
# fully appropriate there, so it'd need some thought to do well.
|
||||
#
|
||||
menuconfig SPI
|
||||
bool "SPI support"
|
||||
depends on HAS_IOMEM
|
||||
@ -379,7 +375,7 @@ config SPI_FSL_DSPI
|
||||
tristate "Freescale DSPI controller"
|
||||
select REGMAP_MMIO
|
||||
depends on HAS_DMA
|
||||
depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
|
||||
depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST
|
||||
help
|
||||
This enables support for the Freescale DSPI controller in master
|
||||
mode. VF610 platform uses the controller.
|
||||
@ -626,6 +622,13 @@ config SPI_SIRF
|
||||
help
|
||||
SPI driver for CSR SiRFprimaII SoCs
|
||||
|
||||
config SPI_SPRD_ADI
|
||||
tristate "Spreadtrum ADI controller"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on HWSPINLOCK || (COMPILE_TEST && !HWSPINLOCK)
|
||||
help
|
||||
ADI driver based on SPI for Spreadtrum SoCs.
|
||||
|
||||
config SPI_STM32
|
||||
tristate "STMicroelectronics STM32 SPI controller"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
|
@ -91,6 +91,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
||||
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
|
||||
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
|
||||
obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o
|
||||
obj-$(CONFIG_SPI_STM32) += spi-stm32.o
|
||||
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
|
||||
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
|
||||
|
@ -213,7 +213,7 @@ static void a3700_spi_mode_set(struct a3700_spi *a3700_spi,
|
||||
}
|
||||
|
||||
static void a3700_spi_clock_set(struct a3700_spi *a3700_spi,
|
||||
unsigned int speed_hz, u16 mode)
|
||||
unsigned int speed_hz)
|
||||
{
|
||||
u32 val;
|
||||
u32 prescale;
|
||||
@ -231,17 +231,6 @@ static void a3700_spi_clock_set(struct a3700_spi *a3700_spi,
|
||||
val |= A3700_SPI_CLK_CAPT_EDGE;
|
||||
spireg_write(a3700_spi, A3700_SPI_IF_TIME_REG, val);
|
||||
}
|
||||
|
||||
val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
|
||||
val &= ~(A3700_SPI_CLK_POL | A3700_SPI_CLK_PHA);
|
||||
|
||||
if (mode & SPI_CPOL)
|
||||
val |= A3700_SPI_CLK_POL;
|
||||
|
||||
if (mode & SPI_CPHA)
|
||||
val |= A3700_SPI_CLK_PHA;
|
||||
|
||||
spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
|
||||
}
|
||||
|
||||
static void a3700_spi_bytelen_set(struct a3700_spi *a3700_spi, unsigned int len)
|
||||
@ -423,7 +412,7 @@ static void a3700_spi_transfer_setup(struct spi_device *spi,
|
||||
|
||||
a3700_spi = spi_master_get_devdata(spi->master);
|
||||
|
||||
a3700_spi_clock_set(a3700_spi, xfer->speed_hz, spi->mode);
|
||||
a3700_spi_clock_set(a3700_spi, xfer->speed_hz);
|
||||
|
||||
byte_len = xfer->bits_per_word >> 3;
|
||||
|
||||
@ -584,6 +573,8 @@ static int a3700_spi_prepare_message(struct spi_master *master,
|
||||
|
||||
a3700_spi_bytelen_set(a3700_spi, 4);
|
||||
|
||||
a3700_spi_mode_set(a3700_spi, spi->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -553,7 +553,7 @@ err_put_master:
|
||||
|
||||
static int spi_engine_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct spi_engine *spi_engine = spi_master_get_devdata(master);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
@ -561,6 +561,8 @@ static int spi_engine_remove(struct platform_device *pdev)
|
||||
|
||||
free_irq(irq, master);
|
||||
|
||||
spi_master_put(master);
|
||||
|
||||
writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
|
||||
writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
|
||||
writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-fsl-dspi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
@ -151,6 +152,11 @@ static const struct fsl_dspi_devtype_data ls2085a_data = {
|
||||
.max_clock_factor = 8,
|
||||
};
|
||||
|
||||
static const struct fsl_dspi_devtype_data coldfire_data = {
|
||||
.trans_mode = DSPI_EOQ_MODE,
|
||||
.max_clock_factor = 8,
|
||||
};
|
||||
|
||||
struct fsl_dspi_dma {
|
||||
/* Length of transfer in words of DSPI_FIFO_SIZE */
|
||||
u32 curr_xfer_len;
|
||||
@ -741,6 +747,7 @@ static int dspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct chip_data *chip;
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_dspi_platform_data *pdata;
|
||||
u32 cs_sck_delay = 0, sck_cs_delay = 0;
|
||||
unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0;
|
||||
unsigned char pasc = 0, asc = 0, fmsz = 0;
|
||||
@ -761,11 +768,18 @@ static int dspi_setup(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
|
||||
&cs_sck_delay);
|
||||
pdata = dev_get_platdata(&dspi->pdev->dev);
|
||||
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
|
||||
&sck_cs_delay);
|
||||
if (!pdata) {
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
|
||||
&cs_sck_delay);
|
||||
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
|
||||
&sck_cs_delay);
|
||||
} else {
|
||||
cs_sck_delay = pdata->cs_sck_delay;
|
||||
sck_cs_delay = pdata->sck_cs_delay;
|
||||
}
|
||||
|
||||
chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS |
|
||||
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
|
||||
@ -949,6 +963,7 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
struct fsl_dspi *dspi;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct fsl_dspi_platform_data *pdata;
|
||||
int ret = 0, cs_num, bus_num;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
|
||||
@ -969,25 +984,34 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) |
|
||||
SPI_BPW_MASK(16);
|
||||
|
||||
ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get spi-num-chipselects\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->num_chipselect = cs_num;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
master->num_chipselect = pdata->cs_num;
|
||||
master->bus_num = pdata->bus_num;
|
||||
|
||||
ret = of_property_read_u32(np, "bus-num", &bus_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get bus-num\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->bus_num = bus_num;
|
||||
dspi->devtype_data = &coldfire_data;
|
||||
} else {
|
||||
|
||||
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;
|
||||
goto out_master_put;
|
||||
ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get spi-num-chipselects\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->num_chipselect = cs_num;
|
||||
|
||||
ret = of_property_read_u32(np, "bus-num", &bus_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get bus-num\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->bus_num = bus_num;
|
||||
|
||||
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;
|
||||
goto out_master_put;
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -53,10 +53,13 @@
|
||||
/* generic defines to abstract from the different register layouts */
|
||||
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
||||
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
|
||||
#define MXC_INT_RDR BIT(4) /* Receive date threshold interrupt */
|
||||
|
||||
/* The maximum bytes that a sdma BD can transfer.*/
|
||||
#define MAX_SDMA_BD_BYTES (1 << 15)
|
||||
#define MX51_ECSPI_CTRL_MAX_BURST 512
|
||||
/* The maximum bytes that IMX53_ECSPI can transfer in slave mode.*/
|
||||
#define MX53_MAX_TRANSFER_BYTES 512
|
||||
|
||||
enum spi_imx_devtype {
|
||||
IMX1_CSPI,
|
||||
@ -76,7 +79,9 @@ struct spi_imx_devtype_data {
|
||||
void (*trigger)(struct spi_imx_data *);
|
||||
int (*rx_available)(struct spi_imx_data *);
|
||||
void (*reset)(struct spi_imx_data *);
|
||||
void (*disable)(struct spi_imx_data *);
|
||||
bool has_dmamode;
|
||||
bool has_slavemode;
|
||||
unsigned int fifo_size;
|
||||
bool dynamic_burst;
|
||||
enum spi_imx_devtype devtype;
|
||||
@ -108,6 +113,11 @@ struct spi_imx_data {
|
||||
unsigned int dynamic_burst, read_u32;
|
||||
unsigned int word_mask;
|
||||
|
||||
/* Slave mode */
|
||||
bool slave_mode;
|
||||
bool slave_aborted;
|
||||
unsigned int slave_burst;
|
||||
|
||||
/* DMA */
|
||||
bool usedma;
|
||||
u32 wml;
|
||||
@ -221,6 +231,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
if (!master->dma_rx)
|
||||
return false;
|
||||
|
||||
if (spi_imx->slave_mode)
|
||||
return false;
|
||||
|
||||
bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word);
|
||||
|
||||
if (bytes_per_word != 1 && bytes_per_word != 2 && bytes_per_word != 4)
|
||||
@ -262,6 +275,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
#define MX51_ECSPI_INT 0x10
|
||||
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
||||
#define MX51_ECSPI_INT_RREN (1 << 3)
|
||||
#define MX51_ECSPI_INT_RDREN (1 << 4)
|
||||
|
||||
#define MX51_ECSPI_DMA 0x14
|
||||
#define MX51_ECSPI_DMA_TX_WML(wml) ((wml) & 0x3f)
|
||||
@ -378,6 +392,44 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
|
||||
spi_imx_buf_tx_u16(spi_imx);
|
||||
}
|
||||
|
||||
static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 val = be32_to_cpu(readl(spi_imx->base + MXC_CSPIRXDATA));
|
||||
|
||||
if (spi_imx->rx_buf) {
|
||||
int n_bytes = spi_imx->slave_burst % sizeof(val);
|
||||
|
||||
if (!n_bytes)
|
||||
n_bytes = sizeof(val);
|
||||
|
||||
memcpy(spi_imx->rx_buf,
|
||||
((u8 *)&val) + sizeof(val) - n_bytes, n_bytes);
|
||||
|
||||
spi_imx->rx_buf += n_bytes;
|
||||
spi_imx->slave_burst -= n_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static void mx53_ecspi_tx_slave(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 val = 0;
|
||||
int n_bytes = spi_imx->count % sizeof(val);
|
||||
|
||||
if (!n_bytes)
|
||||
n_bytes = sizeof(val);
|
||||
|
||||
if (spi_imx->tx_buf) {
|
||||
memcpy(((u8 *)&val) + sizeof(val) - n_bytes,
|
||||
spi_imx->tx_buf, n_bytes);
|
||||
val = cpu_to_be32(val);
|
||||
spi_imx->tx_buf += n_bytes;
|
||||
}
|
||||
|
||||
spi_imx->count -= n_bytes;
|
||||
|
||||
writel(val, spi_imx->base + MXC_CSPITXDATA);
|
||||
}
|
||||
|
||||
/* MX51 eCSPI */
|
||||
static unsigned int mx51_ecspi_clkdiv(struct spi_imx_data *spi_imx,
|
||||
unsigned int fspi, unsigned int *fres)
|
||||
@ -427,6 +479,9 @@ static void mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int enable)
|
||||
if (enable & MXC_INT_RR)
|
||||
val |= MX51_ECSPI_INT_RREN;
|
||||
|
||||
if (enable & MXC_INT_RDR)
|
||||
val |= MX51_ECSPI_INT_RDREN;
|
||||
|
||||
writel(val, spi_imx->base + MX51_ECSPI_INT);
|
||||
}
|
||||
|
||||
@ -439,6 +494,15 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
|
||||
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
|
||||
}
|
||||
|
||||
static void mx51_ecspi_disable(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
ctrl &= ~MX51_ECSPI_CTRL_ENABLE;
|
||||
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
||||
}
|
||||
|
||||
static int mx51_ecspi_config(struct spi_device *spi)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
@ -446,14 +510,11 @@ static int mx51_ecspi_config(struct spi_device *spi)
|
||||
u32 clk = spi_imx->speed_hz, delay, reg;
|
||||
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
|
||||
|
||||
/*
|
||||
* The hardware seems to have a race condition when changing modes. The
|
||||
* current assumption is that the selection of the channel arrives
|
||||
* earlier in the hardware than the mode bits when they are written at
|
||||
* the same time.
|
||||
* So set master mode for all channels as we do not support slave mode.
|
||||
*/
|
||||
ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
|
||||
/* set Master or Slave mode */
|
||||
if (spi_imx->slave_mode)
|
||||
ctrl &= ~MX51_ECSPI_CTRL_MODE_MASK;
|
||||
else
|
||||
ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
|
||||
|
||||
/*
|
||||
* Enable SPI_RDY handling (falling edge/level triggered).
|
||||
@ -468,9 +529,22 @@ static int mx51_ecspi_config(struct spi_device *spi)
|
||||
/* set chip select to use */
|
||||
ctrl |= MX51_ECSPI_CTRL_CS(spi->chip_select);
|
||||
|
||||
ctrl |= (spi_imx->bits_per_word - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
|
||||
if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx))
|
||||
ctrl |= (spi_imx->slave_burst * 8 - 1)
|
||||
<< MX51_ECSPI_CTRL_BL_OFFSET;
|
||||
else
|
||||
ctrl |= (spi_imx->bits_per_word - 1)
|
||||
<< MX51_ECSPI_CTRL_BL_OFFSET;
|
||||
|
||||
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
|
||||
/*
|
||||
* eCSPI burst completion by Chip Select signal in Slave mode
|
||||
* is not functional for imx53 Soc, config SPI burst completed when
|
||||
* BURST_LENGTH + 1 bits are received
|
||||
*/
|
||||
if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx))
|
||||
cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
|
||||
else
|
||||
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
|
||||
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select);
|
||||
@ -805,6 +879,7 @@ static struct spi_imx_devtype_data imx1_cspi_devtype_data = {
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.has_slavemode = false,
|
||||
.devtype = IMX1_CSPI,
|
||||
};
|
||||
|
||||
@ -817,6 +892,7 @@ static struct spi_imx_devtype_data imx21_cspi_devtype_data = {
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.has_slavemode = false,
|
||||
.devtype = IMX21_CSPI,
|
||||
};
|
||||
|
||||
@ -830,6 +906,7 @@ static struct spi_imx_devtype_data imx27_cspi_devtype_data = {
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.has_slavemode = false,
|
||||
.devtype = IMX27_CSPI,
|
||||
};
|
||||
|
||||
@ -842,6 +919,7 @@ static struct spi_imx_devtype_data imx31_cspi_devtype_data = {
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.has_slavemode = false,
|
||||
.devtype = IMX31_CSPI,
|
||||
};
|
||||
|
||||
@ -855,6 +933,7 @@ static struct spi_imx_devtype_data imx35_cspi_devtype_data = {
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = true,
|
||||
.dynamic_burst = false,
|
||||
.has_slavemode = false,
|
||||
.devtype = IMX35_CSPI,
|
||||
};
|
||||
|
||||
@ -867,6 +946,8 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
|
||||
.fifo_size = 64,
|
||||
.has_dmamode = true,
|
||||
.dynamic_burst = true,
|
||||
.has_slavemode = true,
|
||||
.disable = mx51_ecspi_disable,
|
||||
.devtype = IMX51_ECSPI,
|
||||
};
|
||||
|
||||
@ -878,6 +959,8 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
|
||||
.reset = mx51_ecspi_reset,
|
||||
.fifo_size = 64,
|
||||
.has_dmamode = true,
|
||||
.has_slavemode = true,
|
||||
.disable = mx51_ecspi_disable,
|
||||
.devtype = IMX53_ECSPI,
|
||||
};
|
||||
|
||||
@ -945,14 +1028,16 @@ static void spi_imx_push(struct spi_imx_data *spi_imx)
|
||||
spi_imx->txfifo++;
|
||||
}
|
||||
|
||||
spi_imx->devtype_data->trigger(spi_imx);
|
||||
if (!spi_imx->slave_mode)
|
||||
spi_imx->devtype_data->trigger(spi_imx);
|
||||
}
|
||||
|
||||
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = dev_id;
|
||||
|
||||
while (spi_imx->devtype_data->rx_available(spi_imx)) {
|
||||
while (spi_imx->txfifo &&
|
||||
spi_imx->devtype_data->rx_available(spi_imx)) {
|
||||
spi_imx->rx(spi_imx);
|
||||
spi_imx->txfifo--;
|
||||
}
|
||||
@ -1034,7 +1119,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||
spi_imx->speed_hz = t->speed_hz;
|
||||
|
||||
/* Initialize the functions for transfer */
|
||||
if (spi_imx->devtype_data->dynamic_burst) {
|
||||
if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode) {
|
||||
u32 mask;
|
||||
|
||||
spi_imx->dynamic_burst = 0;
|
||||
@ -1078,6 +1163,12 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_imx53_ecspi(spi_imx) && spi_imx->slave_mode) {
|
||||
spi_imx->rx = mx53_ecspi_rx_slave;
|
||||
spi_imx->tx = mx53_ecspi_tx_slave;
|
||||
spi_imx->slave_burst = t->len;
|
||||
}
|
||||
|
||||
spi_imx->devtype_data->config(spi);
|
||||
|
||||
return 0;
|
||||
@ -1262,11 +1353,61 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
|
||||
return transfer->len;
|
||||
}
|
||||
|
||||
static int spi_imx_pio_transfer_slave(struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
int ret = transfer->len;
|
||||
|
||||
if (is_imx53_ecspi(spi_imx) &&
|
||||
transfer->len > MX53_MAX_TRANSFER_BYTES) {
|
||||
dev_err(&spi->dev, "Transaction too big, max size is %d bytes\n",
|
||||
MX53_MAX_TRANSFER_BYTES);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
spi_imx->tx_buf = transfer->tx_buf;
|
||||
spi_imx->rx_buf = transfer->rx_buf;
|
||||
spi_imx->count = transfer->len;
|
||||
spi_imx->txfifo = 0;
|
||||
|
||||
reinit_completion(&spi_imx->xfer_done);
|
||||
spi_imx->slave_aborted = false;
|
||||
|
||||
spi_imx_push(spi_imx);
|
||||
|
||||
spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE | MXC_INT_RDR);
|
||||
|
||||
if (wait_for_completion_interruptible(&spi_imx->xfer_done) ||
|
||||
spi_imx->slave_aborted) {
|
||||
dev_dbg(&spi->dev, "interrupted\n");
|
||||
ret = -EINTR;
|
||||
}
|
||||
|
||||
/* ecspi has a HW issue when works in Slave mode,
|
||||
* after 64 words writtern to TXFIFO, even TXFIFO becomes empty,
|
||||
* ECSPI_TXDATA keeps shift out the last word data,
|
||||
* so we have to disable ECSPI when in slave mode after the
|
||||
* transfer completes
|
||||
*/
|
||||
if (spi_imx->devtype_data->disable)
|
||||
spi_imx->devtype_data->disable(spi_imx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_imx_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
|
||||
/* flush rxfifo before transfer */
|
||||
while (spi_imx->devtype_data->rx_available(spi_imx))
|
||||
spi_imx->rx(spi_imx);
|
||||
|
||||
if (spi_imx->slave_mode)
|
||||
return spi_imx_pio_transfer_slave(spi, transfer);
|
||||
|
||||
if (spi_imx->usedma)
|
||||
return spi_imx_dma_transfer(spi_imx, transfer);
|
||||
else
|
||||
@ -1323,6 +1464,16 @@ spi_imx_unprepare_message(struct spi_master *master, struct spi_message *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_imx_slave_abort(struct spi_master *master)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
spi_imx->slave_aborted = true;
|
||||
complete(&spi_imx->xfer_done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
@ -1334,13 +1485,23 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
struct spi_imx_data *spi_imx;
|
||||
struct resource *res;
|
||||
int i, ret, irq, spi_drctl;
|
||||
const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data :
|
||||
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
|
||||
bool slave_mode;
|
||||
|
||||
if (!np && !mxc_platform_info) {
|
||||
dev_err(&pdev->dev, "can't get the platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
|
||||
slave_mode = devtype_data->has_slavemode &&
|
||||
of_property_read_bool(np, "spi-slave");
|
||||
if (slave_mode)
|
||||
master = spi_alloc_slave(&pdev->dev,
|
||||
sizeof(struct spi_imx_data));
|
||||
else
|
||||
master = spi_alloc_master(&pdev->dev,
|
||||
sizeof(struct spi_imx_data));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1358,20 +1519,29 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
spi_imx = spi_master_get_devdata(master);
|
||||
spi_imx->bitbang.master = master;
|
||||
spi_imx->dev = &pdev->dev;
|
||||
spi_imx->slave_mode = slave_mode;
|
||||
|
||||
spi_imx->devtype_data = of_id ? of_id->data :
|
||||
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
|
||||
spi_imx->devtype_data = devtype_data;
|
||||
|
||||
/* Get number of chip selects, either platform data or OF */
|
||||
if (mxc_platform_info) {
|
||||
master->num_chipselect = mxc_platform_info->num_chipselect;
|
||||
master->cs_gpios = devm_kzalloc(&master->dev,
|
||||
sizeof(int) * master->num_chipselect, GFP_KERNEL);
|
||||
if (!master->cs_gpios)
|
||||
return -ENOMEM;
|
||||
if (mxc_platform_info->chipselect) {
|
||||
master->cs_gpios = devm_kzalloc(&master->dev,
|
||||
sizeof(int) * master->num_chipselect, GFP_KERNEL);
|
||||
if (!master->cs_gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++)
|
||||
master->cs_gpios[i] = mxc_platform_info->chipselect[i];
|
||||
}
|
||||
for (i = 0; i < master->num_chipselect; i++)
|
||||
master->cs_gpios[i] = mxc_platform_info->chipselect[i];
|
||||
}
|
||||
} else {
|
||||
u32 num_cs;
|
||||
|
||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||
master->num_chipselect = num_cs;
|
||||
/* If not preset, default value of 1 is used */
|
||||
}
|
||||
|
||||
spi_imx->bitbang.chipselect = spi_imx_chipselect;
|
||||
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
|
||||
@ -1380,6 +1550,7 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
|
||||
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
|
||||
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
|
||||
spi_imx->bitbang.master->slave_abort = spi_imx_slave_abort;
|
||||
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
|
||||
| SPI_NO_CS;
|
||||
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx) ||
|
||||
@ -1451,37 +1622,38 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
spi_imx->devtype_data->intctrl(spi_imx, 0);
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
/* Request GPIO CS lines, if any */
|
||||
if (!spi_imx->slave_mode && master->cs_gpios) {
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
if (!gpio_is_valid(master->cs_gpios[i]))
|
||||
continue;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev,
|
||||
master->cs_gpios[i],
|
||||
DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't get CS GPIO %i\n",
|
||||
master->cs_gpios[i]);
|
||||
goto out_spi_bitbang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = spi_bitbang_start(&spi_imx->bitbang);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
|
||||
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;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i],
|
||||
DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't get CS GPIO %i\n",
|
||||
master->cs_gpios[i]);
|
||||
goto out_clk_put;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
clk_disable(spi_imx->clk_ipg);
|
||||
clk_disable(spi_imx->clk_per);
|
||||
return ret;
|
||||
|
||||
out_spi_bitbang:
|
||||
spi_bitbang_stop(&spi_imx->bitbang);
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
out_put_per:
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -442,6 +443,85 @@ static int mxs_spi_transfer_one(struct spi_master *master,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int mxs_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mxs_spi *spi = spi_master_get_devdata(master);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
int ret;
|
||||
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
|
||||
ret = pinctrl_pm_select_idle_state(dev);
|
||||
if (ret) {
|
||||
int ret2 = clk_prepare_enable(ssp->clk);
|
||||
|
||||
if (ret2)
|
||||
dev_warn(dev, "Failed to reenable clock after failing pinctrl request (pinctrl: %d, clk: %d)\n",
|
||||
ret, ret2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mxs_spi *spi = spi_master_get_devdata(master);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(ssp->clk);
|
||||
if (ret)
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused mxs_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_master_suspend(master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
return mxs_spi_runtime_suspend(dev);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mxs_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
ret = mxs_spi_runtime_resume(dev);
|
||||
else
|
||||
ret = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_master_resume(master);
|
||||
if (ret < 0 && !pm_runtime_suspended(dev))
|
||||
mxs_spi_runtime_suspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxs_spi_pm = {
|
||||
SET_RUNTIME_PM_OPS(mxs_spi_runtime_suspend,
|
||||
mxs_spi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mxs_spi_suspend, mxs_spi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id mxs_spi_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
|
||||
{ .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
|
||||
@ -493,12 +573,15 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
master->transfer_one_message = mxs_spi_transfer_one;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->num_chipselect = 3;
|
||||
master->dev.of_node = np;
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
spi = spi_master_get_devdata(master);
|
||||
ssp = &spi->ssp;
|
||||
@ -521,28 +604,41 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
||||
goto out_master_free;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(ssp->clk);
|
||||
if (ret)
|
||||
goto out_dma_release;
|
||||
pm_runtime_enable(ssp->dev);
|
||||
if (!pm_runtime_enabled(ssp->dev)) {
|
||||
ret = mxs_spi_runtime_resume(ssp->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ssp->dev, "runtime resume failed\n");
|
||||
goto out_dma_release;
|
||||
}
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(ssp->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ssp->dev, "runtime_get_sync failed\n");
|
||||
goto out_pm_runtime_disable;
|
||||
}
|
||||
|
||||
clk_set_rate(ssp->clk, clk_freq);
|
||||
|
||||
ret = stmp_reset_block(ssp->base);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
goto out_pm_runtime_put;
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
|
||||
goto out_disable_clk;
|
||||
goto out_pm_runtime_put;
|
||||
}
|
||||
|
||||
pm_runtime_put(ssp->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_clk:
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
out_pm_runtime_put:
|
||||
pm_runtime_put(ssp->dev);
|
||||
out_pm_runtime_disable:
|
||||
pm_runtime_disable(ssp->dev);
|
||||
out_dma_release:
|
||||
dma_release_channel(ssp->dmach);
|
||||
out_master_free:
|
||||
@ -560,7 +656,10 @@ static int mxs_spi_remove(struct platform_device *pdev)
|
||||
spi = spi_master_get_devdata(master);
|
||||
ssp = &spi->ssp;
|
||||
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
mxs_spi_runtime_suspend(&pdev->dev);
|
||||
|
||||
dma_release_channel(ssp->dmach);
|
||||
|
||||
return 0;
|
||||
@ -572,6 +671,7 @@ static struct platform_driver mxs_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = mxs_spi_dt_ids,
|
||||
.pm = &mxs_spi_pm,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -671,7 +671,6 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev,
|
||||
"%pOF has no valid 'reg' property (%d)\n",
|
||||
np, status);
|
||||
status = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1221,7 +1221,6 @@ static int rspi_probe(struct platform_device *pdev)
|
||||
struct spi_master *master;
|
||||
struct rspi_data *rspi;
|
||||
int ret;
|
||||
const struct of_device_id *of_id;
|
||||
const struct rspi_plat_data *rspi_pd;
|
||||
const struct spi_ops *ops;
|
||||
|
||||
@ -1229,9 +1228,8 @@ static int rspi_probe(struct platform_device *pdev)
|
||||
if (master == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(rspi_of_match, &pdev->dev);
|
||||
if (of_id) {
|
||||
ops = of_id->data;
|
||||
ops = of_device_get_match_data(&pdev->dev);
|
||||
if (ops) {
|
||||
ret = rspi_parse_dt(&pdev->dev, master);
|
||||
if (ret)
|
||||
goto error1;
|
||||
|
@ -752,7 +752,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct s3c64xx_spi_info *sci;
|
||||
int err;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
@ -788,8 +787,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
spi_set_ctldata(spi, cs);
|
||||
}
|
||||
|
||||
sci = sdd->cntrlr_info;
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
/* Check if we can provide the requested rate */
|
||||
|
@ -900,7 +900,7 @@ static int sh_msiof_transfer_one(struct spi_master *master,
|
||||
break;
|
||||
copy32 = copy_bswap32;
|
||||
} else if (bits <= 16) {
|
||||
if (l & 1)
|
||||
if (l & 3)
|
||||
break;
|
||||
copy32 = copy_wswap32;
|
||||
} else {
|
||||
@ -1021,6 +1021,8 @@ static const struct sh_msiof_chipdata rcar_gen3_data = {
|
||||
|
||||
static const struct of_device_id sh_msiof_match[] = {
|
||||
{ .compatible = "renesas,sh-mobile-msiof", .data = &sh_data },
|
||||
{ .compatible = "renesas,msiof-r8a7743", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7745", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7790", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7791", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7792", .data = &rcar_gen2_data },
|
||||
@ -1188,12 +1190,10 @@ free_tx_chan:
|
||||
static void sh_msiof_release_dma(struct sh_msiof_spi_priv *p)
|
||||
{
|
||||
struct spi_master *master = p->master;
|
||||
struct device *dev;
|
||||
|
||||
if (!master->dma_tx)
|
||||
return;
|
||||
|
||||
dev = &p->pdev->dev;
|
||||
dma_unmap_single(master->dma_rx->device->dev, p->rx_dma_addr,
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(master->dma_tx->device->dev, p->tx_dma_addr,
|
||||
@ -1209,15 +1209,13 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||
struct resource *r;
|
||||
struct spi_master *master;
|
||||
const struct sh_msiof_chipdata *chipdata;
|
||||
const struct of_device_id *of_id;
|
||||
struct sh_msiof_spi_info *info;
|
||||
struct sh_msiof_spi_priv *p;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
||||
if (of_id) {
|
||||
chipdata = of_id->data;
|
||||
chipdata = of_device_get_match_data(&pdev->dev);
|
||||
if (chipdata) {
|
||||
info = sh_msiof_spi_parse_dt(&pdev->dev);
|
||||
} else {
|
||||
chipdata = (const void *)pdev->id_entry->driver_data;
|
||||
|
418
drivers/spi/spi-sprd-adi.c
Normal file
418
drivers/spi/spi-sprd-adi.c
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Spreadtrum Communications Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <linux/hwspinlock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
/* Registers definitions for ADI controller */
|
||||
#define REG_ADI_CTRL0 0x4
|
||||
#define REG_ADI_CHN_PRIL 0x8
|
||||
#define REG_ADI_CHN_PRIH 0xc
|
||||
#define REG_ADI_INT_EN 0x10
|
||||
#define REG_ADI_INT_RAW 0x14
|
||||
#define REG_ADI_INT_MASK 0x18
|
||||
#define REG_ADI_INT_CLR 0x1c
|
||||
#define REG_ADI_GSSI_CFG0 0x20
|
||||
#define REG_ADI_GSSI_CFG1 0x24
|
||||
#define REG_ADI_RD_CMD 0x28
|
||||
#define REG_ADI_RD_DATA 0x2c
|
||||
#define REG_ADI_ARM_FIFO_STS 0x30
|
||||
#define REG_ADI_STS 0x34
|
||||
#define REG_ADI_EVT_FIFO_STS 0x38
|
||||
#define REG_ADI_ARM_CMD_STS 0x3c
|
||||
#define REG_ADI_CHN_EN 0x40
|
||||
#define REG_ADI_CHN_ADDR(id) (0x44 + (id - 2) * 4)
|
||||
#define REG_ADI_CHN_EN1 0x20c
|
||||
|
||||
/* Bits definitions for register REG_ADI_GSSI_CFG0 */
|
||||
#define BIT_CLK_ALL_ON BIT(30)
|
||||
|
||||
/* Bits definitions for register REG_ADI_RD_DATA */
|
||||
#define BIT_RD_CMD_BUSY BIT(31)
|
||||
#define RD_ADDR_SHIFT 16
|
||||
#define RD_VALUE_MASK GENMASK(15, 0)
|
||||
#define RD_ADDR_MASK GENMASK(30, 16)
|
||||
|
||||
/* Bits definitions for register REG_ADI_ARM_FIFO_STS */
|
||||
#define BIT_FIFO_FULL BIT(11)
|
||||
#define BIT_FIFO_EMPTY BIT(10)
|
||||
|
||||
/*
|
||||
* ADI slave devices include RTC, ADC, regulator, charger, thermal and so on.
|
||||
* The slave devices address offset is always 0x8000 and size is 4K.
|
||||
*/
|
||||
#define ADI_SLAVE_ADDR_SIZE SZ_4K
|
||||
#define ADI_SLAVE_OFFSET 0x8000
|
||||
|
||||
/* Timeout (ms) for the trylock of hardware spinlocks */
|
||||
#define ADI_HWSPINLOCK_TIMEOUT 5000
|
||||
/*
|
||||
* ADI controller has 50 channels including 2 software channels
|
||||
* and 48 hardware channels.
|
||||
*/
|
||||
#define ADI_HW_CHNS 50
|
||||
|
||||
#define ADI_FIFO_DRAIN_TIMEOUT 1000
|
||||
#define ADI_READ_TIMEOUT 2000
|
||||
#define REG_ADDR_LOW_MASK GENMASK(11, 0)
|
||||
|
||||
struct sprd_adi {
|
||||
struct spi_controller *ctlr;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct hwspinlock *hwlock;
|
||||
unsigned long slave_vbase;
|
||||
unsigned long slave_pbase;
|
||||
};
|
||||
|
||||
static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr)
|
||||
{
|
||||
if (paddr < sadi->slave_pbase || paddr >
|
||||
(sadi->slave_pbase + ADI_SLAVE_ADDR_SIZE)) {
|
||||
dev_err(sadi->dev,
|
||||
"slave physical address is incorrect, addr = 0x%x\n",
|
||||
paddr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long sprd_adi_to_vaddr(struct sprd_adi *sadi, u32 paddr)
|
||||
{
|
||||
return (paddr - sadi->slave_pbase + sadi->slave_vbase);
|
||||
}
|
||||
|
||||
static int sprd_adi_drain_fifo(struct sprd_adi *sadi)
|
||||
{
|
||||
u32 timeout = ADI_FIFO_DRAIN_TIMEOUT;
|
||||
u32 sts;
|
||||
|
||||
do {
|
||||
sts = readl_relaxed(sadi->base + REG_ADI_ARM_FIFO_STS);
|
||||
if (sts & BIT_FIFO_EMPTY)
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
} while (--timeout);
|
||||
|
||||
if (timeout == 0) {
|
||||
dev_err(sadi->dev, "drain write fifo timeout\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_adi_fifo_is_full(struct sprd_adi *sadi)
|
||||
{
|
||||
return readl_relaxed(sadi->base + REG_ADI_ARM_FIFO_STS) & BIT_FIFO_FULL;
|
||||
}
|
||||
|
||||
static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
||||
{
|
||||
int read_timeout = ADI_READ_TIMEOUT;
|
||||
u32 val, rd_addr;
|
||||
|
||||
/*
|
||||
* Set the physical register address need to read into RD_CMD register,
|
||||
* then ADI controller will start to transfer automatically.
|
||||
*/
|
||||
writel_relaxed(reg_paddr, sadi->base + REG_ADI_RD_CMD);
|
||||
|
||||
/*
|
||||
* Wait read operation complete, the BIT_RD_CMD_BUSY will be set
|
||||
* simultaneously when writing read command to register, and the
|
||||
* BIT_RD_CMD_BUSY will be cleared after the read operation is
|
||||
* completed.
|
||||
*/
|
||||
do {
|
||||
val = readl_relaxed(sadi->base + REG_ADI_RD_DATA);
|
||||
if (!(val & BIT_RD_CMD_BUSY))
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
} while (--read_timeout);
|
||||
|
||||
if (read_timeout == 0) {
|
||||
dev_err(sadi->dev, "ADI read timeout\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* The return value includes data and read register address, from bit 0
|
||||
* to bit 15 are data, and from bit 16 to bit 30 are read register
|
||||
* address. Then we can check the returned register address to validate
|
||||
* data.
|
||||
*/
|
||||
rd_addr = (val & RD_ADDR_MASK ) >> RD_ADDR_SHIFT;
|
||||
|
||||
if (rd_addr != (reg_paddr & REG_ADDR_LOW_MASK)) {
|
||||
dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n",
|
||||
reg_paddr, val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*read_val = val & RD_VALUE_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_adi_write(struct sprd_adi *sadi, unsigned long reg, u32 val)
|
||||
{
|
||||
u32 timeout = ADI_FIFO_DRAIN_TIMEOUT;
|
||||
int ret;
|
||||
|
||||
ret = sprd_adi_drain_fifo(sadi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* we should wait for write fifo is empty before writing data to PMIC
|
||||
* registers.
|
||||
*/
|
||||
do {
|
||||
if (!sprd_adi_fifo_is_full(sadi)) {
|
||||
writel_relaxed(val, (void __iomem *)reg);
|
||||
break;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
} while (--timeout);
|
||||
|
||||
if (timeout == 0) {
|
||||
dev_err(sadi->dev, "write fifo is full\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_adi_transfer_one(struct spi_controller *ctlr,
|
||||
struct spi_device *spi_dev,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct sprd_adi *sadi = spi_controller_get_devdata(ctlr);
|
||||
unsigned long flags, virt_reg;
|
||||
u32 phy_reg, val;
|
||||
int ret;
|
||||
|
||||
if (t->rx_buf) {
|
||||
phy_reg = *(u32 *)t->rx_buf + sadi->slave_pbase;
|
||||
|
||||
ret = sprd_adi_check_paddr(sadi, phy_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
|
||||
ADI_HWSPINLOCK_TIMEOUT,
|
||||
&flags);
|
||||
if (ret) {
|
||||
dev_err(sadi->dev, "get the hw lock failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sprd_adi_read(sadi, phy_reg, &val);
|
||||
hwspin_unlock_irqrestore(sadi->hwlock, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(u32 *)t->rx_buf = val;
|
||||
} else if (t->tx_buf) {
|
||||
u32 *p = (u32 *)t->tx_buf;
|
||||
|
||||
/*
|
||||
* Get the physical register address need to write and convert
|
||||
* the physical address to virtual address. Since we need
|
||||
* virtual register address to write.
|
||||
*/
|
||||
phy_reg = *p++ + sadi->slave_pbase;
|
||||
ret = sprd_adi_check_paddr(sadi, phy_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
virt_reg = sprd_adi_to_vaddr(sadi, phy_reg);
|
||||
val = *p;
|
||||
|
||||
ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
|
||||
ADI_HWSPINLOCK_TIMEOUT,
|
||||
&flags);
|
||||
if (ret) {
|
||||
dev_err(sadi->dev, "get the hw lock failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sprd_adi_write(sadi, virt_reg, val);
|
||||
hwspin_unlock_irqrestore(sadi->hwlock, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(sadi->dev, "no buffer for transfer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_adi_hw_init(struct sprd_adi *sadi)
|
||||
{
|
||||
struct device_node *np = sadi->dev->of_node;
|
||||
int i, size, chn_cnt;
|
||||
const __be32 *list;
|
||||
u32 tmp;
|
||||
|
||||
/* Address bits select default 12 bits */
|
||||
writel_relaxed(0, sadi->base + REG_ADI_CTRL0);
|
||||
|
||||
/* Set all channels as default priority */
|
||||
writel_relaxed(0, sadi->base + REG_ADI_CHN_PRIL);
|
||||
writel_relaxed(0, sadi->base + REG_ADI_CHN_PRIH);
|
||||
|
||||
/* Set clock auto gate mode */
|
||||
tmp = readl_relaxed(sadi->base + REG_ADI_GSSI_CFG0);
|
||||
tmp &= ~BIT_CLK_ALL_ON;
|
||||
writel_relaxed(tmp, sadi->base + REG_ADI_GSSI_CFG0);
|
||||
|
||||
/* Set hardware channels setting */
|
||||
list = of_get_property(np, "sprd,hw-channels", &size);
|
||||
if (!list || !size) {
|
||||
dev_info(sadi->dev, "no hw channels setting in node\n");
|
||||
return;
|
||||
}
|
||||
|
||||
chn_cnt = size / 8;
|
||||
for (i = 0; i < chn_cnt; i++) {
|
||||
u32 value;
|
||||
u32 chn_id = be32_to_cpu(*list++);
|
||||
u32 chn_config = be32_to_cpu(*list++);
|
||||
|
||||
/* Channel 0 and 1 are software channels */
|
||||
if (chn_id < 2)
|
||||
continue;
|
||||
|
||||
writel_relaxed(chn_config, sadi->base +
|
||||
REG_ADI_CHN_ADDR(chn_id));
|
||||
|
||||
if (chn_id < 32) {
|
||||
value = readl_relaxed(sadi->base + REG_ADI_CHN_EN);
|
||||
value |= BIT(chn_id);
|
||||
writel_relaxed(value, sadi->base + REG_ADI_CHN_EN);
|
||||
} else if (chn_id < ADI_HW_CHNS) {
|
||||
value = readl_relaxed(sadi->base + REG_ADI_CHN_EN1);
|
||||
value |= BIT(chn_id - 32);
|
||||
writel_relaxed(value, sadi->base + REG_ADI_CHN_EN1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sprd_adi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spi_controller *ctlr;
|
||||
struct sprd_adi *sadi;
|
||||
struct resource *res;
|
||||
u32 num_chipselect;
|
||||
int ret;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "can not find the adi bus node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pdev->id = of_alias_get_id(np, "spi");
|
||||
num_chipselect = of_get_child_count(np);
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(struct sprd_adi));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, ctlr);
|
||||
sadi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sadi->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(sadi->base)) {
|
||||
ret = PTR_ERR(sadi->base);
|
||||
goto put_ctlr;
|
||||
}
|
||||
|
||||
sadi->slave_vbase = (unsigned long)sadi->base + ADI_SLAVE_OFFSET;
|
||||
sadi->slave_pbase = res->start + ADI_SLAVE_OFFSET;
|
||||
sadi->ctlr = ctlr;
|
||||
sadi->dev = &pdev->dev;
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can not get the hardware spinlock\n");
|
||||
goto put_ctlr;
|
||||
}
|
||||
|
||||
sadi->hwlock = hwspin_lock_request_specific(ret);
|
||||
if (!sadi->hwlock) {
|
||||
ret = -ENXIO;
|
||||
goto put_ctlr;
|
||||
}
|
||||
|
||||
sprd_adi_hw_init(sadi);
|
||||
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->bus_num = pdev->id;
|
||||
ctlr->num_chipselect = num_chipselect;
|
||||
ctlr->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
ctlr->bits_per_word_mask = 0;
|
||||
ctlr->transfer_one = sprd_adi_transfer_one;
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register SPI controller\n");
|
||||
goto free_hwlock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_hwlock:
|
||||
hwspin_lock_free(sadi->hwlock);
|
||||
put_ctlr:
|
||||
spi_controller_put(ctlr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_adi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev);
|
||||
struct sprd_adi *sadi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
hwspin_lock_free(sadi->hwlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_adi_of_match[] = {
|
||||
{
|
||||
.compatible = "sprd,sc9860-adi",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_adi_of_match);
|
||||
|
||||
static struct platform_driver sprd_adi_driver = {
|
||||
.driver = {
|
||||
.name = "sprd-adi",
|
||||
.of_match_table = sprd_adi_of_match,
|
||||
},
|
||||
.probe = sprd_adi_probe,
|
||||
.remove = sprd_adi_remove,
|
||||
};
|
||||
module_platform_driver(sprd_adi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Spreadtrum ADI Controller Driver");
|
||||
MODULE_AUTHOR("Baolin Wang <Baolin.Wang@spreadtrum.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -50,7 +50,7 @@
|
||||
#define SPI_IDLE_SDA_PULL_LOW (2 << 18)
|
||||
#define SPI_IDLE_SDA_PULL_HIGH (3 << 18)
|
||||
#define SPI_IDLE_SDA_MASK (3 << 18)
|
||||
#define SPI_CS_SS_VAL (1 << 20)
|
||||
#define SPI_CS_SW_VAL (1 << 20)
|
||||
#define SPI_CS_SW_HW (1 << 21)
|
||||
/* SPI_CS_POL_INACTIVE bits are default high */
|
||||
/* n from 0 to 3 */
|
||||
@ -705,9 +705,9 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
|
||||
|
||||
command1 |= SPI_CS_SW_HW;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
command1 |= SPI_CS_SS_VAL;
|
||||
command1 |= SPI_CS_SW_VAL;
|
||||
else
|
||||
command1 &= ~SPI_CS_SS_VAL;
|
||||
command1 &= ~SPI_CS_SW_VAL;
|
||||
|
||||
tegra_spi_writel(tspi, 0, SPI_COMMAND2);
|
||||
} else {
|
||||
|
@ -2200,7 +2200,7 @@ static void devm_spi_unregister(struct device *dev, void *res)
|
||||
* Context: can sleep
|
||||
*
|
||||
* Register a SPI device as with spi_register_controller() which will
|
||||
* automatically be unregister
|
||||
* automatically be unregistered and freed.
|
||||
*
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
@ -2241,15 +2241,18 @@ static int __unregister(struct device *dev, void *null)
|
||||
* only ones directly touching chip registers.
|
||||
*
|
||||
* This must be called from context that can sleep.
|
||||
*
|
||||
* Note that this function also drops a reference to the controller.
|
||||
*/
|
||||
void spi_unregister_controller(struct spi_controller *ctlr)
|
||||
{
|
||||
struct spi_controller *found;
|
||||
int id = ctlr->bus_num;
|
||||
int dummy;
|
||||
|
||||
/* First make sure that this controller was ever added */
|
||||
mutex_lock(&board_lock);
|
||||
found = idr_find(&spi_master_idr, ctlr->bus_num);
|
||||
found = idr_find(&spi_master_idr, id);
|
||||
mutex_unlock(&board_lock);
|
||||
if (found != ctlr) {
|
||||
dev_dbg(&ctlr->dev,
|
||||
@ -2269,7 +2272,7 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
||||
device_unregister(&ctlr->dev);
|
||||
/* free bus id */
|
||||
mutex_lock(&board_lock);
|
||||
idr_remove(&spi_master_idr, ctlr->bus_num);
|
||||
idr_remove(&spi_master_idr, id);
|
||||
mutex_unlock(&board_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_unregister_controller);
|
||||
|
31
include/linux/spi/spi-fsl-dspi.h
Normal file
31
include/linux/spi/spi-fsl-dspi.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Freescale DSPI controller driver
|
||||
*
|
||||
* Copyright (c) 2017 Angelo Dureghello <angelo@sysam.it>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SPI_FSL_DSPI_HEADER_H
|
||||
#define SPI_FSL_DSPI_HEADER_H
|
||||
|
||||
/**
|
||||
* struct fsl_dspi_platform_data - platform data for the Freescale DSPI driver
|
||||
* @bus_num: board specific identifier for this DSPI driver.
|
||||
* @cs_num: number of chip selects supported by this DSPI driver.
|
||||
*/
|
||||
struct fsl_dspi_platform_data {
|
||||
u32 cs_num;
|
||||
u32 bus_num;
|
||||
u32 sck_cs_delay;
|
||||
u32 cs_sck_delay;
|
||||
};
|
||||
|
||||
#endif /* SPI_FSL_DSPI_HEADER_H */
|
Loading…
Reference in New Issue
Block a user