mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 21:02:19 +00:00
spi: Updates for v4.14
A fairly quiet release for the SPI subsystem: - Move to using IDR for allocating bus numbers. - Modernisation of the ep93xx driver, removing a lot of open coding and using the framework more. - The tools have been moved to use the standard tools build system and an install target added (there will be a fairly trivial conflict with tip resulting from the changes in the main tools Makefile). - A refactoring of the Qualcomm QUP driver which enables new variants to be supported. - Explicit support for the Freescale i.MX53 and i.MX6 SPI, Renesas R-Car H3 and Rockchip RV1108 controllers. There's also a trivial add/add conflict in spi.c with the ACPI tree adding a header for some Apple support and the IDR code needing a header too. -----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAlmtgGYTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0IwAB/9U/s75RgZN+w/IZneex3gD2+mnNxTV SiWWlR66580vbXsh7lgzTtANrDKfYTsYGP3JS7vjhsegBlzcT4M5OMyJZTcCi40a JF0edDFnkqNM96LubJVnrIJj4KHL1Zx+FICIXItOSBZh6ECgI92KAfAbfQhLdB10 BtNdlsQMzcTygLqf2/N6W7u/6yYBnUuaaNeu+j4QsF1pE9L/5kt3lLXxv2DTL3cA vncqu/ndxIjCKLpIGjMOrvAlLCKmZdIxX3Y/d1XJSxyG3vJj30kEvxATZ15sKln8 gXOnmWF2JgBcxEHxzsTGuVZpXXblu7uiicniglP0geC9dMboJnky993j =tbQ9 -----END PGP SIGNATURE----- Merge tag 'spi-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "A fairly quiet release for the SPI subsystem: - Move to using IDR for allocating bus numbers - Modernisation of the ep93xx driver, removing a lot of open coding and using the framework more - The tools have been moved to use the standard tools build system and an install target added (there will be a fairly trivial conflict with tip resulting from the changes in the main tools Makefile) - A refactoring of the Qualcomm QUP driver which enables new variants to be supported - Explicit support for the Freescale i.MX53 and i.MX6 SPI, Renesas R-Car H3 and Rockchip RV1108 controllers" * tag 'spi-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (71 commits) spi: spi-falcon: drop check of boot select spi: imx: fix use of native chip-selects with devicetree spi: pl022: constify amba_id spi: imx: fix little-endian build spi: omap: Allocate bus number from spi framework spi: Kernel coding style fixes spi: imx: dynamic burst length adjust for PIO mode spi: Pick spi bus number from Linux idr or spi alias spi: rockchip: configure CTRLR1 according to size and data frame spi: altera: Consolidate TX/RX data register access spi: altera: Switch to SPI core transfer queue management spi: rockchip: add compatible string for rv1108 spi spi: qup: fix 64-bit build warning spi: qup: hide warning for uninitialized variable spi: spi-ep93xx: use the default master transfer queueing mechanism spi: spi-ep93xx: remove private data 'current_msg' spi: spi-ep93xx: pass the spi_master pointer around spi: spi-ep93xx: absorb the interrupt enable/disable helpers spi: spi-ep93xx: add spi master prepare_transfer_hardware() spi: spi-ep93xx: use 32-bit read/write for all registers ...
This commit is contained in:
commit
b88f55774f
@ -9,6 +9,7 @@ Required properties:
|
||||
- "fsl,imx31-cspi" for SPI compatible with the one integrated on i.MX31
|
||||
- "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35
|
||||
- "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51
|
||||
- "fsl,imx53-ecspi" for SPI compatible with the one integrated on i.MX53 and later Soc
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
||||
|
@ -6,6 +6,7 @@ Required properties:
|
||||
"renesas,msiof-r8a7792" (R-Car V2H)
|
||||
"renesas,msiof-r8a7793" (R-Car M2-N)
|
||||
"renesas,msiof-r8a7794" (R-Car E2)
|
||||
"renesas,msiof-r8a7795" (R-Car H3)
|
||||
"renesas,msiof-r8a7796" (R-Car M3-W)
|
||||
"renesas,msiof-sh73a0" (SH-Mobile AG5)
|
||||
"renesas,sh-mobile-msiof" (generic SH-Mobile compatibile device)
|
||||
|
@ -6,6 +6,7 @@ and display controllers using the SPI communication interface.
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
"rockchip,rv1108-spi" for rv1108 SoCs.
|
||||
"rockchip,rk3036-spi" for rk3036 SoCS.
|
||||
"rockchip,rk3066-spi" for rk3066 SoCs.
|
||||
"rockchip,rk3188-spi" for rk3188 SoCs.
|
||||
|
@ -55,7 +55,6 @@ comment "SPI Master Controller Drivers"
|
||||
|
||||
config SPI_ALTERA
|
||||
tristate "Altera SPI Controller"
|
||||
select SPI_BITBANG
|
||||
help
|
||||
This is the driver for the Altera SPI Controller.
|
||||
|
||||
@ -518,8 +517,8 @@ config SPI_PPC4xx
|
||||
|
||||
config SPI_PXA2XX
|
||||
tristate "PXA2xx SSP SPI master"
|
||||
depends on (ARCH_PXA || PCI || ACPI)
|
||||
select PXA_SSP if ARCH_PXA
|
||||
depends on (ARCH_PXA || ARCH_MMP || PCI || ACPI)
|
||||
select PXA_SSP if ARCH_PXA || ARCH_MMP
|
||||
help
|
||||
This enables using a PXA2xx or Sodaville SSP port as a SPI master
|
||||
controller. The driver can be configured to use any SSP port and
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
@ -45,10 +44,6 @@
|
||||
#define ALTERA_SPI_CONTROL_SSO_MSK 0x400
|
||||
|
||||
struct altera_spi {
|
||||
/* bitbang has to be first */
|
||||
struct spi_bitbang bitbang;
|
||||
struct completion done;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
int len;
|
||||
@ -66,112 +61,42 @@ static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
|
||||
return spi_master_get_devdata(sdev->master);
|
||||
}
|
||||
|
||||
static void altera_spi_chipsel(struct spi_device *spi, int value)
|
||||
static void altera_spi_set_cs(struct spi_device *spi, bool is_high)
|
||||
{
|
||||
struct altera_spi *hw = altera_spi_to_hw(spi);
|
||||
|
||||
if (spi->mode & SPI_CS_HIGH) {
|
||||
switch (value) {
|
||||
case BITBANG_CS_INACTIVE:
|
||||
writel(1 << spi->chip_select,
|
||||
hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
break;
|
||||
|
||||
case BITBANG_CS_ACTIVE:
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
break;
|
||||
}
|
||||
if (is_high) {
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
} else {
|
||||
switch (value) {
|
||||
case BITBANG_CS_INACTIVE:
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
break;
|
||||
|
||||
case BITBANG_CS_ACTIVE:
|
||||
writel(1 << spi->chip_select,
|
||||
hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
break;
|
||||
}
|
||||
writel(BIT(spi->chip_select), hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
|
||||
static void altera_spi_tx_word(struct altera_spi *hw)
|
||||
{
|
||||
unsigned int txd = 0;
|
||||
|
||||
if (hw->tx) {
|
||||
switch (hw->bytes_per_word) {
|
||||
case 1:
|
||||
return hw->tx[count];
|
||||
txd = hw->tx[hw->count];
|
||||
break;
|
||||
case 2:
|
||||
return (hw->tx[count * 2]
|
||||
| (hw->tx[count * 2 + 1] << 8));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct altera_spi *hw = altera_spi_to_hw(spi);
|
||||
|
||||
hw->tx = t->tx_buf;
|
||||
hw->rx = t->rx_buf;
|
||||
hw->count = 0;
|
||||
hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
|
||||
hw->len = t->len / hw->bytes_per_word;
|
||||
|
||||
if (hw->irq >= 0) {
|
||||
/* enable receive interrupt */
|
||||
hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
|
||||
/* send the first byte */
|
||||
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
|
||||
|
||||
wait_for_completion(&hw->done);
|
||||
/* disable receive interrupt */
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
} else {
|
||||
while (hw->count < hw->len) {
|
||||
unsigned int rxd;
|
||||
|
||||
writel(hw_txbyte(hw, hw->count),
|
||||
hw->base + ALTERA_SPI_TXDATA);
|
||||
|
||||
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
|
||||
ALTERA_SPI_STATUS_RRDY_MSK))
|
||||
cpu_relax();
|
||||
|
||||
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
|
||||
if (hw->rx) {
|
||||
switch (hw->bytes_per_word) {
|
||||
case 1:
|
||||
hw->rx[hw->count] = rxd;
|
||||
break;
|
||||
case 2:
|
||||
hw->rx[hw->count * 2] = rxd;
|
||||
hw->rx[hw->count * 2 + 1] = rxd >> 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hw->count++;
|
||||
txd = (hw->tx[hw->count * 2]
|
||||
| (hw->tx[hw->count * 2 + 1] << 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hw->count * hw->bytes_per_word;
|
||||
writel(txd, hw->base + ALTERA_SPI_TXDATA);
|
||||
}
|
||||
|
||||
static irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||
static void altera_spi_rx_word(struct altera_spi *hw)
|
||||
{
|
||||
struct altera_spi *hw = dev;
|
||||
unsigned int rxd;
|
||||
|
||||
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
|
||||
@ -188,11 +113,58 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||
}
|
||||
|
||||
hw->count++;
|
||||
}
|
||||
|
||||
if (hw->count < hw->len)
|
||||
writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA);
|
||||
else
|
||||
complete(&hw->done);
|
||||
static int altera_spi_txrx(struct spi_master *master,
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct altera_spi *hw = spi_master_get_devdata(master);
|
||||
|
||||
hw->tx = t->tx_buf;
|
||||
hw->rx = t->rx_buf;
|
||||
hw->count = 0;
|
||||
hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
|
||||
hw->len = t->len / hw->bytes_per_word;
|
||||
|
||||
if (hw->irq >= 0) {
|
||||
/* enable receive interrupt */
|
||||
hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
|
||||
/* send the first byte */
|
||||
altera_spi_tx_word(hw);
|
||||
} else {
|
||||
while (hw->count < hw->len) {
|
||||
altera_spi_tx_word(hw);
|
||||
|
||||
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
|
||||
ALTERA_SPI_STATUS_RRDY_MSK))
|
||||
cpu_relax();
|
||||
|
||||
altera_spi_rx_word(hw);
|
||||
}
|
||||
spi_finalize_current_transfer(master);
|
||||
}
|
||||
|
||||
return t->len;
|
||||
}
|
||||
|
||||
static irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||
{
|
||||
struct spi_master *master = dev;
|
||||
struct altera_spi *hw = spi_master_get_devdata(master);
|
||||
|
||||
altera_spi_rx_word(hw);
|
||||
|
||||
if (hw->count < hw->len) {
|
||||
altera_spi_tx_word(hw);
|
||||
} else {
|
||||
/* disable receive interrupt */
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -214,14 +186,10 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->transfer_one = altera_spi_txrx;
|
||||
master->set_cs = altera_spi_set_cs;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
platform_set_drvdata(pdev, hw);
|
||||
|
||||
/* setup the state for the bitbang driver */
|
||||
hw->bitbang.master = master;
|
||||
hw->bitbang.chipselect = altera_spi_chipsel;
|
||||
hw->bitbang.txrx_bufs = altera_spi_txrx;
|
||||
|
||||
/* find and map our resources */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -239,15 +207,13 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
init_completion(&hw->done);
|
||||
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
|
||||
pdev->name, hw);
|
||||
pdev->name, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* register our spi controller */
|
||||
err = spi_bitbang_start(&hw->bitbang);
|
||||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
|
||||
@ -258,16 +224,6 @@ exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int altera_spi_remove(struct platform_device *dev)
|
||||
{
|
||||
struct altera_spi *hw = platform_get_drvdata(dev);
|
||||
struct spi_master *master = hw->bitbang.master;
|
||||
|
||||
spi_bitbang_stop(&hw->bitbang);
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id altera_spi_match[] = {
|
||||
{ .compatible = "ALTR,spi-1.0", },
|
||||
@ -279,7 +235,6 @@ MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
.probe = altera_spi_probe,
|
||||
.remove = altera_spi_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = NULL,
|
||||
|
@ -39,15 +39,15 @@ struct ath79_spi {
|
||||
u32 reg_ctrl;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
unsigned rrw_delay;
|
||||
unsigned int rrw_delay;
|
||||
};
|
||||
|
||||
static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg)
|
||||
static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned int reg)
|
||||
{
|
||||
return ioread32(sp->base + reg);
|
||||
}
|
||||
|
||||
static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned reg, u32 val)
|
||||
static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned int reg, u32 val)
|
||||
{
|
||||
iowrite32(val, sp->base + reg);
|
||||
}
|
||||
@ -57,7 +57,7 @@ static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi)
|
||||
return spi_master_get_devdata(spi->master);
|
||||
}
|
||||
|
||||
static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned nsecs)
|
||||
static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned int nsecs)
|
||||
{
|
||||
if (nsecs > sp->rrw_delay)
|
||||
ndelay(nsecs - sp->rrw_delay);
|
||||
@ -148,9 +148,8 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
|
||||
|
||||
static void ath79_spi_cleanup_cs(struct spi_device *spi)
|
||||
{
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_free(spi->cs_gpio);
|
||||
}
|
||||
}
|
||||
|
||||
static int ath79_spi_setup(struct spi_device *spi)
|
||||
@ -176,7 +175,7 @@ static void ath79_spi_cleanup(struct spi_device *spi)
|
||||
spi_bitbang_cleanup(spi);
|
||||
}
|
||||
|
||||
static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs,
|
||||
static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs,
|
||||
u32 word, u8 bits)
|
||||
{
|
||||
struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -349,76 +348,60 @@ static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte,
|
||||
bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, flex_mode);
|
||||
}
|
||||
|
||||
static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,
|
||||
int addrlen, int hp)
|
||||
static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
|
||||
struct spi_flash_read_message *msg,
|
||||
int hp)
|
||||
{
|
||||
int bpc = 0, bpp = 0;
|
||||
u8 command = SPINOR_OP_READ_FAST;
|
||||
int flex_mode = 1, rv = 0;
|
||||
bool spans_4byte = false;
|
||||
u8 command = msg->read_opcode;
|
||||
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
|
||||
int addrlen = msg->addr_width;
|
||||
int addr_nbits = msg->addr_nbits ? msg->addr_nbits : SPI_NBITS_SINGLE;
|
||||
int flex_mode = 1;
|
||||
|
||||
dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n",
|
||||
width, addrlen, hp);
|
||||
|
||||
if (addrlen == BSPI_ADDRLEN_4BYTES) {
|
||||
if (addrlen == BSPI_ADDRLEN_4BYTES)
|
||||
bpp = BSPI_BPP_ADDR_SELECT_MASK;
|
||||
spans_4byte = true;
|
||||
}
|
||||
|
||||
bpp |= 8;
|
||||
bpp |= msg->dummy_bytes * (8/addr_nbits);
|
||||
|
||||
switch (width) {
|
||||
case SPI_NBITS_SINGLE:
|
||||
if (addrlen == BSPI_ADDRLEN_3BYTES)
|
||||
/* default mode, does not need flex_cmd */
|
||||
flex_mode = 0;
|
||||
else
|
||||
command = SPINOR_OP_READ_FAST_4B;
|
||||
break;
|
||||
case SPI_NBITS_DUAL:
|
||||
bpc = 0x00000001;
|
||||
if (hp) {
|
||||
bpc |= 0x00010100; /* address and mode are 2-bit */
|
||||
bpp = BSPI_BPP_MODE_SELECT_MASK;
|
||||
command = OPCODE_DIOR;
|
||||
if (spans_4byte)
|
||||
command = OPCODE_DIOR_4B;
|
||||
} else {
|
||||
command = SPINOR_OP_READ_1_1_2;
|
||||
if (spans_4byte)
|
||||
command = SPINOR_OP_READ_1_1_2_4B;
|
||||
}
|
||||
break;
|
||||
case SPI_NBITS_QUAD:
|
||||
bpc = 0x00000002;
|
||||
if (hp) {
|
||||
bpc |= 0x00020200; /* address and mode are 4-bit */
|
||||
bpp = 4; /* dummy cycles */
|
||||
bpp |= BSPI_BPP_ADDR_SELECT_MASK;
|
||||
command = OPCODE_QIOR;
|
||||
if (spans_4byte)
|
||||
command = OPCODE_QIOR_4B;
|
||||
} else {
|
||||
command = SPINOR_OP_READ_1_1_4;
|
||||
if (spans_4byte)
|
||||
command = SPINOR_OP_READ_1_1_4_4B;
|
||||
bpp |= BSPI_BPP_MODE_SELECT_MASK;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rv == 0)
|
||||
bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc,
|
||||
flex_mode);
|
||||
bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc, flex_mode);
|
||||
|
||||
return rv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width,
|
||||
int addrlen, int hp)
|
||||
static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
|
||||
struct spi_flash_read_message *msg,
|
||||
int hp)
|
||||
{
|
||||
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
|
||||
int addrlen = msg->addr_width;
|
||||
u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
|
||||
|
||||
dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n",
|
||||
@ -430,7 +413,6 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width,
|
||||
data &= ~(BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD |
|
||||
BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL);
|
||||
break;
|
||||
|
||||
case SPI_NBITS_QUAD:
|
||||
/* clear dual mode and set quad mode */
|
||||
data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL;
|
||||
@ -455,15 +437,17 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width,
|
||||
/* set the override mode */
|
||||
data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
|
||||
bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data);
|
||||
bcm_qspi_bspi_set_xfer_params(qspi, SPINOR_OP_READ_FAST, 0, 0, 0);
|
||||
bcm_qspi_bspi_set_xfer_params(qspi, msg->read_opcode, 0, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
|
||||
int width, int addrlen, int hp)
|
||||
struct spi_flash_read_message *msg, int hp)
|
||||
{
|
||||
int error = 0;
|
||||
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
|
||||
int addrlen = msg->addr_width;
|
||||
|
||||
/* default mode */
|
||||
qspi->xfer_mode.flex_mode = true;
|
||||
@ -475,23 +459,13 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
|
||||
mask = BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
|
||||
if (val & mask || qspi->s3_strap_override_ctrl & mask) {
|
||||
qspi->xfer_mode.flex_mode = false;
|
||||
bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE,
|
||||
0);
|
||||
|
||||
if ((val | qspi->s3_strap_override_ctrl) &
|
||||
BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL)
|
||||
width = SPI_NBITS_DUAL;
|
||||
else if ((val | qspi->s3_strap_override_ctrl) &
|
||||
BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD)
|
||||
width = SPI_NBITS_QUAD;
|
||||
|
||||
error = bcm_qspi_bspi_set_override(qspi, width, addrlen,
|
||||
hp);
|
||||
bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0);
|
||||
error = bcm_qspi_bspi_set_override(qspi, msg, hp);
|
||||
}
|
||||
}
|
||||
|
||||
if (qspi->xfer_mode.flex_mode)
|
||||
error = bcm_qspi_bspi_set_flex_mode(qspi, width, addrlen, hp);
|
||||
error = bcm_qspi_bspi_set_flex_mode(qspi, msg, hp);
|
||||
|
||||
if (error) {
|
||||
dev_warn(&qspi->pdev->dev,
|
||||
@ -981,7 +955,7 @@ static int bcm_qspi_flash_read(struct spi_device *spi,
|
||||
struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
|
||||
int ret = 0;
|
||||
bool mspi_read = false;
|
||||
u32 io_width, addrlen, addr, len;
|
||||
u32 addr, len;
|
||||
u_char *buf;
|
||||
|
||||
buf = msg->buf;
|
||||
@ -1010,9 +984,7 @@ static int bcm_qspi_flash_read(struct spi_device *spi,
|
||||
if (mspi_read)
|
||||
return bcm_qspi_mspi_flash_read(spi, msg);
|
||||
|
||||
io_width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
|
||||
addrlen = msg->addr_width;
|
||||
ret = bcm_qspi_bspi_set_mode(qspi, io_width, addrlen, -1);
|
||||
ret = bcm_qspi_bspi_set_mode(qspi, msg, -1);
|
||||
|
||||
if (!ret)
|
||||
ret = bcm_qspi_bspi_flash_read(spi, msg);
|
||||
@ -1422,6 +1394,11 @@ static int __maybe_unused bcm_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct bcm_qspi *qspi = dev_get_drvdata(dev);
|
||||
|
||||
/* store the override strap value */
|
||||
if (!bcm_qspi_bspi_ver_three(qspi))
|
||||
qspi->s3_strap_override_ctrl =
|
||||
bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
|
||||
|
||||
spi_master_suspend(qspi->master);
|
||||
clk_disable(qspi->clk);
|
||||
bcm_qspi_hw_uninit(qspi);
|
||||
|
@ -108,7 +108,7 @@ struct bcm63xx_hsspi {
|
||||
u8 cs_polarity;
|
||||
};
|
||||
|
||||
static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs,
|
||||
static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs,
|
||||
bool active)
|
||||
{
|
||||
u32 reg;
|
||||
@ -127,7 +127,7 @@ static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs,
|
||||
static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
|
||||
struct spi_device *spi, int hz)
|
||||
{
|
||||
unsigned profile = spi->chip_select;
|
||||
unsigned int profile = spi->chip_select;
|
||||
u32 reg;
|
||||
|
||||
reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz));
|
||||
@ -154,7 +154,7 @@ static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
|
||||
static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
|
||||
unsigned chip_select = spi->chip_select;
|
||||
unsigned int chip_select = spi->chip_select;
|
||||
u16 opcode = 0;
|
||||
int pending = t->len;
|
||||
int step_size = HSSPI_BUFFER_LEN;
|
||||
@ -338,8 +338,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq\n");
|
||||
return -ENXIO;
|
||||
dev_err(dev, "no irq: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -530,8 +530,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq\n");
|
||||
return -ENXIO;
|
||||
dev_err(dev, "no irq: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, "spi");
|
||||
|
@ -576,10 +576,10 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
||||
goto clk_dis_apb;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
|
||||
if (ret < 0)
|
||||
@ -704,7 +704,9 @@ static int __maybe_unused cdns_spi_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct cdns_spi *xspi = spi_master_get_devdata(master);
|
||||
|
||||
cdns_spi_init_hw(xspi);
|
||||
return spi_master_resume(master);
|
||||
}
|
||||
|
||||
|
@ -70,12 +70,9 @@
|
||||
|
||||
/**
|
||||
* struct ep93xx_spi - EP93xx SPI controller structure
|
||||
* @pdev: pointer to platform device
|
||||
* @clk: clock for the controller
|
||||
* @regs_base: pointer to ioremap()'d registers
|
||||
* @mmio: pointer to ioremap()'d registers
|
||||
* @sspdr_phys: physical address of the SSPDR register
|
||||
* @wait: wait here until given transfer is completed
|
||||
* @current_msg: message that is currently processed (or %NULL if none)
|
||||
* @tx: current byte in transfer to transmit
|
||||
* @rx: current byte in transfer to receive
|
||||
* @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
|
||||
@ -90,12 +87,9 @@
|
||||
* the client
|
||||
*/
|
||||
struct ep93xx_spi {
|
||||
const struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
void __iomem *regs_base;
|
||||
void __iomem *mmio;
|
||||
unsigned long sspdr_phys;
|
||||
struct completion wait;
|
||||
struct spi_message *current_msg;
|
||||
size_t tx;
|
||||
size_t rx;
|
||||
size_t fifo_level;
|
||||
@ -111,91 +105,23 @@ struct ep93xx_spi {
|
||||
/* converts bits per word to CR0.DSS value */
|
||||
#define bits_per_word_to_dss(bpw) ((bpw) - 1)
|
||||
|
||||
static void ep93xx_spi_write_u8(const struct ep93xx_spi *espi,
|
||||
u16 reg, u8 value)
|
||||
{
|
||||
writeb(value, espi->regs_base + reg);
|
||||
}
|
||||
|
||||
static u8 ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
|
||||
{
|
||||
return readb(spi->regs_base + reg);
|
||||
}
|
||||
|
||||
static void ep93xx_spi_write_u16(const struct ep93xx_spi *espi,
|
||||
u16 reg, u16 value)
|
||||
{
|
||||
writew(value, espi->regs_base + reg);
|
||||
}
|
||||
|
||||
static u16 ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
|
||||
{
|
||||
return readw(spi->regs_base + reg);
|
||||
}
|
||||
|
||||
static int ep93xx_spi_enable(const struct ep93xx_spi *espi)
|
||||
{
|
||||
u8 regval;
|
||||
int err;
|
||||
|
||||
err = clk_enable(espi->clk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
regval = ep93xx_spi_read_u8(espi, SSPCR1);
|
||||
regval |= SSPCR1_SSE;
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, regval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ep93xx_spi_disable(const struct ep93xx_spi *espi)
|
||||
{
|
||||
u8 regval;
|
||||
|
||||
regval = ep93xx_spi_read_u8(espi, SSPCR1);
|
||||
regval &= ~SSPCR1_SSE;
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, regval);
|
||||
|
||||
clk_disable(espi->clk);
|
||||
}
|
||||
|
||||
static void ep93xx_spi_enable_interrupts(const struct ep93xx_spi *espi)
|
||||
{
|
||||
u8 regval;
|
||||
|
||||
regval = ep93xx_spi_read_u8(espi, SSPCR1);
|
||||
regval |= (SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, regval);
|
||||
}
|
||||
|
||||
static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi)
|
||||
{
|
||||
u8 regval;
|
||||
|
||||
regval = ep93xx_spi_read_u8(espi, SSPCR1);
|
||||
regval &= ~(SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, regval);
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_spi_calc_divisors() - calculates SPI clock divisors
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @master: SPI master
|
||||
* @rate: desired SPI output clock rate
|
||||
* @div_cpsr: pointer to return the cpsr (pre-scaler) divider
|
||||
* @div_scr: pointer to return the scr divider
|
||||
*/
|
||||
static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
||||
static int ep93xx_spi_calc_divisors(struct spi_master *master,
|
||||
u32 rate, u8 *div_cpsr, u8 *div_scr)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(espi->pdev);
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
unsigned long spi_clk_rate = clk_get_rate(espi->clk);
|
||||
int cpsr, scr;
|
||||
|
||||
/*
|
||||
* Make sure that max value is between values supported by the
|
||||
* controller. Note that minimum value is already checked in
|
||||
* ep93xx_spi_transfer_one_message().
|
||||
* controller.
|
||||
*/
|
||||
rate = clamp(rate, master->min_speed_hz, master->max_speed_hz);
|
||||
|
||||
@ -220,26 +146,18 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ep93xx_spi_cs_control(struct spi_device *spi, bool enable)
|
||||
{
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
enable = !enable;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_set_value(spi->cs_gpio, !enable);
|
||||
}
|
||||
|
||||
static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
|
||||
static int ep93xx_spi_chip_setup(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
u8 dss = bits_per_word_to_dss(xfer->bits_per_word);
|
||||
u8 div_cpsr = 0;
|
||||
u8 div_scr = 0;
|
||||
u16 cr0;
|
||||
int err;
|
||||
|
||||
err = ep93xx_spi_calc_divisors(espi, xfer->speed_hz,
|
||||
err = ep93xx_spi_calc_divisors(master, xfer->speed_hz,
|
||||
&div_cpsr, &div_scr);
|
||||
if (err)
|
||||
return err;
|
||||
@ -248,51 +166,49 @@ static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
|
||||
cr0 |= (spi->mode & (SPI_CPHA | SPI_CPOL)) << SSPCR0_MODE_SHIFT;
|
||||
cr0 |= dss;
|
||||
|
||||
dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
|
||||
dev_dbg(&master->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
|
||||
spi->mode, div_cpsr, div_scr, dss);
|
||||
dev_dbg(&espi->pdev->dev, "setup: cr0 %#x\n", cr0);
|
||||
dev_dbg(&master->dev, "setup: cr0 %#x\n", cr0);
|
||||
|
||||
ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr);
|
||||
ep93xx_spi_write_u16(espi, SSPCR0, cr0);
|
||||
writel(div_cpsr, espi->mmio + SSPCPSR);
|
||||
writel(cr0, espi->mmio + SSPCR0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
|
||||
static void ep93xx_do_write(struct spi_master *master)
|
||||
{
|
||||
if (t->bits_per_word > 8) {
|
||||
u16 tx_val = 0;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer = master->cur_msg->state;
|
||||
u32 val = 0;
|
||||
|
||||
if (t->tx_buf)
|
||||
tx_val = ((u16 *)t->tx_buf)[espi->tx];
|
||||
ep93xx_spi_write_u16(espi, SSPDR, tx_val);
|
||||
espi->tx += sizeof(tx_val);
|
||||
if (xfer->bits_per_word > 8) {
|
||||
if (xfer->tx_buf)
|
||||
val = ((u16 *)xfer->tx_buf)[espi->tx];
|
||||
espi->tx += 2;
|
||||
} else {
|
||||
u8 tx_val = 0;
|
||||
|
||||
if (t->tx_buf)
|
||||
tx_val = ((u8 *)t->tx_buf)[espi->tx];
|
||||
ep93xx_spi_write_u8(espi, SSPDR, tx_val);
|
||||
espi->tx += sizeof(tx_val);
|
||||
if (xfer->tx_buf)
|
||||
val = ((u8 *)xfer->tx_buf)[espi->tx];
|
||||
espi->tx += 1;
|
||||
}
|
||||
writel(val, espi->mmio + SSPDR);
|
||||
}
|
||||
|
||||
static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
|
||||
static void ep93xx_do_read(struct spi_master *master)
|
||||
{
|
||||
if (t->bits_per_word > 8) {
|
||||
u16 rx_val;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer = master->cur_msg->state;
|
||||
u32 val;
|
||||
|
||||
rx_val = ep93xx_spi_read_u16(espi, SSPDR);
|
||||
if (t->rx_buf)
|
||||
((u16 *)t->rx_buf)[espi->rx] = rx_val;
|
||||
espi->rx += sizeof(rx_val);
|
||||
val = readl(espi->mmio + SSPDR);
|
||||
if (xfer->bits_per_word > 8) {
|
||||
if (xfer->rx_buf)
|
||||
((u16 *)xfer->rx_buf)[espi->rx] = val;
|
||||
espi->rx += 2;
|
||||
} else {
|
||||
u8 rx_val;
|
||||
|
||||
rx_val = ep93xx_spi_read_u8(espi, SSPDR);
|
||||
if (t->rx_buf)
|
||||
((u8 *)t->rx_buf)[espi->rx] = rx_val;
|
||||
espi->rx += sizeof(rx_val);
|
||||
if (xfer->rx_buf)
|
||||
((u8 *)xfer->rx_buf)[espi->rx] = val;
|
||||
espi->rx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,44 +223,32 @@ static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
|
||||
* When this function is finished, RX FIFO should be empty and TX FIFO should be
|
||||
* full.
|
||||
*/
|
||||
static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
|
||||
static int ep93xx_spi_read_write(struct spi_master *master)
|
||||
{
|
||||
struct spi_message *msg = espi->current_msg;
|
||||
struct spi_transfer *t = msg->state;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer = master->cur_msg->state;
|
||||
|
||||
/* read as long as RX FIFO has frames in it */
|
||||
while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) {
|
||||
ep93xx_do_read(espi, t);
|
||||
while ((readl(espi->mmio + SSPSR) & SSPSR_RNE)) {
|
||||
ep93xx_do_read(master);
|
||||
espi->fifo_level--;
|
||||
}
|
||||
|
||||
/* write as long as TX FIFO has room */
|
||||
while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) {
|
||||
ep93xx_do_write(espi, t);
|
||||
while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < xfer->len) {
|
||||
ep93xx_do_write(master);
|
||||
espi->fifo_level++;
|
||||
}
|
||||
|
||||
if (espi->rx == t->len)
|
||||
if (espi->rx == xfer->len)
|
||||
return 0;
|
||||
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi)
|
||||
{
|
||||
/*
|
||||
* Now everything is set up for the current transfer. We prime the TX
|
||||
* FIFO, enable interrupts, and wait for the transfer to complete.
|
||||
*/
|
||||
if (ep93xx_spi_read_write(espi)) {
|
||||
ep93xx_spi_enable_interrupts(espi);
|
||||
wait_for_completion(&espi->wait);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_spi_dma_prepare() - prepares a DMA transfer
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @master: SPI master
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Function configures the DMA, maps the buffer and prepares the DMA
|
||||
@ -352,9 +256,11 @@ static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi)
|
||||
* in case of failure.
|
||||
*/
|
||||
static struct dma_async_tx_descriptor *
|
||||
ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
||||
ep93xx_spi_dma_prepare(struct spi_master *master,
|
||||
enum dma_transfer_direction dir)
|
||||
{
|
||||
struct spi_transfer *t = espi->current_msg->state;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer = master->cur_msg->state;
|
||||
struct dma_async_tx_descriptor *txd;
|
||||
enum dma_slave_buswidth buswidth;
|
||||
struct dma_slave_config conf;
|
||||
@ -362,10 +268,10 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
||||
struct sg_table *sgt;
|
||||
struct dma_chan *chan;
|
||||
const void *buf, *pbuf;
|
||||
size_t len = t->len;
|
||||
size_t len = xfer->len;
|
||||
int i, ret, nents;
|
||||
|
||||
if (t->bits_per_word > 8)
|
||||
if (xfer->bits_per_word > 8)
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
else
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
@ -375,14 +281,14 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
||||
|
||||
if (dir == DMA_DEV_TO_MEM) {
|
||||
chan = espi->dma_rx;
|
||||
buf = t->rx_buf;
|
||||
buf = xfer->rx_buf;
|
||||
sgt = &espi->rx_sgt;
|
||||
|
||||
conf.src_addr = espi->sspdr_phys;
|
||||
conf.src_addr_width = buswidth;
|
||||
} else {
|
||||
chan = espi->dma_tx;
|
||||
buf = t->tx_buf;
|
||||
buf = xfer->tx_buf;
|
||||
sgt = &espi->tx_sgt;
|
||||
|
||||
conf.dst_addr = espi->sspdr_phys;
|
||||
@ -429,7 +335,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
||||
}
|
||||
|
||||
if (WARN_ON(len)) {
|
||||
dev_warn(&espi->pdev->dev, "len = %zu expected 0!\n", len);
|
||||
dev_warn(&master->dev, "len = %zu expected 0!\n", len);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
@ -447,15 +353,16 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
||||
|
||||
/**
|
||||
* ep93xx_spi_dma_finish() - finishes with a DMA transfer
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @master: SPI master
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Function finishes with the DMA transfer. After this, the DMA buffer is
|
||||
* unmapped.
|
||||
*/
|
||||
static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi,
|
||||
static void ep93xx_spi_dma_finish(struct spi_master *master,
|
||||
enum dma_transfer_direction dir)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct dma_chan *chan;
|
||||
struct sg_table *sgt;
|
||||
|
||||
@ -472,223 +379,69 @@ static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi,
|
||||
|
||||
static void ep93xx_spi_dma_callback(void *callback_param)
|
||||
{
|
||||
complete(callback_param);
|
||||
struct spi_master *master = callback_param;
|
||||
|
||||
ep93xx_spi_dma_finish(master, DMA_MEM_TO_DEV);
|
||||
ep93xx_spi_dma_finish(master, DMA_DEV_TO_MEM);
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
}
|
||||
|
||||
static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
|
||||
static int ep93xx_spi_dma_transfer(struct spi_master *master)
|
||||
{
|
||||
struct spi_message *msg = espi->current_msg;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct dma_async_tx_descriptor *rxd, *txd;
|
||||
|
||||
rxd = ep93xx_spi_dma_prepare(espi, DMA_DEV_TO_MEM);
|
||||
rxd = ep93xx_spi_dma_prepare(master, DMA_DEV_TO_MEM);
|
||||
if (IS_ERR(rxd)) {
|
||||
dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
|
||||
msg->status = PTR_ERR(rxd);
|
||||
return;
|
||||
dev_err(&master->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
|
||||
return PTR_ERR(rxd);
|
||||
}
|
||||
|
||||
txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV);
|
||||
txd = ep93xx_spi_dma_prepare(master, DMA_MEM_TO_DEV);
|
||||
if (IS_ERR(txd)) {
|
||||
ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM);
|
||||
dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
|
||||
msg->status = PTR_ERR(txd);
|
||||
return;
|
||||
ep93xx_spi_dma_finish(master, DMA_DEV_TO_MEM);
|
||||
dev_err(&master->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
|
||||
return PTR_ERR(txd);
|
||||
}
|
||||
|
||||
/* We are ready when RX is done */
|
||||
rxd->callback = ep93xx_spi_dma_callback;
|
||||
rxd->callback_param = &espi->wait;
|
||||
rxd->callback_param = master;
|
||||
|
||||
/* Now submit both descriptors and wait while they finish */
|
||||
/* Now submit both descriptors and start DMA */
|
||||
dmaengine_submit(rxd);
|
||||
dmaengine_submit(txd);
|
||||
|
||||
dma_async_issue_pending(espi->dma_rx);
|
||||
dma_async_issue_pending(espi->dma_tx);
|
||||
|
||||
wait_for_completion(&espi->wait);
|
||||
|
||||
ep93xx_spi_dma_finish(espi, DMA_MEM_TO_DEV);
|
||||
ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_spi_process_transfer() - processes one SPI transfer
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @msg: current message
|
||||
* @t: transfer to process
|
||||
*
|
||||
* This function processes one SPI transfer given in @t. Function waits until
|
||||
* transfer is complete (may sleep) and updates @msg->status based on whether
|
||||
* transfer was successfully processed or not.
|
||||
*/
|
||||
static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
|
||||
struct spi_message *msg,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
int err;
|
||||
|
||||
msg->state = t;
|
||||
|
||||
err = ep93xx_spi_chip_setup(espi, msg->spi, t);
|
||||
if (err) {
|
||||
dev_err(&espi->pdev->dev,
|
||||
"failed to setup chip for transfer\n");
|
||||
msg->status = err;
|
||||
return;
|
||||
}
|
||||
|
||||
espi->rx = 0;
|
||||
espi->tx = 0;
|
||||
|
||||
/*
|
||||
* There is no point of setting up DMA for the transfers which will
|
||||
* fit into the FIFO and can be transferred with a single interrupt.
|
||||
* So in these cases we will be using PIO and don't bother for DMA.
|
||||
*/
|
||||
if (espi->dma_rx && t->len > SPI_FIFO_SIZE)
|
||||
ep93xx_spi_dma_transfer(espi);
|
||||
else
|
||||
ep93xx_spi_pio_transfer(espi);
|
||||
|
||||
/*
|
||||
* In case of error during transmit, we bail out from processing
|
||||
* the message.
|
||||
*/
|
||||
if (msg->status)
|
||||
return;
|
||||
|
||||
msg->actual_length += t->len;
|
||||
|
||||
/*
|
||||
* After this transfer is finished, perform any possible
|
||||
* post-transfer actions requested by the protocol driver.
|
||||
*/
|
||||
if (t->delay_usecs) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(usecs_to_jiffies(t->delay_usecs));
|
||||
}
|
||||
if (t->cs_change) {
|
||||
if (!list_is_last(&t->transfer_list, &msg->transfers)) {
|
||||
/*
|
||||
* In case protocol driver is asking us to drop the
|
||||
* chipselect briefly, we let the scheduler to handle
|
||||
* any "delay" here.
|
||||
*/
|
||||
ep93xx_spi_cs_control(msg->spi, false);
|
||||
cond_resched();
|
||||
ep93xx_spi_cs_control(msg->spi, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ep93xx_spi_process_message() - process one SPI message
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @msg: message to process
|
||||
*
|
||||
* This function processes a single SPI message. We go through all transfers in
|
||||
* the message and pass them to ep93xx_spi_process_transfer(). Chipselect is
|
||||
* asserted during the whole message (unless per transfer cs_change is set).
|
||||
*
|
||||
* @msg->status contains %0 in case of success or negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static void ep93xx_spi_process_message(struct ep93xx_spi *espi,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct spi_transfer *t;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Enable the SPI controller and its clock.
|
||||
*/
|
||||
err = ep93xx_spi_enable(espi);
|
||||
if (err) {
|
||||
dev_err(&espi->pdev->dev, "failed to enable SPI controller\n");
|
||||
msg->status = err;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Just to be sure: flush any data from RX FIFO.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(SPI_TIMEOUT);
|
||||
while (ep93xx_spi_read_u16(espi, SSPSR) & SSPSR_RNE) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_warn(&espi->pdev->dev,
|
||||
"timeout while flushing RX FIFO\n");
|
||||
msg->status = -ETIMEDOUT;
|
||||
return;
|
||||
}
|
||||
ep93xx_spi_read_u16(espi, SSPDR);
|
||||
}
|
||||
|
||||
/*
|
||||
* We explicitly handle FIFO level. This way we don't have to check TX
|
||||
* FIFO status using %SSPSR_TNF bit which may cause RX FIFO overruns.
|
||||
*/
|
||||
espi->fifo_level = 0;
|
||||
|
||||
/*
|
||||
* Assert the chipselect.
|
||||
*/
|
||||
ep93xx_spi_cs_control(msg->spi, true);
|
||||
|
||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||
ep93xx_spi_process_transfer(espi, msg, t);
|
||||
if (msg->status)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now the whole message is transferred (or failed for some reason). We
|
||||
* deselect the device and disable the SPI controller.
|
||||
*/
|
||||
ep93xx_spi_cs_control(msg->spi, false);
|
||||
ep93xx_spi_disable(espi);
|
||||
}
|
||||
|
||||
static int ep93xx_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
|
||||
msg->state = NULL;
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
|
||||
espi->current_msg = msg;
|
||||
ep93xx_spi_process_message(espi, msg);
|
||||
espi->current_msg = NULL;
|
||||
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
/* signal that we need to wait for completion */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct ep93xx_spi *espi = dev_id;
|
||||
u8 irq_status = ep93xx_spi_read_u8(espi, SSPIIR);
|
||||
struct spi_master *master = dev_id;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* If we got ROR (receive overrun) interrupt we know that something is
|
||||
* wrong. Just abort the message.
|
||||
*/
|
||||
if (unlikely(irq_status & SSPIIR_RORIS)) {
|
||||
if (readl(espi->mmio + SSPIIR) & SSPIIR_RORIS) {
|
||||
/* clear the overrun interrupt */
|
||||
ep93xx_spi_write_u8(espi, SSPICR, 0);
|
||||
dev_warn(&espi->pdev->dev,
|
||||
writel(0, espi->mmio + SSPICR);
|
||||
dev_warn(&master->dev,
|
||||
"receive overrun, aborting the message\n");
|
||||
espi->current_msg->status = -EIO;
|
||||
master->cur_msg->status = -EIO;
|
||||
} else {
|
||||
/*
|
||||
* Interrupt is either RX (RIS) or TX (TIS). For both cases we
|
||||
* simply execute next data transfer.
|
||||
*/
|
||||
if (ep93xx_spi_read_write(espi)) {
|
||||
if (ep93xx_spi_read_write(master)) {
|
||||
/*
|
||||
* In normal case, there still is some processing left
|
||||
* for current transfer. Let's wait for the next
|
||||
@ -703,11 +456,111 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
|
||||
* any case we disable interrupts and notify the worker to handle
|
||||
* any post-processing of the message.
|
||||
*/
|
||||
ep93xx_spi_disable_interrupts(espi);
|
||||
complete(&espi->wait);
|
||||
val = readl(espi->mmio + SSPCR1);
|
||||
val &= ~(SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
|
||||
writel(val, espi->mmio + SSPCR1);
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ep93xx_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = ep93xx_spi_chip_setup(master, spi, xfer);
|
||||
if (ret) {
|
||||
dev_err(&master->dev, "failed to setup chip for transfer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->cur_msg->state = xfer;
|
||||
espi->rx = 0;
|
||||
espi->tx = 0;
|
||||
|
||||
/*
|
||||
* There is no point of setting up DMA for the transfers which will
|
||||
* fit into the FIFO and can be transferred with a single interrupt.
|
||||
* So in these cases we will be using PIO and don't bother for DMA.
|
||||
*/
|
||||
if (espi->dma_rx && xfer->len > SPI_FIFO_SIZE)
|
||||
return ep93xx_spi_dma_transfer(master);
|
||||
|
||||
/* Using PIO so prime the TX FIFO and enable interrupts */
|
||||
ep93xx_spi_read_write(master);
|
||||
|
||||
val = readl(espi->mmio + SSPCR1);
|
||||
val |= (SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
|
||||
writel(val, espi->mmio + SSPCR1);
|
||||
|
||||
/* signal that we need to wait for completion */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ep93xx_spi_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
unsigned long timeout;
|
||||
|
||||
/*
|
||||
* Just to be sure: flush any data from RX FIFO.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(SPI_TIMEOUT);
|
||||
while (readl(espi->mmio + SSPSR) & SSPSR_RNE) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_warn(&master->dev,
|
||||
"timeout while flushing RX FIFO\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
readl(espi->mmio + SSPDR);
|
||||
}
|
||||
|
||||
/*
|
||||
* We explicitly handle FIFO level. This way we don't have to check TX
|
||||
* FIFO status using %SSPSR_TNF bit which may cause RX FIFO overruns.
|
||||
*/
|
||||
espi->fifo_level = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_spi_prepare_hardware(struct spi_master *master)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(espi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = readl(espi->mmio + SSPCR1);
|
||||
val |= SSPCR1_SSE;
|
||||
writel(val, espi->mmio + SSPCR1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_spi_unprepare_hardware(struct spi_master *master)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
|
||||
val = readl(espi->mmio + SSPCR1);
|
||||
val &= ~SSPCR1_SSE;
|
||||
writel(val, espi->mmio + SSPCR1);
|
||||
|
||||
clk_disable(espi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ep93xx_spi_dma_filter(struct dma_chan *chan, void *filter_param)
|
||||
{
|
||||
if (ep93xx_dma_chan_is_m2p(chan))
|
||||
@ -809,7 +662,10 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->transfer_one_message = ep93xx_spi_transfer_one_message;
|
||||
master->prepare_transfer_hardware = ep93xx_spi_prepare_hardware;
|
||||
master->unprepare_transfer_hardware = ep93xx_spi_unprepare_hardware;
|
||||
master->prepare_message = ep93xx_spi_prepare_message;
|
||||
master->transfer_one = ep93xx_spi_transfer_one;
|
||||
master->bus_num = pdev->id;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
@ -850,26 +706,23 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
||||
goto fail_release_master;
|
||||
}
|
||||
|
||||
init_completion(&espi->wait);
|
||||
|
||||
/*
|
||||
* Calculate maximum and minimum supported clock rates
|
||||
* for the controller.
|
||||
*/
|
||||
master->max_speed_hz = clk_get_rate(espi->clk) / 2;
|
||||
master->min_speed_hz = clk_get_rate(espi->clk) / (254 * 256);
|
||||
espi->pdev = pdev;
|
||||
|
||||
espi->sspdr_phys = res->start + SSPDR;
|
||||
|
||||
espi->regs_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(espi->regs_base)) {
|
||||
error = PTR_ERR(espi->regs_base);
|
||||
espi->mmio = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(espi->mmio)) {
|
||||
error = PTR_ERR(espi->mmio);
|
||||
goto fail_release_master;
|
||||
}
|
||||
|
||||
error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt,
|
||||
0, "ep93xx-spi", espi);
|
||||
0, "ep93xx-spi", master);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to request irq\n");
|
||||
goto fail_release_master;
|
||||
@ -879,7 +732,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
|
||||
|
||||
/* make sure that the hardware is disabled */
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, 0);
|
||||
writel(0, espi->mmio + SSPCR1);
|
||||
|
||||
error = devm_spi_register_master(&pdev->dev, master);
|
||||
if (error) {
|
||||
|
@ -395,11 +395,6 @@ static int falcon_sflash_probe(struct platform_device *pdev)
|
||||
struct spi_master *master;
|
||||
int ret;
|
||||
|
||||
if (ltq_boot_select() != BS_SPI) {
|
||||
dev_err(&pdev->dev, "invalid bootstrap options\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*priv));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
/* The maximum bytes that a sdma BD can transfer.*/
|
||||
#define MAX_SDMA_BD_BYTES (1 << 15)
|
||||
#define MX51_ECSPI_CTRL_MAX_BURST 512
|
||||
|
||||
enum spi_imx_devtype {
|
||||
IMX1_CSPI,
|
||||
@ -63,7 +64,8 @@ enum spi_imx_devtype {
|
||||
IMX27_CSPI,
|
||||
IMX31_CSPI,
|
||||
IMX35_CSPI, /* CSPI on all i.mx except above */
|
||||
IMX51_ECSPI, /* ECSPI on i.mx51 and later */
|
||||
IMX51_ECSPI, /* ECSPI on i.mx51 */
|
||||
IMX53_ECSPI, /* ECSPI on i.mx53 and later */
|
||||
};
|
||||
|
||||
struct spi_imx_data;
|
||||
@ -74,6 +76,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 *);
|
||||
bool has_dmamode;
|
||||
unsigned int fifo_size;
|
||||
bool dynamic_burst;
|
||||
enum spi_imx_devtype devtype;
|
||||
};
|
||||
|
||||
@ -94,12 +99,14 @@ struct spi_imx_data {
|
||||
unsigned int bits_per_word;
|
||||
unsigned int spi_drctl;
|
||||
|
||||
unsigned int count;
|
||||
unsigned int count, remainder;
|
||||
void (*tx)(struct spi_imx_data *);
|
||||
void (*rx)(struct spi_imx_data *);
|
||||
void *rx_buf;
|
||||
const void *tx_buf;
|
||||
unsigned int txfifo; /* number of words pushed in tx FIFO */
|
||||
unsigned int dynamic_burst, read_u32;
|
||||
unsigned int word_mask;
|
||||
|
||||
/* DMA */
|
||||
bool usedma;
|
||||
@ -125,9 +132,9 @@ static inline int is_imx51_ecspi(struct spi_imx_data *d)
|
||||
return d->devtype_data->devtype == IMX51_ECSPI;
|
||||
}
|
||||
|
||||
static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
|
||||
static inline int is_imx53_ecspi(struct spi_imx_data *d)
|
||||
{
|
||||
return is_imx51_ecspi(d) ? 64 : 8;
|
||||
return d->devtype_data->devtype == IMX53_ECSPI;
|
||||
}
|
||||
|
||||
#define MXC_SPI_BUF_RX(type) \
|
||||
@ -219,7 +226,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
if (bytes_per_word != 1 && bytes_per_word != 2 && bytes_per_word != 4)
|
||||
return false;
|
||||
|
||||
for (i = spi_imx_get_fifosize(spi_imx) / 2; i > 0; i--) {
|
||||
for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) {
|
||||
if (!(transfer->len % (i * bytes_per_word)))
|
||||
break;
|
||||
}
|
||||
@ -228,6 +235,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
return false;
|
||||
|
||||
spi_imx->wml = i;
|
||||
spi_imx->dynamic_burst = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -242,6 +250,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
|
||||
#define MX51_ECSPI_CTRL_CS(cs) ((cs) << 18)
|
||||
#define MX51_ECSPI_CTRL_BL_OFFSET 20
|
||||
#define MX51_ECSPI_CTRL_BL_MASK (0xfff << 20)
|
||||
|
||||
#define MX51_ECSPI_CONFIG 0x0c
|
||||
#define MX51_ECSPI_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0))
|
||||
@ -269,6 +278,106 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
#define MX51_ECSPI_TESTREG 0x20
|
||||
#define MX51_ECSPI_TESTREG_LBC BIT(31)
|
||||
|
||||
static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA);
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
unsigned int bytes_per_word;
|
||||
#endif
|
||||
|
||||
if (spi_imx->rx_buf) {
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
if (bytes_per_word == 1)
|
||||
val = cpu_to_be32(val);
|
||||
else if (bytes_per_word == 2)
|
||||
val = (val << 16) | (val >> 16);
|
||||
#endif
|
||||
val &= spi_imx->word_mask;
|
||||
*(u32 *)spi_imx->rx_buf = val;
|
||||
spi_imx->rx_buf += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_imx_buf_rx_swap(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
unsigned int bytes_per_word;
|
||||
|
||||
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
if (spi_imx->read_u32) {
|
||||
spi_imx_buf_rx_swap_u32(spi_imx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_per_word == 1)
|
||||
spi_imx_buf_rx_u8(spi_imx);
|
||||
else if (bytes_per_word == 2)
|
||||
spi_imx_buf_rx_u16(spi_imx);
|
||||
}
|
||||
|
||||
static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 val = 0;
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
unsigned int bytes_per_word;
|
||||
#endif
|
||||
|
||||
if (spi_imx->tx_buf) {
|
||||
val = *(u32 *)spi_imx->tx_buf;
|
||||
val &= spi_imx->word_mask;
|
||||
spi_imx->tx_buf += sizeof(u32);
|
||||
}
|
||||
|
||||
spi_imx->count -= sizeof(u32);
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
|
||||
if (bytes_per_word == 1)
|
||||
val = cpu_to_be32(val);
|
||||
else if (bytes_per_word == 2)
|
||||
val = (val << 16) | (val >> 16);
|
||||
#endif
|
||||
writel(val, spi_imx->base + MXC_CSPITXDATA);
|
||||
}
|
||||
|
||||
static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 ctrl, val;
|
||||
unsigned int bytes_per_word;
|
||||
|
||||
if (spi_imx->count == spi_imx->remainder) {
|
||||
ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
|
||||
if (spi_imx->count > MX51_ECSPI_CTRL_MAX_BURST) {
|
||||
spi_imx->remainder = spi_imx->count %
|
||||
MX51_ECSPI_CTRL_MAX_BURST;
|
||||
val = MX51_ECSPI_CTRL_MAX_BURST * 8 - 1;
|
||||
} else if (spi_imx->count >= sizeof(u32)) {
|
||||
spi_imx->remainder = spi_imx->count % sizeof(u32);
|
||||
val = (spi_imx->count - spi_imx->remainder) * 8 - 1;
|
||||
} else {
|
||||
spi_imx->remainder = 0;
|
||||
val = spi_imx->bits_per_word - 1;
|
||||
spi_imx->read_u32 = 0;
|
||||
}
|
||||
|
||||
ctrl |= (val << MX51_ECSPI_CTRL_BL_OFFSET);
|
||||
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
||||
}
|
||||
|
||||
if (spi_imx->count >= sizeof(u32)) {
|
||||
spi_imx_buf_tx_swap_u32(spi_imx);
|
||||
return;
|
||||
}
|
||||
|
||||
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
|
||||
if (bytes_per_word == 1)
|
||||
spi_imx_buf_tx_u8(spi_imx);
|
||||
else if (bytes_per_word == 2)
|
||||
spi_imx_buf_tx_u16(spi_imx);
|
||||
}
|
||||
|
||||
/* MX51 eCSPI */
|
||||
static unsigned int mx51_ecspi_clkdiv(struct spi_imx_data *spi_imx,
|
||||
unsigned int fspi, unsigned int *fres)
|
||||
@ -513,8 +622,8 @@ static int mx31_config(struct spi_device *spi)
|
||||
reg |= MX31_CSPICTRL_POL;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
reg |= MX31_CSPICTRL_SSPOL;
|
||||
if (spi->cs_gpio < 0)
|
||||
reg |= (spi->cs_gpio + 32) <<
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
reg |= (spi->chip_select) <<
|
||||
(is_imx35_cspi(spi_imx) ? MX35_CSPICTRL_CS_SHIFT :
|
||||
MX31_CSPICTRL_CS_SHIFT);
|
||||
|
||||
@ -605,8 +714,8 @@ static int mx21_config(struct spi_device *spi)
|
||||
reg |= MX21_CSPICTRL_POL;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
reg |= MX21_CSPICTRL_SSPOL;
|
||||
if (spi->cs_gpio < 0)
|
||||
reg |= (spi->cs_gpio + 32) << MX21_CSPICTRL_CS_SHIFT;
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
reg |= spi->chip_select << MX21_CSPICTRL_CS_SHIFT;
|
||||
|
||||
writel(reg, spi_imx->base + MXC_CSPICTRL);
|
||||
|
||||
@ -693,6 +802,9 @@ static struct spi_imx_devtype_data imx1_cspi_devtype_data = {
|
||||
.trigger = mx1_trigger,
|
||||
.rx_available = mx1_rx_available,
|
||||
.reset = mx1_reset,
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.devtype = IMX1_CSPI,
|
||||
};
|
||||
|
||||
@ -702,6 +814,9 @@ static struct spi_imx_devtype_data imx21_cspi_devtype_data = {
|
||||
.trigger = mx21_trigger,
|
||||
.rx_available = mx21_rx_available,
|
||||
.reset = mx21_reset,
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.devtype = IMX21_CSPI,
|
||||
};
|
||||
|
||||
@ -712,6 +827,9 @@ static struct spi_imx_devtype_data imx27_cspi_devtype_data = {
|
||||
.trigger = mx21_trigger,
|
||||
.rx_available = mx21_rx_available,
|
||||
.reset = mx21_reset,
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.devtype = IMX27_CSPI,
|
||||
};
|
||||
|
||||
@ -721,6 +839,9 @@ static struct spi_imx_devtype_data imx31_cspi_devtype_data = {
|
||||
.trigger = mx31_trigger,
|
||||
.rx_available = mx31_rx_available,
|
||||
.reset = mx31_reset,
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = false,
|
||||
.dynamic_burst = false,
|
||||
.devtype = IMX31_CSPI,
|
||||
};
|
||||
|
||||
@ -731,6 +852,9 @@ static struct spi_imx_devtype_data imx35_cspi_devtype_data = {
|
||||
.trigger = mx31_trigger,
|
||||
.rx_available = mx31_rx_available,
|
||||
.reset = mx31_reset,
|
||||
.fifo_size = 8,
|
||||
.has_dmamode = true,
|
||||
.dynamic_burst = false,
|
||||
.devtype = IMX35_CSPI,
|
||||
};
|
||||
|
||||
@ -740,9 +864,23 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
|
||||
.trigger = mx51_ecspi_trigger,
|
||||
.rx_available = mx51_ecspi_rx_available,
|
||||
.reset = mx51_ecspi_reset,
|
||||
.fifo_size = 64,
|
||||
.has_dmamode = true,
|
||||
.dynamic_burst = true,
|
||||
.devtype = IMX51_ECSPI,
|
||||
};
|
||||
|
||||
static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
|
||||
.intctrl = mx51_ecspi_intctrl,
|
||||
.config = mx51_ecspi_config,
|
||||
.trigger = mx51_ecspi_trigger,
|
||||
.rx_available = mx51_ecspi_rx_available,
|
||||
.reset = mx51_ecspi_reset,
|
||||
.fifo_size = 64,
|
||||
.has_dmamode = true,
|
||||
.devtype = IMX53_ECSPI,
|
||||
};
|
||||
|
||||
static const struct platform_device_id spi_imx_devtype[] = {
|
||||
{
|
||||
.name = "imx1-cspi",
|
||||
@ -762,6 +900,9 @@ static const struct platform_device_id spi_imx_devtype[] = {
|
||||
}, {
|
||||
.name = "imx51-ecspi",
|
||||
.driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx53-ecspi",
|
||||
.driver_data = (kernel_ulong_t) &imx53_ecspi_devtype_data,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
@ -774,6 +915,7 @@ static const struct of_device_id spi_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, },
|
||||
{ .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
|
||||
{ .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
|
||||
{ .compatible = "fsl,imx53-ecspi", .data = &imx53_ecspi_devtype_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
|
||||
@ -783,6 +925,9 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
||||
int active = is_active != BITBANG_CS_INACTIVE;
|
||||
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
|
||||
|
||||
if (spi->mode & SPI_NO_CS)
|
||||
return;
|
||||
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
return;
|
||||
|
||||
@ -791,9 +936,11 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
||||
|
||||
static void spi_imx_push(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
while (spi_imx->txfifo < spi_imx_get_fifosize(spi_imx)) {
|
||||
while (spi_imx->txfifo < spi_imx->devtype_data->fifo_size) {
|
||||
if (!spi_imx->count)
|
||||
break;
|
||||
if (spi_imx->txfifo && (spi_imx->count == spi_imx->remainder))
|
||||
break;
|
||||
spi_imx->tx(spi_imx);
|
||||
spi_imx->txfifo++;
|
||||
}
|
||||
@ -887,15 +1034,37 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
||||
spi_imx->speed_hz = t->speed_hz;
|
||||
|
||||
/* Initialize the functions for transfer */
|
||||
if (spi_imx->bits_per_word <= 8) {
|
||||
spi_imx->rx = spi_imx_buf_rx_u8;
|
||||
spi_imx->tx = spi_imx_buf_tx_u8;
|
||||
} else if (spi_imx->bits_per_word <= 16) {
|
||||
spi_imx->rx = spi_imx_buf_rx_u16;
|
||||
spi_imx->tx = spi_imx_buf_tx_u16;
|
||||
if (spi_imx->devtype_data->dynamic_burst) {
|
||||
u32 mask;
|
||||
|
||||
spi_imx->dynamic_burst = 0;
|
||||
spi_imx->remainder = 0;
|
||||
spi_imx->read_u32 = 1;
|
||||
|
||||
mask = (1 << spi_imx->bits_per_word) - 1;
|
||||
spi_imx->rx = spi_imx_buf_rx_swap;
|
||||
spi_imx->tx = spi_imx_buf_tx_swap;
|
||||
spi_imx->dynamic_burst = 1;
|
||||
spi_imx->remainder = t->len;
|
||||
|
||||
if (spi_imx->bits_per_word <= 8)
|
||||
spi_imx->word_mask = mask << 24 | mask << 16
|
||||
| mask << 8 | mask;
|
||||
else if (spi_imx->bits_per_word <= 16)
|
||||
spi_imx->word_mask = mask << 16 | mask;
|
||||
else
|
||||
spi_imx->word_mask = mask;
|
||||
} else {
|
||||
spi_imx->rx = spi_imx_buf_rx_u32;
|
||||
spi_imx->tx = spi_imx_buf_tx_u32;
|
||||
if (spi_imx->bits_per_word <= 8) {
|
||||
spi_imx->rx = spi_imx_buf_rx_u8;
|
||||
spi_imx->tx = spi_imx_buf_tx_u8;
|
||||
} else if (spi_imx->bits_per_word <= 16) {
|
||||
spi_imx->rx = spi_imx_buf_rx_u16;
|
||||
spi_imx->tx = spi_imx_buf_tx_u16;
|
||||
} else {
|
||||
spi_imx->rx = spi_imx_buf_rx_u32;
|
||||
spi_imx->tx = spi_imx_buf_tx_u32;
|
||||
}
|
||||
}
|
||||
|
||||
if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t))
|
||||
@ -938,7 +1107,7 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
||||
if (of_machine_is_compatible("fsl,imx6dl"))
|
||||
return 0;
|
||||
|
||||
spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
spi_imx->wml = spi_imx->devtype_data->fifo_size / 2;
|
||||
|
||||
/* Prepare for TX DMA: */
|
||||
master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
|
||||
@ -1109,6 +1278,9 @@ static int spi_imx_setup(struct spi_device *spi)
|
||||
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
|
||||
spi->mode, spi->bits_per_word, spi->max_speed_hz);
|
||||
|
||||
if (spi->mode & SPI_NO_CS)
|
||||
return 0;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
spi->mode & SPI_CS_HIGH ? 0 : 1);
|
||||
@ -1208,8 +1380,10 @@ 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->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx))
|
||||
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) ||
|
||||
is_imx53_ecspi(spi_imx))
|
||||
spi_imx->bitbang.master->mode_bits |= SPI_LOOP | SPI_READY;
|
||||
|
||||
spi_imx->spi_drctl = spi_drctl;
|
||||
@ -1262,7 +1436,7 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
* Only validated on i.mx35 and i.mx6 now, can remove the constraint
|
||||
* if validated on other chips.
|
||||
*/
|
||||
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx)) {
|
||||
if (spi_imx->devtype_data->has_dmamode) {
|
||||
ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto out_clk_put;
|
||||
|
@ -32,39 +32,50 @@
|
||||
#include "spi-test.h"
|
||||
|
||||
/* flag to only simulate transfers */
|
||||
int simulate_only;
|
||||
static int simulate_only;
|
||||
module_param(simulate_only, int, 0);
|
||||
MODULE_PARM_DESC(simulate_only, "if not 0 do not execute the spi message");
|
||||
|
||||
/* dump spi messages */
|
||||
int dump_messages;
|
||||
static int dump_messages;
|
||||
module_param(dump_messages, int, 0);
|
||||
MODULE_PARM_DESC(dump_messages,
|
||||
"=1 dump the basic spi_message_structure, " \
|
||||
"=2 dump the spi_message_structure including data, " \
|
||||
"=3 dump the spi_message structure before and after execution");
|
||||
/* the device is jumpered for loopback - enabling some rx_buf tests */
|
||||
int loopback;
|
||||
static int loopback;
|
||||
module_param(loopback, int, 0);
|
||||
MODULE_PARM_DESC(loopback,
|
||||
"if set enable loopback mode, where the rx_buf " \
|
||||
"is checked to match tx_buf after the spi_message " \
|
||||
"is executed");
|
||||
|
||||
static int loop_req;
|
||||
module_param(loop_req, int, 0);
|
||||
MODULE_PARM_DESC(loop_req,
|
||||
"if set controller will be asked to enable test loop mode. " \
|
||||
"If controller supported it, MISO and MOSI will be connected");
|
||||
|
||||
static int no_cs;
|
||||
module_param(no_cs, int, 0);
|
||||
MODULE_PARM_DESC(no_cs,
|
||||
"if set Chip Select (CS) will not be used");
|
||||
|
||||
/* run only a specific test */
|
||||
int run_only_test = -1;
|
||||
static int run_only_test = -1;
|
||||
module_param(run_only_test, int, 0);
|
||||
MODULE_PARM_DESC(run_only_test,
|
||||
"only run the test with this number (0-based !)");
|
||||
|
||||
/* use vmalloc'ed buffers */
|
||||
int use_vmalloc;
|
||||
static int use_vmalloc;
|
||||
module_param(use_vmalloc, int, 0644);
|
||||
MODULE_PARM_DESC(use_vmalloc,
|
||||
"use vmalloc'ed buffers instead of kmalloc'ed");
|
||||
|
||||
/* check rx ranges */
|
||||
int check_ranges = 1;
|
||||
static int check_ranges = 1;
|
||||
module_param(check_ranges, int, 0644);
|
||||
MODULE_PARM_DESC(check_ranges,
|
||||
"checks rx_buffer pattern are valid");
|
||||
@ -313,6 +324,17 @@ static int spi_loopback_test_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (loop_req || no_cs) {
|
||||
spi->mode |= loop_req ? SPI_LOOP : 0;
|
||||
spi->mode |= no_cs ? SPI_NO_CS : 0;
|
||||
ret = spi_setup(spi);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "SPI setup with SPI_LOOP or SPI_NO_CS failed (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "Executing spi-loopback-tests\n");
|
||||
|
||||
ret = spi_test_run_tests(spi, spi_tests);
|
||||
|
@ -1338,7 +1338,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||
struct resource *r;
|
||||
int status = 0, i;
|
||||
u32 regs_offset = 0;
|
||||
static int bus_num = 1;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
|
||||
@ -1374,14 +1373,11 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||
|
||||
of_property_read_u32(node, "ti,spi-num-cs", &num_cs);
|
||||
master->num_chipselect = num_cs;
|
||||
master->bus_num = bus_num++;
|
||||
if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL))
|
||||
mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
|
||||
} else {
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
master->num_chipselect = pdata->num_cs;
|
||||
if (pdev->id != -1)
|
||||
master->bus_num = pdev->id;
|
||||
mcspi->pin_dir = pdata->pin_dir;
|
||||
}
|
||||
regs_offset = pdata->regs_offset;
|
||||
|
@ -669,8 +669,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
status = of_property_read_u32(np, "reg", &cs);
|
||||
if (status) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s has no valid 'reg' property (%d)\n",
|
||||
np->full_name, status);
|
||||
"%pOF has no valid 'reg' property (%d)\n",
|
||||
np, status);
|
||||
status = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -52,14 +52,14 @@ struct pic32_spi_regs {
|
||||
|
||||
/* Bit fields of SPI Control Register */
|
||||
#define CTRL_RX_INT_SHIFT 0 /* Rx interrupt generation */
|
||||
#define RX_FIFO_EMTPY 0
|
||||
#define RX_FIFO_EMPTY 0
|
||||
#define RX_FIFO_NOT_EMPTY 1 /* not empty */
|
||||
#define RX_FIFO_HALF_FULL 2 /* full by half or more */
|
||||
#define RX_FIFO_FULL 3 /* completely full */
|
||||
|
||||
#define CTRL_TX_INT_SHIFT 2 /* TX interrupt generation */
|
||||
#define TX_FIFO_ALL_EMPTY 0 /* completely empty */
|
||||
#define TX_FIFO_EMTPY 1 /* empty */
|
||||
#define TX_FIFO_EMPTY 1 /* empty */
|
||||
#define TX_FIFO_HALF_EMPTY 2 /* empty by half or more */
|
||||
#define TX_FIFO_NOT_FULL 3 /* atleast one empty */
|
||||
|
||||
|
@ -2429,7 +2429,7 @@ static struct vendor_data vendor_lsi = {
|
||||
.internal_cs_ctrl = true,
|
||||
};
|
||||
|
||||
static struct amba_id pl022_ids[] = {
|
||||
static const struct amba_id pl022_ids[] = {
|
||||
{
|
||||
/*
|
||||
* ARM PL022 variant, this has a 16bit wide
|
||||
|
@ -402,8 +402,8 @@ static void cs_assert(struct driver_data *drv_data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(chip->gpio_cs)) {
|
||||
gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted);
|
||||
if (chip->gpiod_cs) {
|
||||
gpiod_set_value(chip->gpiod_cs, chip->gpio_cs_inverted);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -424,8 +424,8 @@ static void cs_deassert(struct driver_data *drv_data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(chip->gpio_cs)) {
|
||||
gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted);
|
||||
if (chip->gpiod_cs) {
|
||||
gpiod_set_value(chip->gpiod_cs, !chip->gpio_cs_inverted);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1213,17 +1213,16 @@ 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);
|
||||
struct gpio_desc *gpiod;
|
||||
int err = 0;
|
||||
|
||||
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->gpiod_cs = gpiod;
|
||||
chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;
|
||||
gpiod_set_value(gpiod, chip->gpio_cs_inverted);
|
||||
}
|
||||
@ -1237,8 +1236,10 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip,
|
||||
/* NOTE: setup() can be called multiple times, possibly with
|
||||
* different chip_info, release previously requested GPIO
|
||||
*/
|
||||
if (gpio_is_valid(chip->gpio_cs))
|
||||
gpio_free(chip->gpio_cs);
|
||||
if (chip->gpiod_cs) {
|
||||
gpio_free(desc_to_gpio(chip->gpiod_cs));
|
||||
chip->gpiod_cs = NULL;
|
||||
}
|
||||
|
||||
/* If (*cs_control) is provided, ignore GPIO chip select */
|
||||
if (chip_info->cs_control) {
|
||||
@ -1254,11 +1255,11 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip,
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->gpio_cs = chip_info->gpio_cs;
|
||||
gpiod = gpio_to_desc(chip_info->gpio_cs);
|
||||
chip->gpiod_cs = gpiod;
|
||||
chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;
|
||||
|
||||
err = gpio_direction_output(chip->gpio_cs,
|
||||
!chip->gpio_cs_inverted);
|
||||
err = gpiod_direction_output(gpiod, !chip->gpio_cs_inverted);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -1317,8 +1318,7 @@ static int setup(struct spi_device *spi)
|
||||
}
|
||||
|
||||
chip->frm = spi->chip_select;
|
||||
} else
|
||||
chip->gpio_cs = -1;
|
||||
}
|
||||
chip->enable_dma = drv_data->master_info->enable_dma;
|
||||
chip->timeout = TIMOUT_DFLT;
|
||||
}
|
||||
@ -1416,8 +1416,8 @@ static void cleanup(struct spi_device *spi)
|
||||
return;
|
||||
|
||||
if (drv_data->ssp_type != CE4100_SSP && !drv_data->cs_gpiods &&
|
||||
gpio_is_valid(chip->gpio_cs))
|
||||
gpio_free(chip->gpio_cs);
|
||||
chip->gpiod_cs)
|
||||
gpio_free(desc_to_gpio(chip->gpiod_cs));
|
||||
|
||||
kfree(chip);
|
||||
}
|
||||
@ -1769,8 +1769,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
gpiod = devm_gpiod_get_index(dev, "cs", i,
|
||||
GPIOD_OUT_HIGH);
|
||||
gpiod = devm_gpiod_get_index(dev, "cs", i, GPIOD_ASIS);
|
||||
if (IS_ERR(gpiod)) {
|
||||
/* Means use native chip select */
|
||||
if (PTR_ERR(gpiod) == -ENOENT)
|
||||
|
@ -83,7 +83,7 @@ struct chip_data {
|
||||
u16 lpss_tx_threshold;
|
||||
u8 enable_dma;
|
||||
union {
|
||||
int gpio_cs;
|
||||
struct gpio_desc *gpiod_cs;
|
||||
unsigned int frm;
|
||||
};
|
||||
int gpio_cs_inverted;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -82,6 +83,8 @@
|
||||
#define QUP_IO_M_MODE_BAM 3
|
||||
|
||||
/* QUP_OPERATIONAL fields */
|
||||
#define QUP_OP_IN_BLOCK_READ_REQ BIT(13)
|
||||
#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12)
|
||||
#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
|
||||
#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
|
||||
#define QUP_OP_IN_SERVICE_FLAG BIT(9)
|
||||
@ -118,7 +121,7 @@
|
||||
|
||||
#define SPI_NUM_CHIPSELECTS 4
|
||||
|
||||
#define SPI_MAX_DMA_XFER (SZ_64K - 64)
|
||||
#define SPI_MAX_XFER (SZ_64K - 64)
|
||||
|
||||
/* high speed mode is when bus rate is greater then 26MHz */
|
||||
#define SPI_HS_MIN_RATE 26000000
|
||||
@ -147,13 +150,37 @@ struct spi_qup {
|
||||
int n_words;
|
||||
int tx_bytes;
|
||||
int rx_bytes;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
int qup_v1;
|
||||
|
||||
int use_dma;
|
||||
int mode;
|
||||
struct dma_slave_config rx_conf;
|
||||
struct dma_slave_config tx_conf;
|
||||
};
|
||||
|
||||
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer);
|
||||
|
||||
static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
|
||||
{
|
||||
u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
|
||||
return (opflag & flag) != 0;
|
||||
}
|
||||
|
||||
static inline bool spi_qup_is_dma_xfer(int mode)
|
||||
{
|
||||
if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get's the transaction size length */
|
||||
static inline unsigned int spi_qup_len(struct spi_qup *controller)
|
||||
{
|
||||
return controller->n_words * controller->w_size;
|
||||
}
|
||||
|
||||
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
|
||||
{
|
||||
@ -207,29 +234,26 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_qup_fifo_read(struct spi_qup *controller,
|
||||
struct spi_transfer *xfer)
|
||||
static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
|
||||
{
|
||||
u8 *rx_buf = xfer->rx_buf;
|
||||
u32 word, state;
|
||||
int idx, shift, w_size;
|
||||
u8 *rx_buf = controller->rx_buf;
|
||||
int i, shift, num_bytes;
|
||||
u32 word;
|
||||
|
||||
w_size = controller->w_size;
|
||||
|
||||
while (controller->rx_bytes < xfer->len) {
|
||||
|
||||
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||
break;
|
||||
for (; num_words; num_words--) {
|
||||
|
||||
word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
||||
|
||||
num_bytes = min_t(int, spi_qup_len(controller) -
|
||||
controller->rx_bytes,
|
||||
controller->w_size);
|
||||
|
||||
if (!rx_buf) {
|
||||
controller->rx_bytes += w_size;
|
||||
controller->rx_bytes += num_bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
|
||||
for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
|
||||
/*
|
||||
* The data format depends on bytes per SPI word:
|
||||
* 4 bytes: 0x12345678
|
||||
@ -237,38 +261,81 @@ static void spi_qup_fifo_read(struct spi_qup *controller,
|
||||
* 1 byte : 0x00000012
|
||||
*/
|
||||
shift = BITS_PER_BYTE;
|
||||
shift *= (w_size - idx - 1);
|
||||
shift *= (controller->w_size - i - 1);
|
||||
rx_buf[controller->rx_bytes] = word >> shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_qup_fifo_write(struct spi_qup *controller,
|
||||
struct spi_transfer *xfer)
|
||||
static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
|
||||
{
|
||||
const u8 *tx_buf = xfer->tx_buf;
|
||||
u32 word, state, data;
|
||||
int idx, w_size;
|
||||
u32 remainder, words_per_block, num_words;
|
||||
bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
|
||||
w_size = controller->w_size;
|
||||
remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
|
||||
controller->w_size);
|
||||
words_per_block = controller->in_blk_sz >> 2;
|
||||
|
||||
while (controller->tx_bytes < xfer->len) {
|
||||
do {
|
||||
/* ACK by clearing service flag */
|
||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
if (state & QUP_OP_OUT_FIFO_FULL)
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
words_per_block : remainder;
|
||||
} else {
|
||||
if (!spi_qup_is_flag_set(controller,
|
||||
QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||
break;
|
||||
|
||||
num_words = 1;
|
||||
}
|
||||
|
||||
/* read up to the maximum transfer size available */
|
||||
spi_qup_read_from_fifo(controller, num_words);
|
||||
|
||||
remainder -= num_words;
|
||||
|
||||
/* if block mode, check to see if next block is available */
|
||||
if (is_block_mode && !spi_qup_is_flag_set(controller,
|
||||
QUP_OP_IN_BLOCK_READ_REQ))
|
||||
break;
|
||||
|
||||
word = 0;
|
||||
for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
|
||||
} while (remainder);
|
||||
|
||||
if (!tx_buf) {
|
||||
controller->tx_bytes += w_size;
|
||||
break;
|
||||
/*
|
||||
* Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
|
||||
* reads, it has to be cleared again at the very end. However, be sure
|
||||
* to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
|
||||
* present and this is used to determine if transaction is complete
|
||||
*/
|
||||
*opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
}
|
||||
|
||||
static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
|
||||
{
|
||||
const u8 *tx_buf = controller->tx_buf;
|
||||
int i, num_bytes;
|
||||
u32 word, data;
|
||||
|
||||
for (; num_words; num_words--) {
|
||||
word = 0;
|
||||
|
||||
num_bytes = min_t(int, spi_qup_len(controller) -
|
||||
controller->tx_bytes,
|
||||
controller->w_size);
|
||||
if (tx_buf)
|
||||
for (i = 0; i < num_bytes; i++) {
|
||||
data = tx_buf[controller->tx_bytes + i];
|
||||
word |= data << (BITS_PER_BYTE * (3 - i));
|
||||
}
|
||||
|
||||
data = tx_buf[controller->tx_bytes];
|
||||
word |= data << (BITS_PER_BYTE * (3 - idx));
|
||||
}
|
||||
controller->tx_bytes += num_bytes;
|
||||
|
||||
writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
|
||||
}
|
||||
@ -281,31 +348,61 @@ static void spi_qup_dma_done(void *data)
|
||||
complete(&qup->done);
|
||||
}
|
||||
|
||||
static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
|
||||
enum dma_transfer_direction dir,
|
||||
static void spi_qup_write(struct spi_qup *controller)
|
||||
{
|
||||
bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
|
||||
u32 remainder, words_per_block, num_words;
|
||||
|
||||
remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
|
||||
controller->w_size);
|
||||
words_per_block = controller->out_blk_sz >> 2;
|
||||
|
||||
do {
|
||||
/* ACK by clearing service flag */
|
||||
writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
words_per_block : remainder;
|
||||
} else {
|
||||
if (spi_qup_is_flag_set(controller,
|
||||
QUP_OP_OUT_FIFO_FULL))
|
||||
break;
|
||||
|
||||
num_words = 1;
|
||||
}
|
||||
|
||||
spi_qup_write_to_fifo(controller, num_words);
|
||||
|
||||
remainder -= num_words;
|
||||
|
||||
/* if block mode, check to see if next block is available */
|
||||
if (is_block_mode && !spi_qup_is_flag_set(controller,
|
||||
QUP_OP_OUT_BLOCK_WRITE_REQ))
|
||||
break;
|
||||
|
||||
} while (remainder);
|
||||
}
|
||||
|
||||
static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl,
|
||||
unsigned int nents, enum dma_transfer_direction dir,
|
||||
dma_async_tx_callback callback)
|
||||
{
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct scatterlist *sgl;
|
||||
struct dma_chan *chan;
|
||||
dma_cookie_t cookie;
|
||||
unsigned int nents;
|
||||
|
||||
if (dir == DMA_MEM_TO_DEV) {
|
||||
if (dir == DMA_MEM_TO_DEV)
|
||||
chan = master->dma_tx;
|
||||
nents = xfer->tx_sg.nents;
|
||||
sgl = xfer->tx_sg.sgl;
|
||||
} else {
|
||||
else
|
||||
chan = master->dma_rx;
|
||||
nents = xfer->rx_sg.nents;
|
||||
sgl = xfer->rx_sg.sgl;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
if (IS_ERR_OR_NULL(desc))
|
||||
return desc ? PTR_ERR(desc) : -EINVAL;
|
||||
|
||||
desc->callback = callback;
|
||||
desc->callback_param = qup;
|
||||
@ -324,9 +421,33 @@ static void spi_qup_dma_terminate(struct spi_master *master,
|
||||
dmaengine_terminate_all(master->dma_rx);
|
||||
}
|
||||
|
||||
static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
|
||||
static u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max,
|
||||
u32 *nents)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u32 total = 0;
|
||||
|
||||
for (sg = sgl; sg; sg = sg_next(sg)) {
|
||||
unsigned int len = sg_dma_len(sg);
|
||||
|
||||
/* check for overflow as well as limit */
|
||||
if (((total + len) < total) || ((total + len) > max))
|
||||
break;
|
||||
|
||||
total += len;
|
||||
(*nents)++;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
|
||||
struct spi_master *master = spi->master;
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
struct scatterlist *tx_sgl, *rx_sgl;
|
||||
int ret;
|
||||
|
||||
if (xfer->rx_buf)
|
||||
@ -334,43 +455,122 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
|
||||
else if (xfer->tx_buf)
|
||||
tx_done = spi_qup_dma_done;
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
|
||||
rx_sgl = xfer->rx_sg.sgl;
|
||||
tx_sgl = xfer->tx_sg.sgl;
|
||||
|
||||
do {
|
||||
u32 rx_nents = 0, tx_nents = 0;
|
||||
|
||||
if (rx_sgl)
|
||||
qup->n_words = spi_qup_sgl_get_nents_len(rx_sgl,
|
||||
SPI_MAX_XFER, &rx_nents) / qup->w_size;
|
||||
if (tx_sgl)
|
||||
qup->n_words = spi_qup_sgl_get_nents_len(tx_sgl,
|
||||
SPI_MAX_XFER, &tx_nents) / qup->w_size;
|
||||
if (!qup->n_words)
|
||||
return -EIO;
|
||||
|
||||
ret = spi_qup_io_config(spi, xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dma_async_issue_pending(master->dma_rx);
|
||||
}
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
|
||||
if (ret)
|
||||
/* before issuing the descriptors, set the QUP to run */
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
return ret;
|
||||
}
|
||||
if (rx_sgl) {
|
||||
ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
|
||||
DMA_DEV_TO_MEM, rx_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
dma_async_issue_pending(master->dma_rx);
|
||||
}
|
||||
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
}
|
||||
if (tx_sgl) {
|
||||
ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
|
||||
DMA_MEM_TO_DEV, tx_done);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&qup->done, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl))
|
||||
;
|
||||
for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl))
|
||||
;
|
||||
|
||||
} while (rx_sgl || tx_sgl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
|
||||
static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
unsigned long timeout)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
int ret, n_words, iterations, offset = 0;
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
return ret;
|
||||
}
|
||||
n_words = qup->n_words;
|
||||
iterations = n_words / SPI_MAX_XFER; /* round down */
|
||||
qup->rx_buf = xfer->rx_buf;
|
||||
qup->tx_buf = xfer->tx_buf;
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set PAUSE state\n");
|
||||
return ret;
|
||||
}
|
||||
do {
|
||||
if (iterations)
|
||||
qup->n_words = SPI_MAX_XFER;
|
||||
else
|
||||
qup->n_words = n_words % SPI_MAX_XFER;
|
||||
|
||||
spi_qup_fifo_write(qup, xfer);
|
||||
if (qup->tx_buf && offset)
|
||||
qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;
|
||||
|
||||
if (qup->rx_buf && offset)
|
||||
qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;
|
||||
|
||||
/*
|
||||
* if the transaction is small enough, we need
|
||||
* to fallback to FIFO mode
|
||||
*/
|
||||
if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
qup->mode = QUP_IO_M_MODE_FIFO;
|
||||
|
||||
ret = spi_qup_io_config(spi, xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set PAUSE state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qup->mode == QUP_IO_M_MODE_FIFO)
|
||||
spi_qup_write(qup);
|
||||
|
||||
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
|
||||
if (ret) {
|
||||
dev_warn(qup->dev, "cannot set RUN state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&qup->done, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
offset++;
|
||||
} while (iterations--);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -378,29 +578,15 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
|
||||
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_qup *controller = dev_id;
|
||||
struct spi_transfer *xfer;
|
||||
u32 opflags, qup_err, spi_err;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
xfer = controller->xfer;
|
||||
controller->xfer = NULL;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
|
||||
spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
|
||||
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
|
||||
writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
|
||||
writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
|
||||
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||
|
||||
if (!xfer) {
|
||||
dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
|
||||
qup_err, spi_err, opflags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (qup_err) {
|
||||
if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
|
||||
@ -424,54 +610,27 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
if (!controller->use_dma) {
|
||||
if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||
} else {
|
||||
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||
spi_qup_fifo_read(controller, xfer);
|
||||
spi_qup_read(controller, &opflags);
|
||||
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
spi_qup_fifo_write(controller, xfer);
|
||||
spi_qup_write(controller);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
controller->error = error;
|
||||
controller->xfer = xfer;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
if (controller->rx_bytes == xfer->len || error)
|
||||
if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
|
||||
complete(&controller->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u32
|
||||
spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
|
||||
{
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
u32 mode;
|
||||
|
||||
qup->w_size = 4;
|
||||
|
||||
if (xfer->bits_per_word <= 8)
|
||||
qup->w_size = 1;
|
||||
else if (xfer->bits_per_word <= 16)
|
||||
qup->w_size = 2;
|
||||
|
||||
qup->n_words = xfer->len / qup->w_size;
|
||||
|
||||
if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
mode = QUP_IO_M_MODE_FIFO;
|
||||
else
|
||||
mode = QUP_IO_M_MODE_BLOCK;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/* set clock freq ... bits per word */
|
||||
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
/* set clock freq ... bits per word, determine mode */
|
||||
static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||
u32 config, iomode, mode, control;
|
||||
int ret, n_words;
|
||||
int ret;
|
||||
|
||||
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
||||
@ -486,30 +645,59 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
controller->n_words = xfer->len / controller->w_size;
|
||||
|
||||
if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
|
||||
controller->mode = QUP_IO_M_MODE_FIFO;
|
||||
else if (spi->master->can_dma &&
|
||||
spi->master->can_dma(spi->master, spi, xfer) &&
|
||||
spi->master->cur_msg_mapped)
|
||||
controller->mode = QUP_IO_M_MODE_BAM;
|
||||
else
|
||||
controller->mode = QUP_IO_M_MODE_BLOCK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* prep qup for another spi transaction of specific type */
|
||||
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||
u32 config, iomode, control;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
controller->xfer = xfer;
|
||||
controller->error = 0;
|
||||
controller->rx_bytes = 0;
|
||||
controller->tx_bytes = 0;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
|
||||
if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||
dev_err(controller->dev, "cannot set RESET state\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mode = spi_qup_get_mode(spi->master, xfer);
|
||||
n_words = controller->n_words;
|
||||
|
||||
if (mode == QUP_IO_M_MODE_FIFO) {
|
||||
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
||||
switch (controller->mode) {
|
||||
case QUP_IO_M_MODE_FIFO:
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_WRITE_CNT);
|
||||
/* must be zero for FIFO */
|
||||
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
} else if (!controller->use_dma) {
|
||||
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
||||
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
break;
|
||||
case QUP_IO_M_MODE_BAM:
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_INPUT_CNT);
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_OUTPUT_CNT);
|
||||
/* must be zero for BLOCK and BAM */
|
||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||
} else {
|
||||
mode = QUP_IO_M_MODE_BAM;
|
||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||
|
||||
if (!controller->qup_v1) {
|
||||
void __iomem *input_cnt;
|
||||
@ -524,23 +712,38 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
if (xfer->tx_buf)
|
||||
writel_relaxed(0, input_cnt);
|
||||
else
|
||||
writel_relaxed(n_words, input_cnt);
|
||||
writel_relaxed(controller->n_words, input_cnt);
|
||||
|
||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||
}
|
||||
break;
|
||||
case QUP_IO_M_MODE_BLOCK:
|
||||
reinit_completion(&controller->done);
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_INPUT_CNT);
|
||||
writel_relaxed(controller->n_words,
|
||||
controller->base + QUP_MX_OUTPUT_CNT);
|
||||
/* must be zero for BLOCK and BAM */
|
||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||
break;
|
||||
default:
|
||||
dev_err(controller->dev, "unknown mode = %d\n",
|
||||
controller->mode);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
||||
/* Set input and output transfer mode */
|
||||
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
||||
|
||||
if (!controller->use_dma)
|
||||
if (!spi_qup_is_dma_xfer(controller->mode))
|
||||
iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
||||
else
|
||||
iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
|
||||
|
||||
iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
||||
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
||||
iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
||||
iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
||||
|
||||
writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
|
||||
|
||||
@ -581,7 +784,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
config |= xfer->bits_per_word - 1;
|
||||
config |= QUP_CONFIG_SPI_MODE;
|
||||
|
||||
if (controller->use_dma) {
|
||||
if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
if (!xfer->tx_buf)
|
||||
config |= QUP_CONFIG_NO_OUTPUT;
|
||||
if (!xfer->rx_buf)
|
||||
@ -599,7 +802,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
* status change in BAM mode
|
||||
*/
|
||||
|
||||
if (mode == QUP_IO_M_MODE_BAM)
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
|
||||
|
||||
writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
|
||||
@ -616,12 +819,13 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
||||
unsigned long timeout, flags;
|
||||
int ret = -EIO;
|
||||
|
||||
ret = spi_qup_io_config(spi, xfer);
|
||||
ret = spi_qup_io_prep(spi, xfer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
|
||||
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||
timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER,
|
||||
xfer->len) * 8, timeout);
|
||||
timeout = 100 * msecs_to_jiffies(timeout);
|
||||
|
||||
reinit_completion(&controller->done);
|
||||
@ -633,31 +837,22 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
||||
controller->tx_bytes = 0;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
if (controller->use_dma)
|
||||
ret = spi_qup_do_dma(master, xfer);
|
||||
if (spi_qup_is_dma_xfer(controller->mode))
|
||||
ret = spi_qup_do_dma(spi, xfer, timeout);
|
||||
else
|
||||
ret = spi_qup_do_pio(master, xfer);
|
||||
ret = spi_qup_do_pio(spi, xfer, timeout);
|
||||
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&controller->done, timeout))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
exit:
|
||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
controller->xfer = NULL;
|
||||
if (!ret)
|
||||
ret = controller->error;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
if (ret && controller->use_dma)
|
||||
if (ret && spi_qup_is_dma_xfer(controller->mode))
|
||||
spi_qup_dma_terminate(master, xfer);
|
||||
|
||||
return ret;
|
||||
@ -668,26 +863,28 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
{
|
||||
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||
size_t dma_align = dma_get_cache_alignment();
|
||||
u32 mode;
|
||||
int n_words;
|
||||
|
||||
qup->use_dma = 0;
|
||||
if (xfer->rx_buf) {
|
||||
if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
|
||||
IS_ERR_OR_NULL(master->dma_rx))
|
||||
return false;
|
||||
if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
|
||||
IS_ERR_OR_NULL(master->dma_rx) ||
|
||||
!IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
|
||||
if (xfer->tx_buf) {
|
||||
if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
|
||||
IS_ERR_OR_NULL(master->dma_tx))
|
||||
return false;
|
||||
if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
|
||||
return false;
|
||||
}
|
||||
|
||||
n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
|
||||
return false;
|
||||
|
||||
if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
|
||||
IS_ERR_OR_NULL(master->dma_tx) ||
|
||||
!IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
|
||||
return false;
|
||||
|
||||
mode = spi_qup_get_mode(master, xfer);
|
||||
if (mode == QUP_IO_M_MODE_FIFO)
|
||||
return false;
|
||||
|
||||
qup->use_dma = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -750,6 +947,24 @@ err_tx:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spi_qup_set_cs(struct spi_device *spi, bool val)
|
||||
{
|
||||
struct spi_qup *controller;
|
||||
u32 spi_ioc;
|
||||
u32 spi_ioc_orig;
|
||||
|
||||
controller = spi_master_get_devdata(spi->master);
|
||||
spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||
spi_ioc_orig = spi_ioc;
|
||||
if (!val)
|
||||
spi_ioc |= SPI_IO_C_FORCE_CS;
|
||||
else
|
||||
spi_ioc &= ~SPI_IO_C_FORCE_CS;
|
||||
|
||||
if (spi_ioc != spi_ioc_orig)
|
||||
writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
|
||||
}
|
||||
|
||||
static int spi_qup_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
@ -824,7 +1039,7 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->auto_runtime_pm = true;
|
||||
master->dma_alignment = dma_get_cache_alignment();
|
||||
master->max_dma_len = SPI_MAX_DMA_XFER;
|
||||
master->max_dma_len = SPI_MAX_XFER;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
@ -842,9 +1057,10 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||
else if (!ret)
|
||||
master->can_dma = spi_qup_can_dma;
|
||||
|
||||
/* set v1 flag if device is version 1 */
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
|
||||
controller->qup_v1 = 1;
|
||||
controller->qup_v1 = (uintptr_t)of_device_get_match_data(dev);
|
||||
|
||||
if (!controller->qup_v1)
|
||||
master->set_cs = spi_qup_set_cs;
|
||||
|
||||
spin_lock_init(&controller->lock);
|
||||
init_completion(&controller->done);
|
||||
@ -1037,7 +1253,7 @@ static int spi_qup_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id spi_qup_dt_match[] = {
|
||||
{ .compatible = "qcom,spi-qup-v1.1.1", },
|
||||
{ .compatible = "qcom,spi-qup-v1.1.1", .data = (void *)1, },
|
||||
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
||||
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
||||
{ }
|
||||
|
@ -568,7 +568,13 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||
|
||||
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
||||
|
||||
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||
if (rs->n_bytes == 1)
|
||||
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||
else if (rs->n_bytes == 2)
|
||||
writel_relaxed((rs->len / 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||
else
|
||||
writel_relaxed((rs->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||
|
||||
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
|
||||
|
||||
@ -666,7 +672,7 @@ static bool rockchip_spi_can_dma(struct spi_master *master,
|
||||
|
||||
static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
struct rockchip_spi *rs;
|
||||
struct spi_master *master;
|
||||
struct resource *mem;
|
||||
@ -703,13 +709,13 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rs->apb_pclk);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rs->spiclk);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to enable spi_clk\n");
|
||||
goto err_disable_apbclk;
|
||||
}
|
||||
@ -786,7 +792,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register master\n");
|
||||
goto err_free_dma_rx;
|
||||
}
|
||||
@ -816,11 +822,15 @@ static int rockchip_spi_remove(struct platform_device *pdev)
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(rs->spiclk);
|
||||
clk_disable_unprepare(rs->apb_pclk);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
if (rs->dma_tx.ch)
|
||||
dma_release_channel(rs->dma_tx.ch);
|
||||
if (rs->dma_rx.ch)
|
||||
@ -834,43 +844,34 @@ static int rockchip_spi_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_spi_suspend(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||
|
||||
ret = spi_master_suspend(rs->master);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
clk_disable_unprepare(rs->spiclk);
|
||||
clk_disable_unprepare(rs->apb_pclk);
|
||||
}
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_spi_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
ret = clk_prepare_enable(rs->apb_pclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(rs->spiclk);
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(rs->apb_pclk);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_master_resume(rs->master);
|
||||
if (ret < 0) {
|
||||
@ -878,7 +879,7 @@ static int rockchip_spi_resume(struct device *dev)
|
||||
clk_disable_unprepare(rs->apb_pclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
@ -901,14 +902,14 @@ static int rockchip_spi_runtime_resume(struct device *dev)
|
||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||
|
||||
ret = clk_prepare_enable(rs->apb_pclk);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(rs->spiclk);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
clk_disable_unprepare(rs->apb_pclk);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
@ -919,6 +920,7 @@ static const struct dev_pm_ops rockchip_spi_pm = {
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_spi_dt_match[] = {
|
||||
{ .compatible = "rockchip,rv1108-spi", },
|
||||
{ .compatible = "rockchip,rk3036-spi", },
|
||||
{ .compatible = "rockchip,rk3066-spi", },
|
||||
{ .compatible = "rockchip,rk3188-spi", },
|
||||
|
@ -38,6 +38,7 @@ struct sh_msiof_chipdata {
|
||||
u16 tx_fifo_size;
|
||||
u16 rx_fifo_size;
|
||||
u16 master_flags;
|
||||
u16 min_div;
|
||||
};
|
||||
|
||||
struct sh_msiof_spi_priv {
|
||||
@ -49,6 +50,7 @@ struct sh_msiof_spi_priv {
|
||||
struct completion done;
|
||||
unsigned int tx_fifo_size;
|
||||
unsigned int rx_fifo_size;
|
||||
unsigned int min_div;
|
||||
void *tx_dma_page;
|
||||
void *rx_dma_page;
|
||||
dma_addr_t tx_dma_addr;
|
||||
@ -261,6 +263,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
||||
if (!WARN_ON(!spi_hz || !parent_rate))
|
||||
div = DIV_ROUND_UP(parent_rate, spi_hz);
|
||||
|
||||
div = max_t(unsigned long, div, p->min_div);
|
||||
|
||||
for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) {
|
||||
brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div);
|
||||
/* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */
|
||||
@ -998,24 +1002,33 @@ static const struct sh_msiof_chipdata sh_data = {
|
||||
.tx_fifo_size = 64,
|
||||
.rx_fifo_size = 64,
|
||||
.master_flags = 0,
|
||||
.min_div = 1,
|
||||
};
|
||||
|
||||
static const struct sh_msiof_chipdata r8a779x_data = {
|
||||
static const struct sh_msiof_chipdata rcar_gen2_data = {
|
||||
.tx_fifo_size = 64,
|
||||
.rx_fifo_size = 64,
|
||||
.master_flags = SPI_MASTER_MUST_TX,
|
||||
.min_div = 1,
|
||||
};
|
||||
|
||||
static const struct sh_msiof_chipdata rcar_gen3_data = {
|
||||
.tx_fifo_size = 64,
|
||||
.rx_fifo_size = 64,
|
||||
.master_flags = SPI_MASTER_MUST_TX,
|
||||
.min_div = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id sh_msiof_match[] = {
|
||||
{ .compatible = "renesas,sh-mobile-msiof", .data = &sh_data },
|
||||
{ .compatible = "renesas,msiof-r8a7790", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7791", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7792", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7793", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7794", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,rcar-gen2-msiof", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7796", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,rcar-gen3-msiof", .data = &r8a779x_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 },
|
||||
{ .compatible = "renesas,msiof-r8a7793", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7794", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,rcar-gen2-msiof", .data = &rcar_gen2_data },
|
||||
{ .compatible = "renesas,msiof-r8a7796", .data = &rcar_gen3_data },
|
||||
{ .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data },
|
||||
{ .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */
|
||||
{},
|
||||
};
|
||||
@ -1230,6 +1243,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, p);
|
||||
p->master = master;
|
||||
p->info = info;
|
||||
p->min_div = chipdata->min_div;
|
||||
|
||||
init_completion(&p->done);
|
||||
|
||||
|
@ -446,8 +446,8 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "platform_get_irq error\n");
|
||||
return -ENODEV;
|
||||
dev_err(&pdev->dev, "platform_get_irq error: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
|
||||
|
@ -1132,7 +1132,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
goto err_master_put;
|
||||
}
|
||||
|
||||
spi->rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||
spi->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (!IS_ERR(spi->rst)) {
|
||||
reset_control_assert(spi->rst);
|
||||
udelay(2);
|
||||
|
@ -502,7 +502,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
|
||||
init_completion(&sspi->done);
|
||||
|
||||
sspi->rstc = devm_reset_control_get(&pdev->dev, NULL);
|
||||
sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(sspi->rstc)) {
|
||||
dev_err(&pdev->dev, "Couldn't get reset controller\n");
|
||||
ret = PTR_ERR(sspi->rstc);
|
||||
|
@ -1083,7 +1083,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
||||
goto exit_free_irq;
|
||||
}
|
||||
|
||||
tspi->rst = devm_reset_control_get(&pdev->dev, "spi");
|
||||
tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(tspi->rst)) {
|
||||
dev_err(&pdev->dev, "can not get reset\n");
|
||||
ret = PTR_ERR(tspi->rst);
|
||||
|
@ -485,7 +485,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||
goto exit_free_irq;
|
||||
}
|
||||
|
||||
tsd->rst = devm_reset_control_get(&pdev->dev, "spi");
|
||||
tsd->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(tsd->rst)) {
|
||||
dev_err(&pdev->dev, "can not get reset\n");
|
||||
ret = PTR_ERR(tsd->rst);
|
||||
|
@ -1081,7 +1081,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||
goto exit_free_irq;
|
||||
}
|
||||
|
||||
tspi->rst = devm_reset_control_get(&pdev->dev, "spi");
|
||||
tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(tspi->rst)) {
|
||||
dev_err(&pdev->dev, "can not get reset\n");
|
||||
ret = PTR_ERR(tspi->rst);
|
||||
|
@ -393,8 +393,8 @@ static int xlp_spi_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no IRQ resource found\n");
|
||||
return -EINVAL;
|
||||
dev_err(&pdev->dev, "no IRQ resource found: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
err = devm_request_irq(&pdev->dev, irq, xlp_spi_interrupt, 0,
|
||||
pdev->name, xspi);
|
||||
|
@ -40,9 +40,13 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/spi.h>
|
||||
#define SPI_DYN_FIRST_BUS_NUM 0
|
||||
|
||||
static DEFINE_IDR(spi_master_idr);
|
||||
|
||||
static void spidev_release(struct device *dev)
|
||||
{
|
||||
@ -321,8 +325,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
if (rc != -ENODEV)
|
||||
return rc;
|
||||
|
||||
add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
||||
return 0;
|
||||
return add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
||||
}
|
||||
|
||||
struct bus_type spi_bus_type = {
|
||||
@ -421,6 +424,7 @@ static LIST_HEAD(spi_controller_list);
|
||||
/*
|
||||
* Used to protect add/del opertion for board_info list and
|
||||
* spi_controller list, and their matching process
|
||||
* also used to protect object of type struct idr
|
||||
*/
|
||||
static DEFINE_MUTEX(board_lock);
|
||||
|
||||
@ -1533,15 +1537,15 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
int rc;
|
||||
|
||||
/* Mode (clock phase/polarity/etc.) */
|
||||
if (of_find_property(nc, "spi-cpha", NULL))
|
||||
if (of_property_read_bool(nc, "spi-cpha"))
|
||||
spi->mode |= SPI_CPHA;
|
||||
if (of_find_property(nc, "spi-cpol", NULL))
|
||||
if (of_property_read_bool(nc, "spi-cpol"))
|
||||
spi->mode |= SPI_CPOL;
|
||||
if (of_find_property(nc, "spi-cs-high", NULL))
|
||||
if (of_property_read_bool(nc, "spi-cs-high"))
|
||||
spi->mode |= SPI_CS_HIGH;
|
||||
if (of_find_property(nc, "spi-3wire", NULL))
|
||||
if (of_property_read_bool(nc, "spi-3wire"))
|
||||
spi->mode |= SPI_3WIRE;
|
||||
if (of_find_property(nc, "spi-lsb-first", NULL))
|
||||
if (of_property_read_bool(nc, "spi-lsb-first"))
|
||||
spi->mode |= SPI_LSB_FIRST;
|
||||
|
||||
/* Device DUAL/QUAD mode */
|
||||
@ -1583,8 +1587,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
|
||||
if (spi_controller_is_slave(ctlr)) {
|
||||
if (strcmp(nc->name, "slave")) {
|
||||
dev_err(&ctlr->dev, "%s is not called 'slave'\n",
|
||||
nc->full_name);
|
||||
dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
|
||||
nc);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -1593,8 +1597,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
/* Device address */
|
||||
rc = of_property_read_u32(nc, "reg", &value);
|
||||
if (rc) {
|
||||
dev_err(&ctlr->dev, "%s has no valid 'reg' property (%d)\n",
|
||||
nc->full_name, rc);
|
||||
dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
|
||||
nc, rc);
|
||||
return rc;
|
||||
}
|
||||
spi->chip_select = value;
|
||||
@ -1603,8 +1607,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
|
||||
if (rc) {
|
||||
dev_err(&ctlr->dev,
|
||||
"%s has no valid 'spi-max-frequency' property (%d)\n",
|
||||
nc->full_name, rc);
|
||||
"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
|
||||
return rc;
|
||||
}
|
||||
spi->max_speed_hz = value;
|
||||
@ -1621,8 +1624,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
|
||||
/* Alloc an spi_device */
|
||||
spi = spi_alloc_device(ctlr);
|
||||
if (!spi) {
|
||||
dev_err(&ctlr->dev, "spi_device alloc error for %s\n",
|
||||
nc->full_name);
|
||||
dev_err(&ctlr->dev, "spi_device alloc error for %pOF\n", nc);
|
||||
rc = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
@ -1631,8 +1633,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
|
||||
rc = of_modalias_node(nc, spi->modalias,
|
||||
sizeof(spi->modalias));
|
||||
if (rc < 0) {
|
||||
dev_err(&ctlr->dev, "cannot find modalias for %s\n",
|
||||
nc->full_name);
|
||||
dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
@ -1647,8 +1648,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
|
||||
/* Register the new device */
|
||||
rc = spi_add_device(spi);
|
||||
if (rc) {
|
||||
dev_err(&ctlr->dev, "spi_device register error %s\n",
|
||||
nc->full_name);
|
||||
dev_err(&ctlr->dev, "spi_device register error %pOF\n", nc);
|
||||
goto err_of_node_put;
|
||||
}
|
||||
|
||||
@ -1682,8 +1682,7 @@ static void of_register_spi_devices(struct spi_controller *ctlr)
|
||||
spi = of_register_spi_device(ctlr, nc);
|
||||
if (IS_ERR(spi)) {
|
||||
dev_warn(&ctlr->dev,
|
||||
"Failed to create SPI device for %s\n",
|
||||
nc->full_name);
|
||||
"Failed to create SPI device for %pOF\n", nc);
|
||||
of_node_clear_flag(nc, OF_POPULATED);
|
||||
}
|
||||
}
|
||||
@ -2052,11 +2051,10 @@ static int of_spi_register_master(struct spi_controller *ctlr)
|
||||
*/
|
||||
int spi_register_controller(struct spi_controller *ctlr)
|
||||
{
|
||||
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
|
||||
struct device *dev = ctlr->dev.parent;
|
||||
struct boardinfo *bi;
|
||||
int status = -ENODEV;
|
||||
int dynamic = 0;
|
||||
int id;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
@ -2072,19 +2070,28 @@ int spi_register_controller(struct spi_controller *ctlr)
|
||||
*/
|
||||
if (ctlr->num_chipselect == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ctlr->bus_num < 0) && ctlr->dev.of_node)
|
||||
ctlr->bus_num = of_alias_get_id(ctlr->dev.of_node, "spi");
|
||||
|
||||
/* convention: dynamically assigned bus IDs count down from the max */
|
||||
if (ctlr->bus_num < 0) {
|
||||
/* FIXME switch to an IDR based scheme, something like
|
||||
* I2C now uses, so we can't run out of "dynamic" IDs
|
||||
*/
|
||||
ctlr->bus_num = atomic_dec_return(&dyn_bus_id);
|
||||
dynamic = 1;
|
||||
/* allocate dynamic bus number using Linux idr */
|
||||
if ((ctlr->bus_num < 0) && ctlr->dev.of_node) {
|
||||
id = of_alias_get_id(ctlr->dev.of_node, "spi");
|
||||
if (id >= 0) {
|
||||
ctlr->bus_num = id;
|
||||
mutex_lock(&board_lock);
|
||||
id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
|
||||
ctlr->bus_num + 1, GFP_KERNEL);
|
||||
mutex_unlock(&board_lock);
|
||||
if (WARN(id < 0, "couldn't get idr"))
|
||||
return id == -ENOSPC ? -EBUSY : id;
|
||||
}
|
||||
}
|
||||
if (ctlr->bus_num < 0) {
|
||||
mutex_lock(&board_lock);
|
||||
id = idr_alloc(&spi_master_idr, ctlr, SPI_DYN_FIRST_BUS_NUM, 0,
|
||||
GFP_KERNEL);
|
||||
mutex_unlock(&board_lock);
|
||||
if (WARN(id < 0, "couldn't get idr"))
|
||||
return id;
|
||||
ctlr->bus_num = id;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&ctlr->queue);
|
||||
spin_lock_init(&ctlr->queue_lock);
|
||||
spin_lock_init(&ctlr->bus_lock_spinlock);
|
||||
@ -2100,11 +2107,16 @@ int spi_register_controller(struct spi_controller *ctlr)
|
||||
*/
|
||||
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
|
||||
status = device_add(&ctlr->dev);
|
||||
if (status < 0)
|
||||
if (status < 0) {
|
||||
/* free bus id */
|
||||
mutex_lock(&board_lock);
|
||||
idr_remove(&spi_master_idr, ctlr->bus_num);
|
||||
mutex_unlock(&board_lock);
|
||||
goto done;
|
||||
dev_dbg(dev, "registered %s %s%s\n",
|
||||
}
|
||||
dev_dbg(dev, "registered %s %s\n",
|
||||
spi_controller_is_slave(ctlr) ? "slave" : "master",
|
||||
dev_name(&ctlr->dev), dynamic ? " (dynamic)" : "");
|
||||
dev_name(&ctlr->dev));
|
||||
|
||||
/* If we're using a queued driver, start the queue */
|
||||
if (ctlr->transfer)
|
||||
@ -2113,6 +2125,10 @@ int spi_register_controller(struct spi_controller *ctlr)
|
||||
status = spi_controller_initialize_queue(ctlr);
|
||||
if (status) {
|
||||
device_del(&ctlr->dev);
|
||||
/* free bus id */
|
||||
mutex_lock(&board_lock);
|
||||
idr_remove(&spi_master_idr, ctlr->bus_num);
|
||||
mutex_unlock(&board_lock);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@ -2191,19 +2207,33 @@ static int __unregister(struct device *dev, void *null)
|
||||
*/
|
||||
void spi_unregister_controller(struct spi_controller *ctlr)
|
||||
{
|
||||
struct spi_controller *found;
|
||||
int dummy;
|
||||
|
||||
/* First make sure that this controller was ever added */
|
||||
mutex_lock(&board_lock);
|
||||
found = idr_find(&spi_master_idr, ctlr->bus_num);
|
||||
mutex_unlock(&board_lock);
|
||||
if (found != ctlr) {
|
||||
dev_dbg(&ctlr->dev,
|
||||
"attempting to delete unregistered controller [%s]\n",
|
||||
dev_name(&ctlr->dev));
|
||||
return;
|
||||
}
|
||||
if (ctlr->queued) {
|
||||
if (spi_destroy_queue(ctlr))
|
||||
dev_err(&ctlr->dev, "queue remove failed\n");
|
||||
}
|
||||
|
||||
mutex_lock(&board_lock);
|
||||
list_del(&ctlr->list);
|
||||
mutex_unlock(&board_lock);
|
||||
|
||||
dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
|
||||
device_unregister(&ctlr->dev);
|
||||
/* free bus id */
|
||||
mutex_lock(&board_lock);
|
||||
idr_remove(&spi_master_idr, ctlr->bus_num);
|
||||
mutex_unlock(&board_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_unregister_controller);
|
||||
|
||||
@ -3311,8 +3341,8 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
|
||||
put_device(&ctlr->dev);
|
||||
|
||||
if (IS_ERR(spi)) {
|
||||
pr_err("%s: failed to create for '%s'\n",
|
||||
__func__, rd->dn->full_name);
|
||||
pr_err("%s: failed to create for '%pOF'\n",
|
||||
__func__, rd->dn);
|
||||
of_node_clear_flag(rd->dn, OF_POPULATED);
|
||||
return notifier_from_errno(PTR_ERR(spi));
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ kvm_stat: FORCE
|
||||
$(call descend,kvm/$@)
|
||||
|
||||
all: acpi cgroup cpupower gpio hv firewire liblockdep \
|
||||
perf selftests turbostat usb \
|
||||
perf selftests spi turbostat usb \
|
||||
virtio vm net x86_energy_perf_policy \
|
||||
tmon freefall iio objtool kvm_stat
|
||||
|
||||
@ -100,7 +100,7 @@ acpi_install:
|
||||
cpupower_install:
|
||||
$(call descend,power/$(@:_install=),install)
|
||||
|
||||
cgroup_install firewire_install gpio_install hv_install iio_install perf_install usb_install virtio_install vm_install net_install objtool_install:
|
||||
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install:
|
||||
$(call descend,$(@:_install=),install)
|
||||
|
||||
liblockdep_install:
|
||||
|
2
tools/spi/Build
Normal file
2
tools/spi/Build
Normal file
@ -0,0 +1,2 @@
|
||||
spidev_test-y += spidev_test.o
|
||||
spidev_fdx-y += spidev_fdx.o
|
@ -1,6 +1,66 @@
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
include ../scripts/Makefile.include
|
||||
|
||||
all: spidev_test spidev_fdx
|
||||
bindir ?= /usr/bin
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
# Do not use make's built-in rules
|
||||
# (this improves performance and avoids hard-to-debug behaviour);
|
||||
MAKEFLAGS += -r
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
LD = $(CROSS_COMPILE)ld
|
||||
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
||||
|
||||
ALL_TARGETS := spidev_test spidev_fdx
|
||||
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
||||
|
||||
all: $(ALL_PROGRAMS)
|
||||
|
||||
export srctree OUTPUT CC LD CFLAGS
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
#
|
||||
# We need the following to be outside of kernel tree
|
||||
#
|
||||
$(OUTPUT)include/linux/spi/spidev.h: ../../include/uapi/linux/spi/spidev.h
|
||||
mkdir -p $(OUTPUT)include/linux/spi 2>&1 || true
|
||||
ln -sf $(CURDIR)/../../include/uapi/linux/spi/spidev.h $@
|
||||
|
||||
prepare: $(OUTPUT)include/linux/spi/spidev.h
|
||||
|
||||
#
|
||||
# spidev_test
|
||||
#
|
||||
SPIDEV_TEST_IN := $(OUTPUT)spidev_test-in.o
|
||||
$(SPIDEV_TEST_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=spidev_test
|
||||
$(OUTPUT)spidev_test: $(SPIDEV_TEST_IN)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
#
|
||||
# spidev_fdx
|
||||
#
|
||||
SPIDEV_FDX_IN := $(OUTPUT)spidev_fdx-in.o
|
||||
$(SPIDEV_FDX_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=spidev_fdx
|
||||
$(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) spidev_test spidev_fdx
|
||||
rm -f $(ALL_PROGRAMS)
|
||||
rm -f $(OUTPUT)include/linux/spi/spidev.h
|
||||
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
|
||||
|
||||
install: $(ALL_PROGRAMS)
|
||||
install -d -m 755 $(DESTDIR)$(bindir); \
|
||||
for program in $(ALL_PROGRAMS); do \
|
||||
install $$program $(DESTDIR)$(bindir); \
|
||||
done
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: all install clean FORCE prepare
|
||||
|
Loading…
Reference in New Issue
Block a user