mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-14 04:41:26 +00:00
Merge remote-tracking branches 'spi/topic/s3c64xx', 'spi/topic/sc18is602', 'spi/topic/sh-hspi', 'spi/topic/sh-msiof', 'spi/topic/sh-sci', 'spi/topic/sirf' and 'spi/topic/spidev' into spi-next
This commit is contained in:
commit
81235b4ea3
@ -1,7 +1,29 @@
|
|||||||
Renesas HSPI.
|
Renesas HSPI.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,hspi"
|
- compatible : "renesas,hspi-<soctype>", "renesas,hspi" as fallback.
|
||||||
- reg : Offset and length of the register set for the device
|
Examples with soctypes are:
|
||||||
- interrupts : interrupt line used by HSPI
|
- "renesas,hspi-r8a7778" (R-Car M1)
|
||||||
|
- "renesas,hspi-r8a7779" (R-Car H1)
|
||||||
|
- reg : Offset and length of the register set for the device
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device
|
||||||
|
- interrupts : Interrupt specifier
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
hspi0: spi@fffc7000 {
|
||||||
|
compatible = "renesas,hspi-r8a7778", "renesas,hspi";
|
||||||
|
reg = <0xfffc7000 0x18>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 63 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,40 @@
|
|||||||
Renesas MSIOF spi controller
|
Renesas MSIOF spi controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,sh-msiof" for SuperH or
|
- compatible : "renesas,msiof-<soctype>" for SoCs,
|
||||||
"renesas,sh-mobile-msiof" for SH Mobile series
|
"renesas,sh-msiof" for SuperH, or
|
||||||
- reg : Offset and length of the register set for the device
|
"renesas,sh-mobile-msiof" for SH Mobile series.
|
||||||
- interrupts : interrupt line used by MSIOF
|
Examples with soctypes are:
|
||||||
|
"renesas,msiof-r8a7790" (R-Car H2)
|
||||||
|
"renesas,msiof-r8a7791" (R-Car M2)
|
||||||
|
- reg : Offset and length of the register set for the device
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device
|
||||||
|
- interrupts : Interrupt specifier
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- num-cs : total number of chip-selects
|
- clocks : Must contain a reference to the functional clock.
|
||||||
- renesas,tx-fifo-size : Overrides the default tx fifo size given in words
|
- num-cs : Total number of chip-selects (default is 1)
|
||||||
- renesas,rx-fifo-size : Overrides the default rx fifo size given in words
|
|
||||||
|
Optional properties, deprecated for soctype-specific bindings:
|
||||||
|
- renesas,tx-fifo-size : Overrides the default tx fifo size given in words
|
||||||
|
(default is 64)
|
||||||
|
- renesas,rx-fifo-size : Overrides the default rx fifo size given in words
|
||||||
|
(default is 64, or 256 on R-Car H2 and M2)
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
msiof0: spi@e6e20000 {
|
||||||
|
compatible = "renesas,msiof-r8a7791";
|
||||||
|
reg = <0 0xe6e20000 0 0x0064>;
|
||||||
|
interrupts = <0 156 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&mstp0_clks R8A7791_CLK_MSIOF0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
@ -85,6 +85,12 @@ settings for data transfer parameters:
|
|||||||
SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL
|
SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL
|
||||||
(clock polarity, idle high iff this is set) or SPI_CPHA (clock phase,
|
(clock polarity, idle high iff this is set) or SPI_CPHA (clock phase,
|
||||||
sample on trailing edge iff this is set) flags.
|
sample on trailing edge iff this is set) flags.
|
||||||
|
Note that this request is limited to SPI mode flags that fit in a
|
||||||
|
single byte.
|
||||||
|
|
||||||
|
SPI_IOC_RD_MODE32, SPI_IOC_WR_MODE32 ... pass a pointer to a uin32_t
|
||||||
|
which will return (RD) or assign (WR) the full SPI transfer mode,
|
||||||
|
not limited to the bits that fit in one byte.
|
||||||
|
|
||||||
SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ... pass a pointer to a byte
|
SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ... pass a pointer to a byte
|
||||||
which will return (RD) or assign (WR) the bit justification used to
|
which will return (RD) or assign (WR) the bit justification used to
|
||||||
|
@ -78,10 +78,10 @@ static void do_msg(int fd, int len)
|
|||||||
|
|
||||||
static void dumpstat(const char *name, int fd)
|
static void dumpstat(const char *name, int fd)
|
||||||
{
|
{
|
||||||
__u8 mode, lsb, bits;
|
__u8 lsb, bits;
|
||||||
__u32 speed;
|
__u32 mode, speed;
|
||||||
|
|
||||||
if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0) {
|
if (ioctl(fd, SPI_IOC_RD_MODE32, &mode) < 0) {
|
||||||
perror("SPI rd_mode");
|
perror("SPI rd_mode");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ static void dumpstat(const char *name, int fd)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",
|
printf("%s: spi mode 0x%x, %d bits %sper word, %d Hz max\n",
|
||||||
name, mode, bits, lsb ? "(lsb first) " : "", speed);
|
name, mode, bits, lsb ? "(lsb first) " : "", speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ static void pabort(const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *device = "/dev/spidev1.1";
|
static const char *device = "/dev/spidev1.1";
|
||||||
static uint8_t mode;
|
static uint32_t mode;
|
||||||
static uint8_t bits = 8;
|
static uint8_t bits = 8;
|
||||||
static uint32_t speed = 500000;
|
static uint32_t speed = 500000;
|
||||||
static uint16_t delay;
|
static uint16_t delay;
|
||||||
@ -57,6 +57,21 @@ static void transfer(int fd)
|
|||||||
.bits_per_word = bits,
|
.bits_per_word = bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mode & SPI_TX_QUAD)
|
||||||
|
tr.tx_nbits = 4;
|
||||||
|
else if (mode & SPI_TX_DUAL)
|
||||||
|
tr.tx_nbits = 2;
|
||||||
|
if (mode & SPI_RX_QUAD)
|
||||||
|
tr.rx_nbits = 4;
|
||||||
|
else if (mode & SPI_RX_DUAL)
|
||||||
|
tr.rx_nbits = 2;
|
||||||
|
if (!(mode & SPI_LOOP)) {
|
||||||
|
if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
|
||||||
|
tr.rx_buf = 0;
|
||||||
|
else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
|
||||||
|
tr.tx_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
|
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
|
||||||
if (ret < 1)
|
if (ret < 1)
|
||||||
pabort("can't send spi message");
|
pabort("can't send spi message");
|
||||||
@ -81,7 +96,11 @@ static void print_usage(const char *prog)
|
|||||||
" -O --cpol clock polarity\n"
|
" -O --cpol clock polarity\n"
|
||||||
" -L --lsb least significant bit first\n"
|
" -L --lsb least significant bit first\n"
|
||||||
" -C --cs-high chip select active high\n"
|
" -C --cs-high chip select active high\n"
|
||||||
" -3 --3wire SI/SO signals shared\n");
|
" -3 --3wire SI/SO signals shared\n"
|
||||||
|
" -N --no-cs no chip select\n"
|
||||||
|
" -R --ready slave pulls low to pause\n"
|
||||||
|
" -2 --dual dual transfer\n"
|
||||||
|
" -4 --quad quad transfer\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,11 +120,13 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
{ "3wire", 0, 0, '3' },
|
{ "3wire", 0, 0, '3' },
|
||||||
{ "no-cs", 0, 0, 'N' },
|
{ "no-cs", 0, 0, 'N' },
|
||||||
{ "ready", 0, 0, 'R' },
|
{ "ready", 0, 0, 'R' },
|
||||||
|
{ "dual", 0, 0, '2' },
|
||||||
|
{ "quad", 0, 0, '4' },
|
||||||
{ NULL, 0, 0, 0 },
|
{ NULL, 0, 0, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
|
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@ -147,11 +168,23 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
case 'R':
|
case 'R':
|
||||||
mode |= SPI_READY;
|
mode |= SPI_READY;
|
||||||
break;
|
break;
|
||||||
|
case '2':
|
||||||
|
mode |= SPI_TX_DUAL;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
mode |= SPI_TX_QUAD;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mode & SPI_LOOP) {
|
||||||
|
if (mode & SPI_TX_DUAL)
|
||||||
|
mode |= SPI_RX_DUAL;
|
||||||
|
if (mode & SPI_TX_QUAD)
|
||||||
|
mode |= SPI_RX_QUAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -168,11 +201,11 @@ int main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* spi mode
|
* spi mode
|
||||||
*/
|
*/
|
||||||
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
|
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't set spi mode");
|
pabort("can't set spi mode");
|
||||||
|
|
||||||
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
|
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't get spi mode");
|
pabort("can't get spi mode");
|
||||||
|
|
||||||
@ -198,7 +231,7 @@ int main(int argc, char *argv[])
|
|||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't get max speed hz");
|
pabort("can't get max speed hz");
|
||||||
|
|
||||||
printf("spi mode: %d\n", mode);
|
printf("spi mode: 0x%x\n", mode);
|
||||||
printf("bits per word: %d\n", bits);
|
printf("bits per word: %d\n", bits);
|
||||||
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
||||||
|
|
||||||
|
@ -429,7 +429,6 @@ config SPI_SH_MSIOF
|
|||||||
tristate "SuperH MSIOF SPI controller"
|
tristate "SuperH MSIOF SPI controller"
|
||||||
depends on HAVE_CLK
|
depends on HAVE_CLK
|
||||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
select SPI_BITBANG
|
|
||||||
help
|
help
|
||||||
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
||||||
|
|
||||||
|
@ -34,10 +34,6 @@
|
|||||||
|
|
||||||
#include <linux/platform_data/spi-s3c64xx.h>
|
#include <linux/platform_data/spi-s3c64xx.h>
|
||||||
|
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
#include <mach/dma.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_SPI_PORTS 3
|
#define MAX_SPI_PORTS 3
|
||||||
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
|
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
|
||||||
|
|
||||||
@ -200,9 +196,6 @@ struct s3c64xx_spi_driver_data {
|
|||||||
unsigned cur_speed;
|
unsigned cur_speed;
|
||||||
struct s3c64xx_spi_dma_data rx_dma;
|
struct s3c64xx_spi_dma_data rx_dma;
|
||||||
struct s3c64xx_spi_dma_data tx_dma;
|
struct s3c64xx_spi_dma_data tx_dma;
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
struct samsung_dma_ops *ops;
|
|
||||||
#endif
|
|
||||||
struct s3c64xx_spi_port_config *port_conf;
|
struct s3c64xx_spi_port_config *port_conf;
|
||||||
unsigned int port_id;
|
unsigned int port_id;
|
||||||
bool cs_gpio;
|
bool cs_gpio;
|
||||||
@ -284,102 +277,6 @@ static void s3c64xx_spi_dmacb(void *data)
|
|||||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */
|
|
||||||
|
|
||||||
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
|
|
||||||
.name = "samsung-spi-dma",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
|
||||||
unsigned len, dma_addr_t buf)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd;
|
|
||||||
struct samsung_dma_prep info;
|
|
||||||
struct samsung_dma_config config;
|
|
||||||
|
|
||||||
if (dma->direction == DMA_DEV_TO_MEM) {
|
|
||||||
sdd = container_of((void *)dma,
|
|
||||||
struct s3c64xx_spi_driver_data, rx_dma);
|
|
||||||
config.direction = sdd->rx_dma.direction;
|
|
||||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
|
|
||||||
config.width = sdd->cur_bpw / 8;
|
|
||||||
sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config);
|
|
||||||
} else {
|
|
||||||
sdd = container_of((void *)dma,
|
|
||||||
struct s3c64xx_spi_driver_data, tx_dma);
|
|
||||||
config.direction = sdd->tx_dma.direction;
|
|
||||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
|
|
||||||
config.width = sdd->cur_bpw / 8;
|
|
||||||
sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.cap = DMA_SLAVE;
|
|
||||||
info.len = len;
|
|
||||||
info.fp = s3c64xx_spi_dmacb;
|
|
||||||
info.fp_param = dma;
|
|
||||||
info.direction = dma->direction;
|
|
||||||
info.buf = buf;
|
|
||||||
|
|
||||||
sdd->ops->prepare((enum dma_ch)dma->ch, &info);
|
|
||||||
sdd->ops->trigger((enum dma_ch)dma->ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
|
|
||||||
{
|
|
||||||
struct samsung_dma_req req;
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
|
|
||||||
sdd->ops = samsung_dma_get_ops();
|
|
||||||
|
|
||||||
req.cap = DMA_SLAVE;
|
|
||||||
req.client = &s3c64xx_spi_dma_client;
|
|
||||||
|
|
||||||
sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
|
|
||||||
sdd->rx_dma.dmach, &req, dev, "rx");
|
|
||||||
sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
|
|
||||||
sdd->tx_dma.dmach, &req, dev, "tx");
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If DMA resource was not available during
|
|
||||||
* probe, no need to continue with dma requests
|
|
||||||
* else Acquire DMA channels
|
|
||||||
*/
|
|
||||||
while (!is_polling(sdd) && !acquire_dma(sdd))
|
|
||||||
usleep_range(10000, 11000);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
|
||||||
|
|
||||||
/* Free DMA channels */
|
|
||||||
if (!is_polling(sdd)) {
|
|
||||||
sdd->ops->release((enum dma_ch)sdd->rx_dma.ch,
|
|
||||||
&s3c64xx_spi_dma_client);
|
|
||||||
sdd->ops->release((enum dma_ch)sdd->tx_dma.ch,
|
|
||||||
&s3c64xx_spi_dma_client);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct s3c64xx_spi_dma_data *dma)
|
|
||||||
{
|
|
||||||
sdd->ops->stop((enum dma_ch)dma->ch);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||||
struct sg_table *sgt)
|
struct sg_table *sgt)
|
||||||
{
|
{
|
||||||
@ -437,6 +334,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
spi->dma_rx = sdd->rx_dma.ch;
|
||||||
|
|
||||||
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||||
(void *)sdd->tx_dma.dmach, dev, "tx");
|
(void *)sdd->tx_dma.dmach, dev, "tx");
|
||||||
@ -445,6 +343,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out_rx;
|
goto out_rx;
|
||||||
}
|
}
|
||||||
|
spi->dma_tx = sdd->tx_dma.ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
||||||
@ -477,12 +376,14 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
static bool s3c64xx_spi_can_dma(struct spi_master *master,
|
||||||
struct s3c64xx_spi_dma_data *dma)
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
dmaengine_terminate_all(dma->ch);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
@ -515,11 +416,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
||||||
if (dma_mode) {
|
if (dma_mode) {
|
||||||
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
||||||
#ifndef CONFIG_S3C_DMA
|
|
||||||
prepare_dma(&sdd->tx_dma, &xfer->tx_sg);
|
prepare_dma(&sdd->tx_dma, &xfer->tx_sg);
|
||||||
#else
|
|
||||||
prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
switch (sdd->cur_bpw) {
|
switch (sdd->cur_bpw) {
|
||||||
case 32:
|
case 32:
|
||||||
@ -551,11 +448,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
||||||
| S3C64XX_SPI_PACKET_CNT_EN,
|
| S3C64XX_SPI_PACKET_CNT_EN,
|
||||||
regs + S3C64XX_SPI_PACKET_CNT);
|
regs + S3C64XX_SPI_PACKET_CNT);
|
||||||
#ifndef CONFIG_S3C_DMA
|
|
||||||
prepare_dma(&sdd->rx_dma, &xfer->rx_sg);
|
prepare_dma(&sdd->rx_dma, &xfer->rx_sg);
|
||||||
#else
|
|
||||||
prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,81 +657,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
|||||||
|
|
||||||
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
|
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
|
||||||
|
|
||||||
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
struct spi_transfer *xfer;
|
|
||||||
|
|
||||||
if (is_polling(sdd) || msg->is_dma_mapped)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* First mark all xfer unmapped */
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
xfer->rx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map until end or first fail */
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
|
|
||||||
if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xfer->tx_buf != NULL) {
|
|
||||||
xfer->tx_dma = dma_map_single(dev,
|
|
||||||
(void *)xfer->tx_buf, xfer->len,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (dma_mapping_error(dev, xfer->tx_dma)) {
|
|
||||||
dev_err(dev, "dma_map_single Tx failed\n");
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xfer->rx_buf != NULL) {
|
|
||||||
xfer->rx_dma = dma_map_single(dev, xfer->rx_buf,
|
|
||||||
xfer->len, DMA_FROM_DEVICE);
|
|
||||||
if (dma_mapping_error(dev, xfer->rx_dma)) {
|
|
||||||
dev_err(dev, "dma_map_single Rx failed\n");
|
|
||||||
dma_unmap_single(dev, xfer->tx_dma,
|
|
||||||
xfer->len, DMA_TO_DEVICE);
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
xfer->rx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
struct spi_transfer *xfer;
|
|
||||||
|
|
||||||
if (is_polling(sdd) || msg->is_dma_mapped)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
|
|
||||||
if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xfer->rx_buf != NULL
|
|
||||||
&& xfer->rx_dma != XFER_DMAADDR_INVALID)
|
|
||||||
dma_unmap_single(dev, xfer->rx_dma,
|
|
||||||
xfer->len, DMA_FROM_DEVICE);
|
|
||||||
|
|
||||||
if (xfer->tx_buf != NULL
|
|
||||||
&& xfer->tx_dma != XFER_DMAADDR_INVALID)
|
|
||||||
dma_unmap_single(dev, xfer->tx_dma,
|
|
||||||
xfer->len, DMA_TO_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
@ -856,13 +674,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
|||||||
s3c64xx_spi_config(sdd);
|
s3c64xx_spi_config(sdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Map all the transfers if needed */
|
|
||||||
if (s3c64xx_spi_map_mssg(sdd, msg)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"Xfer: Unable to map message buffers!\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure feedback delay */
|
/* Configure feedback delay */
|
||||||
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
|
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
|
||||||
|
|
||||||
@ -886,13 +697,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|||||||
bpw = xfer->bits_per_word;
|
bpw = xfer->bits_per_word;
|
||||||
speed = xfer->speed_hz ? : spi->max_speed_hz;
|
speed = xfer->speed_hz ? : spi->max_speed_hz;
|
||||||
|
|
||||||
if (xfer->len % (bpw / 8)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"Xfer length(%u) not a multiple of word size(%u)\n",
|
|
||||||
xfer->len, bpw / 8);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
|
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
|
||||||
sdd->cur_bpw = bpw;
|
sdd->cur_bpw = bpw;
|
||||||
sdd->cur_speed = speed;
|
sdd->cur_speed = speed;
|
||||||
@ -934,10 +738,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
if (xfer->tx_buf != NULL
|
if (xfer->tx_buf != NULL
|
||||||
&& (sdd->state & TXBUSY))
|
&& (sdd->state & TXBUSY))
|
||||||
s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma);
|
dmaengine_terminate_all(sdd->tx_dma.ch);
|
||||||
if (xfer->rx_buf != NULL
|
if (xfer->rx_buf != NULL
|
||||||
&& (sdd->state & RXBUSY))
|
&& (sdd->state & RXBUSY))
|
||||||
s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma);
|
dmaengine_terminate_all(sdd->rx_dma.ch);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
flush_fifo(sdd);
|
flush_fifo(sdd);
|
||||||
@ -946,16 +750,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s3c64xx_spi_unprepare_message(struct spi_master *master,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
|
||||||
|
|
||||||
s3c64xx_spi_unmap_mssg(sdd, msg);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
||||||
struct spi_device *spi)
|
struct spi_device *spi)
|
||||||
{
|
{
|
||||||
@ -1329,7 +1123,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||||||
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
|
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
|
||||||
master->prepare_message = s3c64xx_spi_prepare_message;
|
master->prepare_message = s3c64xx_spi_prepare_message;
|
||||||
master->transfer_one = s3c64xx_spi_transfer_one;
|
master->transfer_one = s3c64xx_spi_transfer_one;
|
||||||
master->unprepare_message = s3c64xx_spi_unprepare_message;
|
|
||||||
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
||||||
master->num_chipselect = sci->num_cs;
|
master->num_chipselect = sci->num_cs;
|
||||||
master->dma_alignment = 8;
|
master->dma_alignment = 8;
|
||||||
@ -1338,6 +1131,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
if (!is_polling(sdd))
|
||||||
|
master->can_dma = s3c64xx_spi_can_dma;
|
||||||
|
|
||||||
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
|
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||||
if (IS_ERR(sdd->regs)) {
|
if (IS_ERR(sdd->regs)) {
|
||||||
|
@ -183,17 +183,9 @@ static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
|
|||||||
static int sc18is602_check_transfer(struct spi_device *spi,
|
static int sc18is602_check_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *t, int tlen)
|
struct spi_transfer *t, int tlen)
|
||||||
{
|
{
|
||||||
uint32_t hz;
|
|
||||||
|
|
||||||
if (t && t->len + tlen > SC18IS602_BUFSIZ)
|
if (t && t->len + tlen > SC18IS602_BUFSIZ)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hz = spi->max_speed_hz;
|
|
||||||
if (t && t->speed_hz)
|
|
||||||
hz = t->speed_hz;
|
|
||||||
if (hz == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,22 +197,15 @@ static int sc18is602_transfer_one(struct spi_master *master,
|
|||||||
struct spi_transfer *t;
|
struct spi_transfer *t;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
/* SC18IS602 does not support CS2 */
|
|
||||||
if (hw->id == sc18is602 && spi->chip_select == 2) {
|
|
||||||
status = -ENXIO;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
hw->tlen = 0;
|
hw->tlen = 0;
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
u32 hz = t->speed_hz ? : spi->max_speed_hz;
|
|
||||||
bool do_transfer;
|
bool do_transfer;
|
||||||
|
|
||||||
status = sc18is602_check_transfer(spi, t, hw->tlen);
|
status = sc18is602_check_transfer(spi, t, hw->tlen);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
status = sc18is602_setup_transfer(hw, hz, spi->mode);
|
status = sc18is602_setup_transfer(hw, t->speed_hz, spi->mode);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -238,7 +223,6 @@ static int sc18is602_transfer_one(struct spi_master *master,
|
|||||||
if (t->delay_usecs)
|
if (t->delay_usecs)
|
||||||
udelay(t->delay_usecs);
|
udelay(t->delay_usecs);
|
||||||
}
|
}
|
||||||
error:
|
|
||||||
m->status = status;
|
m->status = status;
|
||||||
spi_finalize_current_message(master);
|
spi_finalize_current_message(master);
|
||||||
|
|
||||||
@ -247,10 +231,13 @@ error:
|
|||||||
|
|
||||||
static int sc18is602_setup(struct spi_device *spi)
|
static int sc18is602_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
|
struct sc18is602 *hw = spi_master_get_devdata(spi->master);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return sc18is602_check_transfer(spi, NULL, 0);
|
/* SC18IS602 does not support CS2 */
|
||||||
|
if (hw->id == sc18is602 && spi->chip_select == 2)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sc18is602_probe(struct i2c_client *client,
|
static int sc18is602_probe(struct i2c_client *client,
|
||||||
@ -309,6 +296,8 @@ static int sc18is602_probe(struct i2c_client *client,
|
|||||||
master->setup = sc18is602_setup;
|
master->setup = sc18is602_setup;
|
||||||
master->transfer_one_message = sc18is602_transfer_one;
|
master->transfer_one_message = sc18is602_transfer_one;
|
||||||
master->dev.of_node = np;
|
master->dev.of_node = np;
|
||||||
|
master->min_speed_hz = hw->freq / 128;
|
||||||
|
master->max_speed_hz = hw->freq / 4;
|
||||||
|
|
||||||
error = devm_spi_register_master(dev, master);
|
error = devm_spi_register_master(dev, master);
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -46,8 +46,6 @@
|
|||||||
/* SPSR */
|
/* SPSR */
|
||||||
#define RXFL (1 << 2)
|
#define RXFL (1 << 2)
|
||||||
|
|
||||||
#define hspi2info(h) (h->dev->platform_data)
|
|
||||||
|
|
||||||
struct hspi_priv {
|
struct hspi_priv {
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
@ -113,14 +111,9 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
|||||||
{
|
{
|
||||||
struct spi_device *spi = msg->spi;
|
struct spi_device *spi = msg->spi;
|
||||||
struct device *dev = hspi->dev;
|
struct device *dev = hspi->dev;
|
||||||
u32 target_rate;
|
|
||||||
u32 spcr, idiv_clk;
|
u32 spcr, idiv_clk;
|
||||||
u32 rate, best_rate, min, tmp;
|
u32 rate, best_rate, min, tmp;
|
||||||
|
|
||||||
target_rate = t ? t->speed_hz : 0;
|
|
||||||
if (!target_rate)
|
|
||||||
target_rate = spi->max_speed_hz;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find best IDIV/CLKCx settings
|
* find best IDIV/CLKCx settings
|
||||||
*/
|
*/
|
||||||
@ -140,7 +133,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
|||||||
rate /= (((idiv_clk & 0x1F) + 1) * 2);
|
rate /= (((idiv_clk & 0x1F) + 1) * 2);
|
||||||
|
|
||||||
/* save best settings */
|
/* save best settings */
|
||||||
tmp = abs(target_rate - rate);
|
tmp = abs(t->speed_hz - rate);
|
||||||
if (tmp < min) {
|
if (tmp < min) {
|
||||||
min = tmp;
|
min = tmp;
|
||||||
spcr = idiv_clk;
|
spcr = idiv_clk;
|
||||||
@ -153,7 +146,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
|||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
spcr |= 1 << 6;
|
spcr |= 1 << 6;
|
||||||
|
|
||||||
dev_dbg(dev, "speed %d/%d\n", target_rate, best_rate);
|
dev_dbg(dev, "speed %d/%d\n", t->speed_hz, best_rate);
|
||||||
|
|
||||||
hspi_write(hspi, SPCR, spcr);
|
hspi_write(hspi, SPCR, spcr);
|
||||||
hspi_write(hspi, SPSR, 0x0);
|
hspi_write(hspi, SPSR, 0x0);
|
||||||
@ -230,29 +223,6 @@ static int hspi_transfer_one_message(struct spi_master *master,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hspi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
|
|
||||||
struct device *dev = hspi->dev;
|
|
||||||
|
|
||||||
if (8 != spi->bits_per_word) {
|
|
||||||
dev_err(dev, "bits_per_word should be 8\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(dev, "%s setup\n", spi->modalias);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hspi_cleanup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
|
|
||||||
struct device *dev = hspi->dev;
|
|
||||||
|
|
||||||
dev_dbg(dev, "%s cleanup\n", spi->modalias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hspi_probe(struct platform_device *pdev)
|
static int hspi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
@ -299,20 +269,22 @@ static int hspi_probe(struct platform_device *pdev)
|
|||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
master->setup = hspi_setup;
|
|
||||||
master->cleanup = hspi_cleanup;
|
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->transfer_one_message = hspi_transfer_one_message;
|
master->transfer_one_message = hspi_transfer_one_message;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
goto error1;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error2:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
error1:
|
error1:
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
error0:
|
error0:
|
||||||
|
@ -20,53 +20,103 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/spi/sh_msiof.h>
|
#include <linux/spi/sh_msiof.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
|
||||||
|
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct sh_msiof_chipdata {
|
||||||
|
u16 tx_fifo_size;
|
||||||
|
u16 rx_fifo_size;
|
||||||
|
u16 master_flags;
|
||||||
|
};
|
||||||
|
|
||||||
struct sh_msiof_spi_priv {
|
struct sh_msiof_spi_priv {
|
||||||
struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */
|
|
||||||
void __iomem *mapbase;
|
void __iomem *mapbase;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
const struct sh_msiof_chipdata *chipdata;
|
||||||
struct sh_msiof_spi_info *info;
|
struct sh_msiof_spi_info *info;
|
||||||
struct completion done;
|
struct completion done;
|
||||||
unsigned long flags;
|
|
||||||
int tx_fifo_size;
|
int tx_fifo_size;
|
||||||
int rx_fifo_size;
|
int rx_fifo_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TMDR1 0x00
|
#define TMDR1 0x00 /* Transmit Mode Register 1 */
|
||||||
#define TMDR2 0x04
|
#define TMDR2 0x04 /* Transmit Mode Register 2 */
|
||||||
#define TMDR3 0x08
|
#define TMDR3 0x08 /* Transmit Mode Register 3 */
|
||||||
#define RMDR1 0x10
|
#define RMDR1 0x10 /* Receive Mode Register 1 */
|
||||||
#define RMDR2 0x14
|
#define RMDR2 0x14 /* Receive Mode Register 2 */
|
||||||
#define RMDR3 0x18
|
#define RMDR3 0x18 /* Receive Mode Register 3 */
|
||||||
#define TSCR 0x20
|
#define TSCR 0x20 /* Transmit Clock Select Register */
|
||||||
#define RSCR 0x22
|
#define RSCR 0x22 /* Receive Clock Select Register (SH, A1, APE6) */
|
||||||
#define CTR 0x28
|
#define CTR 0x28 /* Control Register */
|
||||||
#define FCTR 0x30
|
#define FCTR 0x30 /* FIFO Control Register */
|
||||||
#define STR 0x40
|
#define STR 0x40 /* Status Register */
|
||||||
#define IER 0x44
|
#define IER 0x44 /* Interrupt Enable Register */
|
||||||
#define TDR1 0x48
|
#define TDR1 0x48 /* Transmit Control Data Register 1 (SH, A1) */
|
||||||
#define TDR2 0x4c
|
#define TDR2 0x4c /* Transmit Control Data Register 2 (SH, A1) */
|
||||||
#define TFDR 0x50
|
#define TFDR 0x50 /* Transmit FIFO Data Register */
|
||||||
#define RDR1 0x58
|
#define RDR1 0x58 /* Receive Control Data Register 1 (SH, A1) */
|
||||||
#define RDR2 0x5c
|
#define RDR2 0x5c /* Receive Control Data Register 2 (SH, A1) */
|
||||||
#define RFDR 0x60
|
#define RFDR 0x60 /* Receive FIFO Data Register */
|
||||||
|
|
||||||
#define CTR_TSCKE (1 << 15)
|
/* TMDR1 and RMDR1 */
|
||||||
#define CTR_TFSE (1 << 14)
|
#define MDR1_TRMD 0x80000000 /* Transfer Mode (1 = Master mode) */
|
||||||
#define CTR_TXE (1 << 9)
|
#define MDR1_SYNCMD_MASK 0x30000000 /* SYNC Mode */
|
||||||
#define CTR_RXE (1 << 8)
|
#define MDR1_SYNCMD_SPI 0x20000000 /* Level mode/SPI */
|
||||||
|
#define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */
|
||||||
|
#define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */
|
||||||
|
#define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */
|
||||||
|
#define MDR1_FLD_MASK 0x000000c0 /* Frame Sync Signal Interval (0-3) */
|
||||||
|
#define MDR1_FLD_SHIFT 2
|
||||||
|
#define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */
|
||||||
|
/* TMDR1 */
|
||||||
|
#define TMDR1_PCON 0x40000000 /* Transfer Signal Connection */
|
||||||
|
|
||||||
|
/* TMDR2 and RMDR2 */
|
||||||
|
#define MDR2_BITLEN1(i) (((i) - 1) << 24) /* Data Size (8-32 bits) */
|
||||||
|
#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
||||||
|
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||||
|
|
||||||
|
/* TSCR and RSCR */
|
||||||
|
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||||
|
#define SCR_BRPS(i) (((i) - 1) << 8)
|
||||||
|
#define SCR_BRDV_MASK 0x0007 /* Baud Rate Generator's Division Ratio */
|
||||||
|
#define SCR_BRDV_DIV_2 0x0000
|
||||||
|
#define SCR_BRDV_DIV_4 0x0001
|
||||||
|
#define SCR_BRDV_DIV_8 0x0002
|
||||||
|
#define SCR_BRDV_DIV_16 0x0003
|
||||||
|
#define SCR_BRDV_DIV_32 0x0004
|
||||||
|
#define SCR_BRDV_DIV_1 0x0007
|
||||||
|
|
||||||
|
/* CTR */
|
||||||
|
#define CTR_TSCKIZ_MASK 0xc0000000 /* Transmit Clock I/O Polarity Select */
|
||||||
|
#define CTR_TSCKIZ_SCK 0x80000000 /* Disable SCK when TX disabled */
|
||||||
|
#define CTR_TSCKIZ_POL_SHIFT 30 /* Transmit Clock Polarity */
|
||||||
|
#define CTR_RSCKIZ_MASK 0x30000000 /* Receive Clock Polarity Select */
|
||||||
|
#define CTR_RSCKIZ_SCK 0x20000000 /* Must match CTR_TSCKIZ_SCK */
|
||||||
|
#define CTR_RSCKIZ_POL_SHIFT 28 /* Receive Clock Polarity */
|
||||||
|
#define CTR_TEDG_SHIFT 27 /* Transmit Timing (1 = falling edge) */
|
||||||
|
#define CTR_REDG_SHIFT 26 /* Receive Timing (1 = falling edge) */
|
||||||
|
#define CTR_TXDIZ_MASK 0x00c00000 /* Pin Output When TX is Disabled */
|
||||||
|
#define CTR_TXDIZ_LOW 0x00000000 /* 0 */
|
||||||
|
#define CTR_TXDIZ_HIGH 0x00400000 /* 1 */
|
||||||
|
#define CTR_TXDIZ_HIZ 0x00800000 /* High-impedance */
|
||||||
|
#define CTR_TSCKE 0x00008000 /* Transmit Serial Clock Output Enable */
|
||||||
|
#define CTR_TFSE 0x00004000 /* Transmit Frame Sync Signal Output Enable */
|
||||||
|
#define CTR_TXE 0x00000200 /* Transmit Enable */
|
||||||
|
#define CTR_RXE 0x00000100 /* Receive Enable */
|
||||||
|
|
||||||
|
/* STR and IER */
|
||||||
|
#define STR_TEOF 0x00800000 /* Frame Transmission End */
|
||||||
|
#define STR_REOF 0x00000080 /* Frame Reception End */
|
||||||
|
|
||||||
#define STR_TEOF (1 << 23)
|
|
||||||
#define STR_REOF (1 << 7)
|
|
||||||
|
|
||||||
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
||||||
{
|
{
|
||||||
@ -130,22 +180,21 @@ static struct {
|
|||||||
unsigned short div;
|
unsigned short div;
|
||||||
unsigned short scr;
|
unsigned short scr;
|
||||||
} const sh_msiof_spi_clk_table[] = {
|
} const sh_msiof_spi_clk_table[] = {
|
||||||
{ 1, 0x0007 },
|
{ 1, SCR_BRPS( 1) | SCR_BRDV_DIV_1 },
|
||||||
{ 2, 0x0000 },
|
{ 2, SCR_BRPS( 1) | SCR_BRDV_DIV_2 },
|
||||||
{ 4, 0x0001 },
|
{ 4, SCR_BRPS( 1) | SCR_BRDV_DIV_4 },
|
||||||
{ 8, 0x0002 },
|
{ 8, SCR_BRPS( 1) | SCR_BRDV_DIV_8 },
|
||||||
{ 16, 0x0003 },
|
{ 16, SCR_BRPS( 1) | SCR_BRDV_DIV_16 },
|
||||||
{ 32, 0x0004 },
|
{ 32, SCR_BRPS( 1) | SCR_BRDV_DIV_32 },
|
||||||
{ 64, 0x1f00 },
|
{ 64, SCR_BRPS(32) | SCR_BRDV_DIV_2 },
|
||||||
{ 128, 0x1f01 },
|
{ 128, SCR_BRPS(32) | SCR_BRDV_DIV_4 },
|
||||||
{ 256, 0x1f02 },
|
{ 256, SCR_BRPS(32) | SCR_BRDV_DIV_8 },
|
||||||
{ 512, 0x1f03 },
|
{ 512, SCR_BRPS(32) | SCR_BRDV_DIV_16 },
|
||||||
{ 1024, 0x1f04 },
|
{ 1024, SCR_BRPS(32) | SCR_BRDV_DIV_32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
||||||
unsigned long parent_rate,
|
unsigned long parent_rate, u32 spi_hz)
|
||||||
unsigned long spi_hz)
|
|
||||||
{
|
{
|
||||||
unsigned long div = 1024;
|
unsigned long div = 1024;
|
||||||
size_t k;
|
size_t k;
|
||||||
@ -163,7 +212,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
|||||||
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
|
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
|
||||||
|
|
||||||
sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
|
sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
|
||||||
sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
|
if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX))
|
||||||
|
sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
||||||
@ -182,21 +232,25 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
|||||||
*/
|
*/
|
||||||
sh_msiof_write(p, FCTR, 0);
|
sh_msiof_write(p, FCTR, 0);
|
||||||
|
|
||||||
tmp = 0;
|
tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP;
|
||||||
tmp |= !cs_high << 25;
|
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
|
||||||
tmp |= lsb_first << 24;
|
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
|
||||||
sh_msiof_write(p, TMDR1, 0xe0000005 | tmp);
|
sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
|
||||||
sh_msiof_write(p, RMDR1, 0x20000005 | tmp);
|
if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) {
|
||||||
|
/* These bits are reserved if RX needs TX */
|
||||||
|
tmp &= ~0x0000ffff;
|
||||||
|
}
|
||||||
|
sh_msiof_write(p, RMDR1, tmp);
|
||||||
|
|
||||||
tmp = 0xa0000000;
|
tmp = 0;
|
||||||
tmp |= cpol << 30; /* TSCKIZ */
|
tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
|
||||||
tmp |= cpol << 28; /* RSCKIZ */
|
tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
|
||||||
|
|
||||||
edge = cpol ^ !cpha;
|
edge = cpol ^ !cpha;
|
||||||
|
|
||||||
tmp |= edge << 27; /* TEDG */
|
tmp |= edge << CTR_TEDG_SHIFT;
|
||||||
tmp |= edge << 26; /* REDG */
|
tmp |= edge << CTR_REDG_SHIFT;
|
||||||
tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */
|
tmp |= tx_hi_z ? CTR_TXDIZ_HIZ : CTR_TXDIZ_LOW;
|
||||||
sh_msiof_write(p, CTR, tmp);
|
sh_msiof_write(p, CTR, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,12 +258,12 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
|
|||||||
const void *tx_buf, void *rx_buf,
|
const void *tx_buf, void *rx_buf,
|
||||||
u32 bits, u32 words)
|
u32 bits, u32 words)
|
||||||
{
|
{
|
||||||
u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
|
u32 dr2 = MDR2_BITLEN1(bits) | MDR2_WDLEN1(words);
|
||||||
|
|
||||||
if (tx_buf)
|
if (tx_buf || (p->chipdata->master_flags & SPI_MASTER_MUST_TX))
|
||||||
sh_msiof_write(p, TMDR2, dr2);
|
sh_msiof_write(p, TMDR2, dr2);
|
||||||
else
|
else
|
||||||
sh_msiof_write(p, TMDR2, dr2 | 1);
|
sh_msiof_write(p, TMDR2, dr2 | MDR2_GRPMASK1);
|
||||||
|
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
sh_msiof_write(p, RMDR2, dr2);
|
sh_msiof_write(p, RMDR2, dr2);
|
||||||
@ -362,77 +416,45 @@ static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
|
|||||||
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
|
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
|
static int sh_msiof_spi_setup(struct spi_device *spi)
|
||||||
{
|
|
||||||
int bits;
|
|
||||||
|
|
||||||
bits = t ? t->bits_per_word : 0;
|
|
||||||
if (!bits)
|
|
||||||
bits = spi->bits_per_word;
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
|
|
||||||
struct spi_transfer *t)
|
|
||||||
{
|
|
||||||
unsigned long hz;
|
|
||||||
|
|
||||||
hz = t ? t->speed_hz : 0;
|
|
||||||
if (!hz)
|
|
||||||
hz = spi->max_speed_hz;
|
|
||||||
return hz;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_msiof_spi_setup_transfer(struct spi_device *spi,
|
|
||||||
struct spi_transfer *t)
|
|
||||||
{
|
|
||||||
int bits;
|
|
||||||
|
|
||||||
/* noting to check hz values against since parent clock is disabled */
|
|
||||||
|
|
||||||
bits = sh_msiof_spi_bits(spi, t);
|
|
||||||
if (bits < 8)
|
|
||||||
return -EINVAL;
|
|
||||||
if (bits > 32)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return spi_bitbang_setup_transfer(spi, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on)
|
|
||||||
{
|
{
|
||||||
|
struct device_node *np = spi->master->dev.of_node;
|
||||||
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
||||||
int value;
|
|
||||||
|
|
||||||
/* chip select is active low unless SPI_CS_HIGH is set */
|
if (!np) {
|
||||||
if (spi->mode & SPI_CS_HIGH)
|
/*
|
||||||
value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0;
|
* Use spi->controller_data for CS (same strategy as spi_gpio),
|
||||||
else
|
* if any. otherwise let HW control CS
|
||||||
value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1;
|
*/
|
||||||
|
spi->cs_gpio = (uintptr_t)spi->controller_data;
|
||||||
if (is_on == BITBANG_CS_ACTIVE) {
|
|
||||||
if (!test_and_set_bit(0, &p->flags)) {
|
|
||||||
pm_runtime_get_sync(&p->pdev->dev);
|
|
||||||
clk_enable(p->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure pins before asserting CS */
|
|
||||||
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
|
||||||
!!(spi->mode & SPI_CPHA),
|
|
||||||
!!(spi->mode & SPI_3WIRE),
|
|
||||||
!!(spi->mode & SPI_LSB_FIRST),
|
|
||||||
!!(spi->mode & SPI_CS_HIGH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use spi->controller data for CS (same strategy as spi_gpio) */
|
/* Configure pins before deasserting CS */
|
||||||
gpio_set_value((uintptr_t)spi->controller_data, value);
|
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
||||||
|
!!(spi->mode & SPI_CPHA),
|
||||||
|
!!(spi->mode & SPI_3WIRE),
|
||||||
|
!!(spi->mode & SPI_LSB_FIRST),
|
||||||
|
!!(spi->mode & SPI_CS_HIGH));
|
||||||
|
|
||||||
if (is_on == BITBANG_CS_INACTIVE) {
|
if (spi->cs_gpio >= 0)
|
||||||
if (test_and_clear_bit(0, &p->flags)) {
|
gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
|
||||||
clk_disable(p->clk);
|
|
||||||
pm_runtime_put(&p->pdev->dev);
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static int sh_msiof_prepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
||||||
|
const struct spi_device *spi = msg->spi;
|
||||||
|
|
||||||
|
/* Configure pins before asserting CS */
|
||||||
|
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
||||||
|
!!(spi->mode & SPI_CPHA),
|
||||||
|
!!(spi->mode & SPI_3WIRE),
|
||||||
|
!!(spi->mode & SPI_LSB_FIRST),
|
||||||
|
!!(spi->mode & SPI_CS_HIGH));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
||||||
@ -486,7 +508,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
|||||||
/* clear status bits */
|
/* clear status bits */
|
||||||
sh_msiof_reset_str(p);
|
sh_msiof_reset_str(p);
|
||||||
|
|
||||||
/* shut down frame, tx/tx and clock signals */
|
/* shut down frame, rx/tx and clock signals */
|
||||||
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
@ -504,9 +526,11 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
static int sh_msiof_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
||||||
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
||||||
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
||||||
int bits;
|
int bits;
|
||||||
@ -516,7 +540,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
int n;
|
int n;
|
||||||
bool swab;
|
bool swab;
|
||||||
|
|
||||||
bits = sh_msiof_spi_bits(spi, t);
|
bits = t->bits_per_word;
|
||||||
|
|
||||||
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
|
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
|
||||||
bits = 32;
|
bits = 32;
|
||||||
@ -566,8 +590,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* setup clocks (clock already enabled in chipselect()) */
|
/* setup clocks (clock already enabled in chipselect()) */
|
||||||
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk),
|
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
|
||||||
sh_msiof_spi_hz(spi, t));
|
|
||||||
|
|
||||||
/* transfer in fifo sized chunks */
|
/* transfer in fifo sized chunks */
|
||||||
words = t->len / bytes_per_word;
|
words = t->len / bytes_per_word;
|
||||||
@ -587,22 +610,36 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
words -= n;
|
words -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
|
|
||||||
u32 word, u8 bits)
|
|
||||||
{
|
|
||||||
BUG(); /* unused but needed by bitbang code */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct sh_msiof_chipdata sh_data = {
|
||||||
|
.tx_fifo_size = 64,
|
||||||
|
.rx_fifo_size = 64,
|
||||||
|
.master_flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sh_msiof_chipdata r8a779x_data = {
|
||||||
|
.tx_fifo_size = 64,
|
||||||
|
.rx_fifo_size = 256,
|
||||||
|
.master_flags = SPI_MASTER_MUST_TX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id sh_msiof_match[] = {
|
||||||
|
{ .compatible = "renesas,sh-msiof", .data = &sh_data },
|
||||||
|
{ .compatible = "renesas,sh-mobile-msiof", .data = &sh_data },
|
||||||
|
{ .compatible = "renesas,msiof-r8a7790", .data = &r8a779x_data },
|
||||||
|
{ .compatible = "renesas,msiof-r8a7791", .data = &r8a779x_data },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_info *info;
|
struct sh_msiof_spi_info *info;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
u32 num_cs = 0;
|
u32 num_cs = 1;
|
||||||
|
|
||||||
info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL);
|
info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
@ -632,6 +669,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
const struct of_device_id *of_id;
|
||||||
struct sh_msiof_spi_priv *p;
|
struct sh_msiof_spi_priv *p;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
@ -645,10 +683,15 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
p = spi_master_get_devdata(master);
|
p = spi_master_get_devdata(master);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, p);
|
platform_set_drvdata(pdev, p);
|
||||||
if (pdev->dev.of_node)
|
|
||||||
|
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
||||||
|
if (of_id) {
|
||||||
|
p->chipdata = of_id->data;
|
||||||
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
|
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
|
||||||
else
|
} else {
|
||||||
|
p->chipdata = (const void *)pdev->id_entry->driver_data;
|
||||||
p->info = dev_get_platdata(&pdev->dev);
|
p->info = dev_get_platdata(&pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!p->info) {
|
if (!p->info) {
|
||||||
dev_err(&pdev->dev, "failed to obtain device info\n");
|
dev_err(&pdev->dev, "failed to obtain device info\n");
|
||||||
@ -686,49 +729,40 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare(p->clk);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "unable to prepare clock\n");
|
|
||||||
goto err1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->pdev = pdev;
|
p->pdev = pdev;
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
/* The standard version of MSIOF use 64 word FIFOs */
|
|
||||||
p->tx_fifo_size = 64;
|
|
||||||
p->rx_fifo_size = 64;
|
|
||||||
|
|
||||||
/* Platform data may override FIFO sizes */
|
/* Platform data may override FIFO sizes */
|
||||||
|
p->tx_fifo_size = p->chipdata->tx_fifo_size;
|
||||||
|
p->rx_fifo_size = p->chipdata->rx_fifo_size;
|
||||||
if (p->info->tx_fifo_override)
|
if (p->info->tx_fifo_override)
|
||||||
p->tx_fifo_size = p->info->tx_fifo_override;
|
p->tx_fifo_size = p->info->tx_fifo_override;
|
||||||
if (p->info->rx_fifo_override)
|
if (p->info->rx_fifo_override)
|
||||||
p->rx_fifo_size = p->info->rx_fifo_override;
|
p->rx_fifo_size = p->info->rx_fifo_override;
|
||||||
|
|
||||||
/* init master and bitbang code */
|
/* init master code */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
|
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
|
||||||
master->flags = 0;
|
master->flags = p->chipdata->master_flags;
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->num_chipselect = p->info->num_chipselect;
|
master->num_chipselect = p->info->num_chipselect;
|
||||||
master->setup = spi_bitbang_setup;
|
master->setup = sh_msiof_spi_setup;
|
||||||
master->cleanup = spi_bitbang_cleanup;
|
master->prepare_message = sh_msiof_prepare_message;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
master->transfer_one = sh_msiof_transfer_one;
|
||||||
|
|
||||||
p->bitbang.master = master;
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
p->bitbang.chipselect = sh_msiof_spi_chipselect;
|
if (ret < 0) {
|
||||||
p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer;
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
p->bitbang.txrx_bufs = sh_msiof_spi_txrx;
|
goto err2;
|
||||||
p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word;
|
}
|
||||||
p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word;
|
|
||||||
p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word;
|
|
||||||
p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word;
|
|
||||||
|
|
||||||
ret = spi_bitbang_start(&p->bitbang);
|
return 0;
|
||||||
if (ret == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
err2:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_unprepare(p->clk);
|
|
||||||
err1:
|
err1:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
return ret;
|
return ret;
|
||||||
@ -736,30 +770,22 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
int ret;
|
return 0;
|
||||||
|
|
||||||
ret = spi_bitbang_stop(&p->bitbang);
|
|
||||||
if (!ret) {
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
|
||||||
clk_unprepare(p->clk);
|
|
||||||
spi_master_put(p->bitbang.master);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static struct platform_device_id spi_driver_ids[] = {
|
||||||
static const struct of_device_id sh_msiof_match[] = {
|
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
|
||||||
{ .compatible = "renesas,sh-msiof", },
|
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||||
{ .compatible = "renesas,sh-mobile-msiof", },
|
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver sh_msiof_spi_drv = {
|
static struct platform_driver sh_msiof_spi_drv = {
|
||||||
.probe = sh_msiof_spi_probe,
|
.probe = sh_msiof_spi_probe,
|
||||||
.remove = sh_msiof_spi_remove,
|
.remove = sh_msiof_spi_remove,
|
||||||
|
.id_table = spi_driver_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "spi_sh_msiof",
|
.name = "spi_sh_msiof",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
@ -108,7 +108,7 @@ static void sh_sci_spi_chipselect(struct spi_device *dev, int value)
|
|||||||
{
|
{
|
||||||
struct sh_sci_spi *sp = spi_master_get_devdata(dev->master);
|
struct sh_sci_spi *sp = spi_master_get_devdata(dev->master);
|
||||||
|
|
||||||
if (sp->info && sp->info->chip_select)
|
if (sp->info->chip_select)
|
||||||
(sp->info->chip_select)(sp->info, dev->chip_select, value);
|
(sp->info->chip_select)(sp->info, dev->chip_select, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +130,11 @@ static int sh_sci_spi_probe(struct platform_device *dev)
|
|||||||
|
|
||||||
platform_set_drvdata(dev, sp);
|
platform_set_drvdata(dev, sp);
|
||||||
sp->info = dev_get_platdata(&dev->dev);
|
sp->info = dev_get_platdata(&dev->dev);
|
||||||
|
if (!sp->info) {
|
||||||
|
dev_err(&dev->dev, "platform data is missing\n");
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
/* setup spi bitbang adaptor */
|
/* setup spi bitbang adaptor */
|
||||||
sp->bitbang.master = master;
|
sp->bitbang.master = master;
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-direction.h>
|
#include <linux/dma-direction.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/sirfsoc_dma.h>
|
|
||||||
|
|
||||||
#define DRIVER_NAME "sirfsoc_spi"
|
#define DRIVER_NAME "sirfsoc_spi"
|
||||||
|
|
||||||
@ -132,6 +131,8 @@
|
|||||||
#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
|
#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
|
||||||
ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
|
ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
|
||||||
|
|
||||||
|
#define SIRFSOC_MAX_CMD_BYTES 4
|
||||||
|
|
||||||
struct sirfsoc_spi {
|
struct sirfsoc_spi {
|
||||||
struct spi_bitbang bitbang;
|
struct spi_bitbang bitbang;
|
||||||
struct completion rx_done;
|
struct completion rx_done;
|
||||||
@ -162,6 +163,12 @@ struct sirfsoc_spi {
|
|||||||
void *dummypage;
|
void *dummypage;
|
||||||
int word_width; /* in bytes */
|
int word_width; /* in bytes */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if tx size is not more than 4 and rx size is NULL, use
|
||||||
|
* command model
|
||||||
|
*/
|
||||||
|
bool tx_by_cmd;
|
||||||
|
|
||||||
int chipselect[0];
|
int chipselect[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -260,6 +267,12 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
|
|||||||
|
|
||||||
writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
||||||
|
|
||||||
|
if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) {
|
||||||
|
complete(&sspi->tx_done);
|
||||||
|
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
/* Error Conditions */
|
/* Error Conditions */
|
||||||
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
|
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
|
||||||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
|
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
|
||||||
@ -310,6 +323,34 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
|
|
||||||
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fill tx_buf into command register and wait for its completion
|
||||||
|
*/
|
||||||
|
if (sspi->tx_by_cmd) {
|
||||||
|
u32 cmd;
|
||||||
|
memcpy(&cmd, sspi->tx, t->len);
|
||||||
|
|
||||||
|
if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST))
|
||||||
|
cmd = cpu_to_be32(cmd) >>
|
||||||
|
((SIRFSOC_MAX_CMD_BYTES - t->len) * 8);
|
||||||
|
if (sspi->word_width == 2 && t->len == 4 &&
|
||||||
|
(!(spi->mode & SPI_LSB_FIRST)))
|
||||||
|
cmd = ((cmd & 0xffff) << 16) | (cmd >> 16);
|
||||||
|
|
||||||
|
writel(cmd, sspi->base + SIRFSOC_SPI_CMD);
|
||||||
|
writel(SIRFSOC_SPI_FRM_END_INT_EN,
|
||||||
|
sspi->base + SIRFSOC_SPI_INT_EN);
|
||||||
|
writel(SIRFSOC_SPI_CMD_TX_EN,
|
||||||
|
sspi->base + SIRFSOC_SPI_TX_RX_EN);
|
||||||
|
|
||||||
|
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
|
||||||
|
dev_err(&spi->dev, "transfer timeout\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t->len;
|
||||||
|
}
|
||||||
|
|
||||||
if (sspi->left_tx_word == 1) {
|
if (sspi->left_tx_word == 1) {
|
||||||
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
||||||
SIRFSOC_SPI_ENA_AUTO_CLR,
|
SIRFSOC_SPI_ENA_AUTO_CLR,
|
||||||
@ -459,11 +500,6 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
|
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u8;
|
sspi->rx_word = spi_sirfsoc_rx_word_u8;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u8;
|
sspi->tx_word = spi_sirfsoc_tx_word_u8;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
|
|
||||||
sspi->word_width = 1;
|
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
case 16:
|
case 16:
|
||||||
@ -471,26 +507,22 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
SIRFSOC_SPI_TRAN_DAT_FORMAT_16;
|
SIRFSOC_SPI_TRAN_DAT_FORMAT_16;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u16;
|
sspi->rx_word = spi_sirfsoc_rx_word_u16;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u16;
|
sspi->tx_word = spi_sirfsoc_tx_word_u16;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_WORD;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_WORD;
|
|
||||||
sspi->word_width = 2;
|
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
|
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u32;
|
sspi->rx_word = spi_sirfsoc_rx_word_u32;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u32;
|
sspi->tx_word = spi_sirfsoc_tx_word_u32;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
|
|
||||||
sspi->word_width = 4;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
|
||||||
|
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
||||||
|
sspi->word_width;
|
||||||
|
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
||||||
|
sspi->word_width;
|
||||||
|
|
||||||
if (!(spi->mode & SPI_CS_HIGH))
|
if (!(spi->mode & SPI_CS_HIGH))
|
||||||
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
|
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
|
||||||
if (!(spi->mode & SPI_LSB_FIRST))
|
if (!(spi->mode & SPI_LSB_FIRST))
|
||||||
@ -519,6 +551,14 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
|
writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
|
||||||
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
|
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
|
||||||
|
|
||||||
|
if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
|
||||||
|
regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
|
||||||
|
SIRFSOC_SPI_CMD_MODE);
|
||||||
|
sspi->tx_by_cmd = true;
|
||||||
|
} else {
|
||||||
|
regval &= ~SIRFSOC_SPI_CMD_MODE;
|
||||||
|
sspi->tx_by_cmd = false;
|
||||||
|
}
|
||||||
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
||||||
|
|
||||||
if (IS_DMA_VALID(t)) {
|
if (IS_DMA_VALID(t)) {
|
||||||
@ -548,8 +588,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct resource *mem_res;
|
struct resource *mem_res;
|
||||||
int num_cs, cs_gpio, irq;
|
int num_cs, cs_gpio, irq;
|
||||||
u32 rx_dma_ch, tx_dma_ch;
|
|
||||||
dma_cap_mask_t dma_cap_mask;
|
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -560,20 +598,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||||||
goto err_cs;
|
goto err_cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_property_read_u32(pdev->dev.of_node,
|
|
||||||
"sirf,spi-dma-rx-channel", &rx_dma_ch);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "Unable to get rx dma channel\n");
|
|
||||||
goto err_cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = of_property_read_u32(pdev->dev.of_node,
|
|
||||||
"sirf,spi-dma-tx-channel", &tx_dma_ch);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "Unable to get tx dma channel\n");
|
|
||||||
goto err_cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
|
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
|
||||||
if (!master) {
|
if (!master) {
|
||||||
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
||||||
@ -637,18 +661,13 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||||||
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
/* request DMA channels */
|
/* request DMA channels */
|
||||||
dma_cap_zero(dma_cap_mask);
|
sspi->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
||||||
dma_cap_set(DMA_INTERLEAVE, dma_cap_mask);
|
|
||||||
|
|
||||||
sspi->rx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
|
|
||||||
(void *)rx_dma_ch);
|
|
||||||
if (!sspi->rx_chan) {
|
if (!sspi->rx_chan) {
|
||||||
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto free_master;
|
goto free_master;
|
||||||
}
|
}
|
||||||
sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
|
sspi->tx_chan = dma_request_slave_channel(&pdev->dev, "tx");
|
||||||
(void *)tx_dma_ch);
|
|
||||||
if (!sspi->tx_chan) {
|
if (!sspi->tx_chan) {
|
||||||
dev_err(&pdev->dev, "can not allocate tx dma channel\n");
|
dev_err(&pdev->dev, "can not allocate tx dma channel\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
@ -724,11 +743,16 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int spi_sirfsoc_suspend(struct device *dev)
|
static int spi_sirfsoc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
clk_disable(sspi->clk);
|
clk_disable(sspi->clk);
|
||||||
return 0;
|
return 0;
|
||||||
@ -745,15 +769,13 @@ static int spi_sirfsoc_resume(struct device *dev)
|
|||||||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
||||||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
||||||
|
|
||||||
return 0;
|
return spi_master_resume(master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops spi_sirfsoc_pm_ops = {
|
|
||||||
.suspend = spi_sirfsoc_suspend,
|
|
||||||
.resume = spi_sirfsoc_resume,
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend,
|
||||||
|
spi_sirfsoc_resume);
|
||||||
|
|
||||||
static const struct of_device_id spi_sirfsoc_of_match[] = {
|
static const struct of_device_id spi_sirfsoc_of_match[] = {
|
||||||
{ .compatible = "sirf,prima2-spi", },
|
{ .compatible = "sirf,prima2-spi", },
|
||||||
{ .compatible = "sirf,marco-spi", },
|
{ .compatible = "sirf,marco-spi", },
|
||||||
@ -765,9 +787,7 @@ static struct platform_driver spi_sirfsoc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
#ifdef CONFIG_PM
|
|
||||||
.pm = &spi_sirfsoc_pm_ops,
|
.pm = &spi_sirfsoc_pm_ops,
|
||||||
#endif
|
|
||||||
.of_match_table = spi_sirfsoc_of_match,
|
.of_match_table = spi_sirfsoc_of_match,
|
||||||
},
|
},
|
||||||
.probe = spi_sirfsoc_probe,
|
.probe = spi_sirfsoc_probe,
|
||||||
|
@ -73,7 +73,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS);
|
|||||||
*/
|
*/
|
||||||
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
||||||
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
||||||
| SPI_NO_CS | SPI_READY)
|
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
|
||||||
|
| SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)
|
||||||
|
|
||||||
struct spidev_data {
|
struct spidev_data {
|
||||||
dev_t devt;
|
dev_t devt;
|
||||||
@ -265,6 +266,8 @@ static int spidev_message(struct spidev_data *spidev,
|
|||||||
buf += k_tmp->len;
|
buf += k_tmp->len;
|
||||||
|
|
||||||
k_tmp->cs_change = !!u_tmp->cs_change;
|
k_tmp->cs_change = !!u_tmp->cs_change;
|
||||||
|
k_tmp->tx_nbits = u_tmp->tx_nbits;
|
||||||
|
k_tmp->rx_nbits = u_tmp->rx_nbits;
|
||||||
k_tmp->bits_per_word = u_tmp->bits_per_word;
|
k_tmp->bits_per_word = u_tmp->bits_per_word;
|
||||||
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
||||||
k_tmp->speed_hz = u_tmp->speed_hz;
|
k_tmp->speed_hz = u_tmp->speed_hz;
|
||||||
@ -359,6 +362,10 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||||||
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
||||||
(__u8 __user *)arg);
|
(__u8 __user *)arg);
|
||||||
break;
|
break;
|
||||||
|
case SPI_IOC_RD_MODE32:
|
||||||
|
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
||||||
|
(__u32 __user *)arg);
|
||||||
|
break;
|
||||||
case SPI_IOC_RD_LSB_FIRST:
|
case SPI_IOC_RD_LSB_FIRST:
|
||||||
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
|
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
|
||||||
(__u8 __user *)arg);
|
(__u8 __user *)arg);
|
||||||
@ -372,9 +379,13 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||||||
|
|
||||||
/* write requests */
|
/* write requests */
|
||||||
case SPI_IOC_WR_MODE:
|
case SPI_IOC_WR_MODE:
|
||||||
retval = __get_user(tmp, (u8 __user *)arg);
|
case SPI_IOC_WR_MODE32:
|
||||||
|
if (cmd == SPI_IOC_WR_MODE)
|
||||||
|
retval = __get_user(tmp, (u8 __user *)arg);
|
||||||
|
else
|
||||||
|
retval = __get_user(tmp, (u32 __user *)arg);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
u8 save = spi->mode;
|
u32 save = spi->mode;
|
||||||
|
|
||||||
if (tmp & ~SPI_MODE_MASK) {
|
if (tmp & ~SPI_MODE_MASK) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
@ -382,18 +393,18 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmp |= spi->mode & ~SPI_MODE_MASK;
|
tmp |= spi->mode & ~SPI_MODE_MASK;
|
||||||
spi->mode = (u8)tmp;
|
spi->mode = (u16)tmp;
|
||||||
retval = spi_setup(spi);
|
retval = spi_setup(spi);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
spi->mode = save;
|
spi->mode = save;
|
||||||
else
|
else
|
||||||
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
|
dev_dbg(&spi->dev, "spi mode %x\n", tmp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SPI_IOC_WR_LSB_FIRST:
|
case SPI_IOC_WR_LSB_FIRST:
|
||||||
retval = __get_user(tmp, (__u8 __user *)arg);
|
retval = __get_user(tmp, (__u8 __user *)arg);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
u8 save = spi->mode;
|
u32 save = spi->mode;
|
||||||
|
|
||||||
if (tmp)
|
if (tmp)
|
||||||
spi->mode |= SPI_LSB_FIRST;
|
spi->mode |= SPI_LSB_FIRST;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/* linux/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
|
/*
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Samsung Electronics Ltd.
|
* Copyright (C) 2009 Samsung Electronics Ltd.
|
||||||
* Jaswinder Singh <jassi.brar@samsung.com>
|
* Jaswinder Singh <jassi.brar@samsung.com>
|
||||||
*
|
*
|
||||||
@ -8,8 +7,8 @@
|
|||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __S3C64XX_PLAT_SPI_H
|
#ifndef __SPI_S3C64XX_H
|
||||||
#define __S3C64XX_PLAT_SPI_H
|
#define __SPI_S3C64XX_H
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
|
||||||
@ -68,4 +67,4 @@ extern int s3c64xx_spi2_cfg_gpio(void);
|
|||||||
extern struct s3c64xx_spi_info s3c64xx_spi0_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi0_pdata;
|
||||||
extern struct s3c64xx_spi_info s3c64xx_spi1_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi1_pdata;
|
||||||
extern struct s3c64xx_spi_info s3c64xx_spi2_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi2_pdata;
|
||||||
#endif /* __S3C64XX_PLAT_SPI_H */
|
#endif /*__SPI_S3C64XX_H */
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
#define SPI_LOOP 0x20
|
#define SPI_LOOP 0x20
|
||||||
#define SPI_NO_CS 0x40
|
#define SPI_NO_CS 0x40
|
||||||
#define SPI_READY 0x80
|
#define SPI_READY 0x80
|
||||||
|
#define SPI_TX_DUAL 0x100
|
||||||
|
#define SPI_TX_QUAD 0x200
|
||||||
|
#define SPI_RX_DUAL 0x400
|
||||||
|
#define SPI_RX_QUAD 0x800
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
@ -92,7 +96,9 @@ struct spi_ioc_transfer {
|
|||||||
__u16 delay_usecs;
|
__u16 delay_usecs;
|
||||||
__u8 bits_per_word;
|
__u8 bits_per_word;
|
||||||
__u8 cs_change;
|
__u8 cs_change;
|
||||||
__u32 pad;
|
__u8 tx_nbits;
|
||||||
|
__u8 rx_nbits;
|
||||||
|
__u16 pad;
|
||||||
|
|
||||||
/* If the contents of 'struct spi_ioc_transfer' ever change
|
/* If the contents of 'struct spi_ioc_transfer' ever change
|
||||||
* incompatibly, then the ioctl number (currently 0) must change;
|
* incompatibly, then the ioctl number (currently 0) must change;
|
||||||
@ -110,7 +116,7 @@ struct spi_ioc_transfer {
|
|||||||
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
|
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
|
||||||
|
|
||||||
|
|
||||||
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
|
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
|
||||||
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
|
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
|
||||||
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
|
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
|
||||||
|
|
||||||
@ -126,6 +132,10 @@ struct spi_ioc_transfer {
|
|||||||
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
|
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
|
||||||
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
|
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
|
||||||
|
|
||||||
|
/* Read / Write of the SPI mode field */
|
||||||
|
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, __u32)
|
||||||
|
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, __u32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* SPIDEV_H */
|
#endif /* SPIDEV_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user