mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 10:57:46 +00:00
Merge remote-tracking branch 'spi/fix/core' into spi-linus
This commit is contained in:
commit
334c40b17f
39 changed files with 1556 additions and 2746 deletions
|
@ -2,11 +2,21 @@
|
||||||
(CSPI/eCSPI) for i.MX
|
(CSPI/eCSPI) for i.MX
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : Should be "fsl,<soc>-cspi" or "fsl,<soc>-ecspi"
|
- compatible :
|
||||||
|
- "fsl,imx1-cspi" for SPI compatible with the one integrated on i.MX1
|
||||||
|
- "fsl,imx21-cspi" for SPI compatible with the one integrated on i.MX21
|
||||||
|
- "fsl,imx27-cspi" for SPI compatible with the one integrated on i.MX27
|
||||||
|
- "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
|
||||||
- reg : Offset and length of the register set for the device
|
- reg : Offset and length of the register set for the device
|
||||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||||
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
||||||
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
||||||
|
- clocks : Clock specifiers for both ipg and per clocks.
|
||||||
|
- clock-names : Clock names should include both "ipg" and "per"
|
||||||
|
See the clock consumer binding,
|
||||||
|
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||||
Documentation/devicetree/bindings/dma/dma.txt
|
Documentation/devicetree/bindings/dma/dma.txt
|
||||||
- dma-names: DMA request names should include "tx" and "rx" if present.
|
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||||
|
|
|
@ -33,6 +33,11 @@ Optional properties:
|
||||||
nodes. If unspecified, a single SPI device without a chip
|
nodes. If unspecified, a single SPI device without a chip
|
||||||
select can be used.
|
select can be used.
|
||||||
|
|
||||||
|
- dmas: Two DMA channel specifiers following the convention outlined
|
||||||
|
in bindings/dma/dma.txt
|
||||||
|
- dma-names: Names for the dma channels, if present. There must be at
|
||||||
|
least one channel named "tx" for transmit and named "rx" for
|
||||||
|
receive.
|
||||||
|
|
||||||
SPI slave nodes must be children of the SPI master node and can contain
|
SPI slave nodes must be children of the SPI master node and can contain
|
||||||
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||||
|
@ -51,6 +56,9 @@ Example:
|
||||||
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
|
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
|
||||||
clock-names = "core", "iface";
|
clock-names = "core", "iface";
|
||||||
|
|
||||||
|
dmas = <&blsp1_bam 13>, <&blsp1_bam 12>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&spi8_default>;
|
pinctrl-0 = <&spi8_default>;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ Optional property:
|
||||||
in big endian mode, otherwise in native mode(same with CPU), for more
|
in big endian mode, otherwise in native mode(same with CPU), for more
|
||||||
detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
|
detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
|
||||||
|
|
||||||
|
Optional SPI slave node properties:
|
||||||
|
- fsl,spi-cs-sck-delay: a delay in nanoseconds between activating chip
|
||||||
|
select and the start of clock signal, at the start of a transfer.
|
||||||
|
- fsl,spi-sck-cs-delay: a delay in nanoseconds between stopping the clock
|
||||||
|
signal and deactivating chip select, at the end of a transfer.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
dspi0@4002c000 {
|
dspi0@4002c000 {
|
||||||
|
@ -43,6 +49,8 @@ dspi0@4002c000 {
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
linux,modalias = "m25p80";
|
linux,modalias = "m25p80";
|
||||||
modal = "at26df081a";
|
modal = "at26df081a";
|
||||||
|
fsl,spi-cs-sck-delay = <100>;
|
||||||
|
fsl,spi-sck-cs-delay = <50>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ Required properties:
|
||||||
- dma-names: Must include the following entries:
|
- dma-names: Must include the following entries:
|
||||||
- rx
|
- rx
|
||||||
- tx
|
- tx
|
||||||
|
- cs-gpios: Must specify the GPIOs used for chipselect lines.
|
||||||
- #address-cells: Must be 1.
|
- #address-cells: Must be 1.
|
||||||
- #size-cells: Must be 0.
|
- #size-cells: Must be 0.
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ Optional Properties:
|
||||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||||
Documentation/devicetree/bindings/dma/dma.txt
|
Documentation/devicetree/bindings/dma/dma.txt
|
||||||
- dma-names: DMA request names should include "tx" and "rx" if present.
|
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||||
|
- rx-sample-delay-ns: nanoseconds to delay after the SCLK edge before sampling
|
||||||
|
Rx data (may need to be fine tuned for high capacitance lines).
|
||||||
|
No delay (0) by default.
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -33,6 +36,7 @@ Example:
|
||||||
reg = <0xff110000 0x1000>;
|
reg = <0xff110000 0x1000>;
|
||||||
dmas = <&pdma1 11>, <&pdma1 12>;
|
dmas = <&pdma1 11>, <&pdma1 12>;
|
||||||
dma-names = "tx", "rx";
|
dma-names = "tx", "rx";
|
||||||
|
rx-sample-delay-ns = <10>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
|
@ -342,12 +342,11 @@ SPI protocol drivers somewhat resemble platform device drivers:
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "CHIP",
|
.name = "CHIP",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &CHIP_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
||||||
.probe = CHIP_probe,
|
.probe = CHIP_probe,
|
||||||
.remove = CHIP_remove,
|
.remove = CHIP_remove,
|
||||||
.suspend = CHIP_suspend,
|
|
||||||
.resume = CHIP_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
The driver core will automatically attempt to bind this driver to any SPI
|
The driver core will automatically attempt to bind this driver to any SPI
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
@ -34,24 +35,79 @@ 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;
|
||||||
|
static int verbose;
|
||||||
|
|
||||||
static void transfer(int fd)
|
uint8_t default_tx[] = {
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
uint8_t tx[] = {
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
|
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
|
|
||||||
0xF0, 0x0D,
|
0xF0, 0x0D,
|
||||||
};
|
};
|
||||||
uint8_t rx[ARRAY_SIZE(tx)] = {0, };
|
|
||||||
|
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
|
||||||
|
char *input_tx;
|
||||||
|
|
||||||
|
static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
const unsigned char *address = src;
|
||||||
|
const unsigned char *line = address;
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
printf("%s | ", prefix);
|
||||||
|
while (length-- > 0) {
|
||||||
|
printf("%02X ", *address++);
|
||||||
|
if (!(++i % line_size) || (length == 0 && i % line_size)) {
|
||||||
|
if (length == 0) {
|
||||||
|
while (i++ % line_size)
|
||||||
|
printf("__ ");
|
||||||
|
}
|
||||||
|
printf(" | "); /* right close */
|
||||||
|
while (line < address) {
|
||||||
|
c = *line++;
|
||||||
|
printf("%c", (c < 33 || c == 255) ? 0x2E : c);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
if (length > 0)
|
||||||
|
printf("%s | ", prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unescape - process hexadecimal escape character
|
||||||
|
* converts shell input "\x23" -> 0x23
|
||||||
|
*/
|
||||||
|
int unespcape(char *_dst, char *_src, size_t len)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
char *src = _src;
|
||||||
|
char *dst = _dst;
|
||||||
|
unsigned int ch;
|
||||||
|
|
||||||
|
while (*src) {
|
||||||
|
if (*src == '\\' && *(src+1) == 'x') {
|
||||||
|
sscanf(src + 2, "%2x", &ch);
|
||||||
|
src += 4;
|
||||||
|
*dst++ = (unsigned char)ch;
|
||||||
|
} else {
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
struct spi_ioc_transfer tr = {
|
struct spi_ioc_transfer tr = {
|
||||||
.tx_buf = (unsigned long)tx,
|
.tx_buf = (unsigned long)tx,
|
||||||
.rx_buf = (unsigned long)rx,
|
.rx_buf = (unsigned long)rx,
|
||||||
.len = ARRAY_SIZE(tx),
|
.len = len,
|
||||||
.delay_usecs = delay,
|
.delay_usecs = delay,
|
||||||
.speed_hz = speed,
|
.speed_hz = speed,
|
||||||
.bits_per_word = bits,
|
.bits_per_word = bits,
|
||||||
|
@ -76,12 +132,9 @@ static void transfer(int fd)
|
||||||
if (ret < 1)
|
if (ret < 1)
|
||||||
pabort("can't send spi message");
|
pabort("can't send spi message");
|
||||||
|
|
||||||
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
|
if (verbose)
|
||||||
if (!(ret % 6))
|
hex_dump(tx, len, 32, "TX");
|
||||||
puts("");
|
hex_dump(rx, len, 32, "RX");
|
||||||
printf("%.2X ", rx[ret]);
|
|
||||||
}
|
|
||||||
puts("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_usage(const char *prog)
|
static void print_usage(const char *prog)
|
||||||
|
@ -97,6 +150,8 @@ static void print_usage(const char *prog)
|
||||||
" -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"
|
||||||
|
" -v --verbose Verbose (show tx buffer)\n"
|
||||||
|
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
|
||||||
" -N --no-cs no chip select\n"
|
" -N --no-cs no chip select\n"
|
||||||
" -R --ready slave pulls low to pause\n"
|
" -R --ready slave pulls low to pause\n"
|
||||||
" -2 --dual dual transfer\n"
|
" -2 --dual dual transfer\n"
|
||||||
|
@ -121,12 +176,13 @@ static void parse_opts(int argc, char *argv[])
|
||||||
{ "no-cs", 0, 0, 'N' },
|
{ "no-cs", 0, 0, 'N' },
|
||||||
{ "ready", 0, 0, 'R' },
|
{ "ready", 0, 0, 'R' },
|
||||||
{ "dual", 0, 0, '2' },
|
{ "dual", 0, 0, '2' },
|
||||||
|
{ "verbose", 0, 0, 'v' },
|
||||||
{ "quad", 0, 0, '4' },
|
{ "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:lHOLC3NR24", lopts, NULL);
|
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -165,9 +221,15 @@ static void parse_opts(int argc, char *argv[])
|
||||||
case 'N':
|
case 'N':
|
||||||
mode |= SPI_NO_CS;
|
mode |= SPI_NO_CS;
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = 1;
|
||||||
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
mode |= SPI_READY;
|
mode |= SPI_READY;
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
input_tx = optarg;
|
||||||
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
mode |= SPI_TX_DUAL;
|
mode |= SPI_TX_DUAL;
|
||||||
break;
|
break;
|
||||||
|
@ -191,6 +253,9 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int fd;
|
int fd;
|
||||||
|
uint8_t *tx;
|
||||||
|
uint8_t *rx;
|
||||||
|
int size;
|
||||||
|
|
||||||
parse_opts(argc, argv);
|
parse_opts(argc, argv);
|
||||||
|
|
||||||
|
@ -235,7 +300,17 @@ int main(int argc, char *argv[])
|
||||||
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);
|
||||||
|
|
||||||
transfer(fd);
|
if (input_tx) {
|
||||||
|
size = strlen(input_tx+1);
|
||||||
|
tx = malloc(size);
|
||||||
|
rx = malloc(size);
|
||||||
|
size = unespcape((char *)tx, input_tx, size);
|
||||||
|
transfer(fd, tx, rx, size);
|
||||||
|
free(rx);
|
||||||
|
free(tx);
|
||||||
|
} else {
|
||||||
|
transfer(fd, default_tx, default_rx, sizeof(default_tx));
|
||||||
|
}
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
|
|
@ -51,19 +51,6 @@ config INTEL_MIC_X100_DMA
|
||||||
OS and tools for MIC to use with this driver are available from
|
OS and tools for MIC to use with this driver are available from
|
||||||
<http://software.intel.com/en-us/mic-developer>.
|
<http://software.intel.com/en-us/mic-developer>.
|
||||||
|
|
||||||
config INTEL_MID_DMAC
|
|
||||||
tristate "Intel MID DMA support for Peripheral DMA controllers"
|
|
||||||
depends on PCI && X86
|
|
||||||
select DMA_ENGINE
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Enable support for the Intel(R) MID DMA engine present
|
|
||||||
in Intel MID chipsets.
|
|
||||||
|
|
||||||
Say Y here if you have such a chipset.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config ASYNC_TX_ENABLE_CHANNEL_SWITCH
|
config ASYNC_TX_ENABLE_CHANNEL_SWITCH
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
|
||||||
obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
|
obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
|
||||||
obj-$(CONFIG_DMA_OF) += of-dma.o
|
obj-$(CONFIG_DMA_OF) += of-dma.o
|
||||||
|
|
||||||
obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
|
|
||||||
obj-$(CONFIG_DMATEST) += dmatest.o
|
obj-$(CONFIG_DMATEST) += dmatest.o
|
||||||
obj-$(CONFIG_INTEL_IOATDMA) += ioat/
|
obj-$(CONFIG_INTEL_IOATDMA) += ioat/
|
||||||
obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
|
obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,299 +0,0 @@
|
||||||
/*
|
|
||||||
* intel_mid_dma_regs.h - Intel MID DMA Drivers
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008-10 Intel Corp
|
|
||||||
* Author: Vinod Koul <vinod.koul@intel.com>
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef __INTEL_MID_DMAC_REGS_H__
|
|
||||||
#define __INTEL_MID_DMAC_REGS_H__
|
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
|
||||||
#include <linux/dmapool.h>
|
|
||||||
#include <linux/pci_ids.h>
|
|
||||||
|
|
||||||
#define INTEL_MID_DMA_DRIVER_VERSION "1.1.0"
|
|
||||||
|
|
||||||
#define REG_BIT0 0x00000001
|
|
||||||
#define REG_BIT8 0x00000100
|
|
||||||
#define INT_MASK_WE 0x8
|
|
||||||
#define CLEAR_DONE 0xFFFFEFFF
|
|
||||||
#define UNMASK_INTR_REG(chan_num) \
|
|
||||||
((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num))
|
|
||||||
#define MASK_INTR_REG(chan_num) (REG_BIT8 << chan_num)
|
|
||||||
|
|
||||||
#define ENABLE_CHANNEL(chan_num) \
|
|
||||||
((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num))
|
|
||||||
|
|
||||||
#define DISABLE_CHANNEL(chan_num) \
|
|
||||||
(REG_BIT8 << chan_num)
|
|
||||||
|
|
||||||
#define DESCS_PER_CHANNEL 16
|
|
||||||
/*DMA Registers*/
|
|
||||||
/*registers associated with channel programming*/
|
|
||||||
#define DMA_REG_SIZE 0x400
|
|
||||||
#define DMA_CH_SIZE 0x58
|
|
||||||
|
|
||||||
/*CH X REG = (DMA_CH_SIZE)*CH_NO + REG*/
|
|
||||||
#define SAR 0x00 /* Source Address Register*/
|
|
||||||
#define DAR 0x08 /* Destination Address Register*/
|
|
||||||
#define LLP 0x10 /* Linked List Pointer Register*/
|
|
||||||
#define CTL_LOW 0x18 /* Control Register*/
|
|
||||||
#define CTL_HIGH 0x1C /* Control Register*/
|
|
||||||
#define CFG_LOW 0x40 /* Configuration Register Low*/
|
|
||||||
#define CFG_HIGH 0x44 /* Configuration Register high*/
|
|
||||||
|
|
||||||
#define STATUS_TFR 0x2E8
|
|
||||||
#define STATUS_BLOCK 0x2F0
|
|
||||||
#define STATUS_ERR 0x308
|
|
||||||
|
|
||||||
#define RAW_TFR 0x2C0
|
|
||||||
#define RAW_BLOCK 0x2C8
|
|
||||||
#define RAW_ERR 0x2E0
|
|
||||||
|
|
||||||
#define MASK_TFR 0x310
|
|
||||||
#define MASK_BLOCK 0x318
|
|
||||||
#define MASK_SRC_TRAN 0x320
|
|
||||||
#define MASK_DST_TRAN 0x328
|
|
||||||
#define MASK_ERR 0x330
|
|
||||||
|
|
||||||
#define CLEAR_TFR 0x338
|
|
||||||
#define CLEAR_BLOCK 0x340
|
|
||||||
#define CLEAR_SRC_TRAN 0x348
|
|
||||||
#define CLEAR_DST_TRAN 0x350
|
|
||||||
#define CLEAR_ERR 0x358
|
|
||||||
|
|
||||||
#define INTR_STATUS 0x360
|
|
||||||
#define DMA_CFG 0x398
|
|
||||||
#define DMA_CHAN_EN 0x3A0
|
|
||||||
|
|
||||||
/*DMA channel control registers*/
|
|
||||||
union intel_mid_dma_ctl_lo {
|
|
||||||
struct {
|
|
||||||
u32 int_en:1; /*enable or disable interrupts*/
|
|
||||||
/*should be 0*/
|
|
||||||
u32 dst_tr_width:3; /*destination transfer width*/
|
|
||||||
/*usually 32 bits = 010*/
|
|
||||||
u32 src_tr_width:3; /*source transfer width*/
|
|
||||||
/*usually 32 bits = 010*/
|
|
||||||
u32 dinc:2; /*destination address inc/dec*/
|
|
||||||
/*For mem:INC=00, Periphral NoINC=11*/
|
|
||||||
u32 sinc:2; /*source address inc or dec, as above*/
|
|
||||||
u32 dst_msize:3; /*destination burst transaction length*/
|
|
||||||
/*always = 16 ie 011*/
|
|
||||||
u32 src_msize:3; /*source burst transaction length*/
|
|
||||||
/*always = 16 ie 011*/
|
|
||||||
u32 reser1:3;
|
|
||||||
u32 tt_fc:3; /*transfer type and flow controller*/
|
|
||||||
/*M-M = 000
|
|
||||||
P-M = 010
|
|
||||||
M-P = 001*/
|
|
||||||
u32 dms:2; /*destination master select = 0*/
|
|
||||||
u32 sms:2; /*source master select = 0*/
|
|
||||||
u32 llp_dst_en:1; /*enable/disable destination LLP = 0*/
|
|
||||||
u32 llp_src_en:1; /*enable/disable source LLP = 0*/
|
|
||||||
u32 reser2:3;
|
|
||||||
} ctlx;
|
|
||||||
u32 ctl_lo;
|
|
||||||
};
|
|
||||||
|
|
||||||
union intel_mid_dma_ctl_hi {
|
|
||||||
struct {
|
|
||||||
u32 block_ts:12; /*block transfer size*/
|
|
||||||
u32 done:1; /*Done - updated by DMAC*/
|
|
||||||
u32 reser:19; /*configured by DMAC*/
|
|
||||||
} ctlx;
|
|
||||||
u32 ctl_hi;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/*DMA channel configuration registers*/
|
|
||||||
union intel_mid_dma_cfg_lo {
|
|
||||||
struct {
|
|
||||||
u32 reser1:5;
|
|
||||||
u32 ch_prior:3; /*channel priority = 0*/
|
|
||||||
u32 ch_susp:1; /*channel suspend = 0*/
|
|
||||||
u32 fifo_empty:1; /*FIFO empty or not R bit = 0*/
|
|
||||||
u32 hs_sel_dst:1; /*select HW/SW destn handshaking*/
|
|
||||||
/*HW = 0, SW = 1*/
|
|
||||||
u32 hs_sel_src:1; /*select HW/SW src handshaking*/
|
|
||||||
u32 reser2:6;
|
|
||||||
u32 dst_hs_pol:1; /*dest HS interface polarity*/
|
|
||||||
u32 src_hs_pol:1; /*src HS interface polarity*/
|
|
||||||
u32 max_abrst:10; /*max AMBA burst len = 0 (no sw limit*/
|
|
||||||
u32 reload_src:1; /*auto reload src addr =1 if src is P*/
|
|
||||||
u32 reload_dst:1; /*AR destn addr =1 if dstn is P*/
|
|
||||||
} cfgx;
|
|
||||||
u32 cfg_lo;
|
|
||||||
};
|
|
||||||
|
|
||||||
union intel_mid_dma_cfg_hi {
|
|
||||||
struct {
|
|
||||||
u32 fcmode:1; /*flow control mode = 1*/
|
|
||||||
u32 fifo_mode:1; /*FIFO mode select = 1*/
|
|
||||||
u32 protctl:3; /*protection control = 0*/
|
|
||||||
u32 rsvd:2;
|
|
||||||
u32 src_per:4; /*src hw HS interface*/
|
|
||||||
u32 dst_per:4; /*dstn hw HS interface*/
|
|
||||||
u32 reser2:17;
|
|
||||||
} cfgx;
|
|
||||||
u32 cfg_hi;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct intel_mid_dma_chan - internal mid representation of a DMA channel
|
|
||||||
* @chan: dma_chan strcture represetation for mid chan
|
|
||||||
* @ch_regs: MMIO register space pointer to channel register
|
|
||||||
* @dma_base: MMIO register space DMA engine base pointer
|
|
||||||
* @ch_id: DMA channel id
|
|
||||||
* @lock: channel spinlock
|
|
||||||
* @active_list: current active descriptors
|
|
||||||
* @queue: current queued up descriptors
|
|
||||||
* @free_list: current free descriptors
|
|
||||||
* @slave: dma slave structure
|
|
||||||
* @descs_allocated: total number of descriptors allocated
|
|
||||||
* @dma: dma device structure pointer
|
|
||||||
* @busy: bool representing if ch is busy (active txn) or not
|
|
||||||
* @in_use: bool representing if ch is in use or not
|
|
||||||
* @raw_tfr: raw trf interrupt received
|
|
||||||
* @raw_block: raw block interrupt received
|
|
||||||
*/
|
|
||||||
struct intel_mid_dma_chan {
|
|
||||||
struct dma_chan chan;
|
|
||||||
void __iomem *ch_regs;
|
|
||||||
void __iomem *dma_base;
|
|
||||||
int ch_id;
|
|
||||||
spinlock_t lock;
|
|
||||||
struct list_head active_list;
|
|
||||||
struct list_head queue;
|
|
||||||
struct list_head free_list;
|
|
||||||
unsigned int descs_allocated;
|
|
||||||
struct middma_device *dma;
|
|
||||||
bool busy;
|
|
||||||
bool in_use;
|
|
||||||
u32 raw_tfr;
|
|
||||||
u32 raw_block;
|
|
||||||
struct intel_mid_dma_slave *mid_slave;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct intel_mid_dma_chan *to_intel_mid_dma_chan(
|
|
||||||
struct dma_chan *chan)
|
|
||||||
{
|
|
||||||
return container_of(chan, struct intel_mid_dma_chan, chan);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum intel_mid_dma_state {
|
|
||||||
RUNNING = 0,
|
|
||||||
SUSPENDED,
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* struct middma_device - internal representation of a DMA device
|
|
||||||
* @pdev: PCI device
|
|
||||||
* @dma_base: MMIO register space pointer of DMA
|
|
||||||
* @dma_pool: for allocating DMA descriptors
|
|
||||||
* @common: embedded struct dma_device
|
|
||||||
* @tasklet: dma tasklet for processing interrupts
|
|
||||||
* @ch: per channel data
|
|
||||||
* @pci_id: DMA device PCI ID
|
|
||||||
* @intr_mask: Interrupt mask to be used
|
|
||||||
* @mask_reg: MMIO register for periphral mask
|
|
||||||
* @chan_base: Base ch index (read from driver data)
|
|
||||||
* @max_chan: max number of chs supported (from drv_data)
|
|
||||||
* @block_size: Block size of DMA transfer supported (from drv_data)
|
|
||||||
* @pimr_mask: MMIO register addr for periphral interrupt (from drv_data)
|
|
||||||
* @state: dma PM device state
|
|
||||||
*/
|
|
||||||
struct middma_device {
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
void __iomem *dma_base;
|
|
||||||
struct pci_pool *dma_pool;
|
|
||||||
struct dma_device common;
|
|
||||||
struct tasklet_struct tasklet;
|
|
||||||
struct intel_mid_dma_chan ch[MAX_CHAN];
|
|
||||||
unsigned int pci_id;
|
|
||||||
unsigned int intr_mask;
|
|
||||||
void __iomem *mask_reg;
|
|
||||||
int chan_base;
|
|
||||||
int max_chan;
|
|
||||||
int block_size;
|
|
||||||
unsigned int pimr_mask;
|
|
||||||
enum intel_mid_dma_state state;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct middma_device *to_middma_device(struct dma_device *common)
|
|
||||||
{
|
|
||||||
return container_of(common, struct middma_device, common);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct intel_mid_dma_desc {
|
|
||||||
void __iomem *block; /*ch ptr*/
|
|
||||||
struct list_head desc_node;
|
|
||||||
struct dma_async_tx_descriptor txd;
|
|
||||||
size_t len;
|
|
||||||
dma_addr_t sar;
|
|
||||||
dma_addr_t dar;
|
|
||||||
u32 cfg_hi;
|
|
||||||
u32 cfg_lo;
|
|
||||||
u32 ctl_lo;
|
|
||||||
u32 ctl_hi;
|
|
||||||
struct pci_pool *lli_pool;
|
|
||||||
struct intel_mid_dma_lli *lli;
|
|
||||||
dma_addr_t lli_phys;
|
|
||||||
unsigned int lli_length;
|
|
||||||
unsigned int current_lli;
|
|
||||||
dma_addr_t next;
|
|
||||||
enum dma_transfer_direction dirn;
|
|
||||||
enum dma_status status;
|
|
||||||
enum dma_slave_buswidth width; /*width of DMA txn*/
|
|
||||||
enum intel_mid_dma_mode cfg_mode; /*mode configuration*/
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct intel_mid_dma_lli {
|
|
||||||
dma_addr_t sar;
|
|
||||||
dma_addr_t dar;
|
|
||||||
dma_addr_t llp;
|
|
||||||
u32 ctl_lo;
|
|
||||||
u32 ctl_hi;
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
static inline int test_ch_en(void __iomem *dma, u32 ch_no)
|
|
||||||
{
|
|
||||||
u32 en_reg = ioread32(dma + DMA_CHAN_EN);
|
|
||||||
return (en_reg >> ch_no) & 0x1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct intel_mid_dma_desc *to_intel_mid_dma_desc
|
|
||||||
(struct dma_async_tx_descriptor *txd)
|
|
||||||
{
|
|
||||||
return container_of(txd, struct intel_mid_dma_desc, txd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave
|
|
||||||
(struct dma_slave_config *slave)
|
|
||||||
{
|
|
||||||
return container_of(slave, struct intel_mid_dma_slave, dma_slave);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int dma_resume(struct device *dev);
|
|
||||||
|
|
||||||
#endif /*__INTEL_MID_DMAC_REGS_H__*/
|
|
|
@ -159,10 +159,9 @@ config SPI_BUTTERFLY
|
||||||
|
|
||||||
config SPI_CADENCE
|
config SPI_CADENCE
|
||||||
tristate "Cadence SPI controller"
|
tristate "Cadence SPI controller"
|
||||||
depends on ARM
|
|
||||||
help
|
help
|
||||||
This selects the Cadence SPI controller master driver
|
This selects the Cadence SPI controller master driver
|
||||||
used by Xilinx Zynq.
|
used by Xilinx Zynq and ZynqMP.
|
||||||
|
|
||||||
config SPI_CLPS711X
|
config SPI_CLPS711X
|
||||||
tristate "CLPS711X host SPI controller"
|
tristate "CLPS711X host SPI controller"
|
||||||
|
@ -632,7 +631,7 @@ config SPI_DW_PCI
|
||||||
|
|
||||||
config SPI_DW_MID_DMA
|
config SPI_DW_MID_DMA
|
||||||
bool "DMA support for DW SPI controller on Intel MID platform"
|
bool "DMA support for DW SPI controller on Intel MID platform"
|
||||||
depends on SPI_DW_PCI && INTEL_MID_DMAC
|
depends on SPI_DW_PCI && DW_DMAC_PCI
|
||||||
|
|
||||||
config SPI_DW_MMIO
|
config SPI_DW_MMIO
|
||||||
tristate "Memory-mapped io interface driver for DW SPI core"
|
tristate "Memory-mapped io interface driver for DW SPI core"
|
||||||
|
|
|
@ -180,11 +180,17 @@
|
||||||
| SPI_BF(name, value))
|
| SPI_BF(name, value))
|
||||||
|
|
||||||
/* Register access macros */
|
/* Register access macros */
|
||||||
|
#ifdef CONFIG_AVR32
|
||||||
#define spi_readl(port, reg) \
|
#define spi_readl(port, reg) \
|
||||||
__raw_readl((port)->regs + SPI_##reg)
|
__raw_readl((port)->regs + SPI_##reg)
|
||||||
#define spi_writel(port, reg, value) \
|
#define spi_writel(port, reg, value) \
|
||||||
__raw_writel((value), (port)->regs + SPI_##reg)
|
__raw_writel((value), (port)->regs + SPI_##reg)
|
||||||
|
#else
|
||||||
|
#define spi_readl(port, reg) \
|
||||||
|
readl_relaxed((port)->regs + SPI_##reg)
|
||||||
|
#define spi_writel(port, reg, value) \
|
||||||
|
writel_relaxed((value), (port)->regs + SPI_##reg)
|
||||||
|
#endif
|
||||||
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
||||||
* cache operations; better heuristics consider wordsize and bitrate.
|
* cache operations; better heuristics consider wordsize and bitrate.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Chris Boot
|
* Copyright (C) 2012 Chris Boot
|
||||||
* Copyright (C) 2013 Stephen Warren
|
* Copyright (C) 2013 Stephen Warren
|
||||||
|
* Copyright (C) 2015 Martin Sperl
|
||||||
*
|
*
|
||||||
* This driver is inspired by:
|
* This driver is inspired by:
|
||||||
* spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
* spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
@ -66,8 +68,10 @@
|
||||||
#define BCM2835_SPI_CS_CS_10 0x00000002
|
#define BCM2835_SPI_CS_CS_10 0x00000002
|
||||||
#define BCM2835_SPI_CS_CS_01 0x00000001
|
#define BCM2835_SPI_CS_CS_01 0x00000001
|
||||||
|
|
||||||
|
#define BCM2835_SPI_POLLING_LIMIT_US 30
|
||||||
#define BCM2835_SPI_TIMEOUT_MS 30000
|
#define BCM2835_SPI_TIMEOUT_MS 30000
|
||||||
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS)
|
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
|
||||||
|
| SPI_NO_CS | SPI_3WIRE)
|
||||||
|
|
||||||
#define DRV_NAME "spi-bcm2835"
|
#define DRV_NAME "spi-bcm2835"
|
||||||
|
|
||||||
|
@ -75,10 +79,10 @@ struct bcm2835_spi {
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int irq;
|
int irq;
|
||||||
struct completion done;
|
|
||||||
const u8 *tx_buf;
|
const u8 *tx_buf;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
int len;
|
int tx_len;
|
||||||
|
int rx_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
|
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
|
||||||
|
@ -91,205 +95,315 @@ static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val)
|
||||||
writel(val, bs->regs + reg);
|
writel(val, bs->regs + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len)
|
static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs)
|
||||||
{
|
{
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
while (len--) {
|
while ((bs->rx_len) &&
|
||||||
|
(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD)) {
|
||||||
byte = bcm2835_rd(bs, BCM2835_SPI_FIFO);
|
byte = bcm2835_rd(bs, BCM2835_SPI_FIFO);
|
||||||
if (bs->rx_buf)
|
if (bs->rx_buf)
|
||||||
*bs->rx_buf++ = byte;
|
*bs->rx_buf++ = byte;
|
||||||
|
bs->rx_len--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len)
|
static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs)
|
||||||
{
|
{
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
if (len > bs->len)
|
while ((bs->tx_len) &&
|
||||||
len = bs->len;
|
(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_TXD)) {
|
||||||
|
|
||||||
while (len--) {
|
|
||||||
byte = bs->tx_buf ? *bs->tx_buf++ : 0;
|
byte = bs->tx_buf ? *bs->tx_buf++ : 0;
|
||||||
bcm2835_wr(bs, BCM2835_SPI_FIFO, byte);
|
bcm2835_wr(bs, BCM2835_SPI_FIFO, byte);
|
||||||
bs->len--;
|
bs->tx_len--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bcm2835_spi_reset_hw(struct spi_master *master)
|
||||||
|
{
|
||||||
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
|
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||||
|
|
||||||
|
/* Disable SPI interrupts and transfer */
|
||||||
|
cs &= ~(BCM2835_SPI_CS_INTR |
|
||||||
|
BCM2835_SPI_CS_INTD |
|
||||||
|
BCM2835_SPI_CS_TA);
|
||||||
|
/* and reset RX/TX FIFOS */
|
||||||
|
cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
|
||||||
|
|
||||||
|
/* and reset the SPI_HW */
|
||||||
|
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_id;
|
struct spi_master *master = dev_id;
|
||||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
|
||||||
|
|
||||||
/*
|
/* Read as many bytes as possible from FIFO */
|
||||||
* RXR - RX needs Reading. This means 12 (or more) bytes have been
|
bcm2835_rd_fifo(bs);
|
||||||
* transmitted and hence 12 (or more) bytes have been received.
|
/* Write as many bytes as possible to FIFO */
|
||||||
*
|
bcm2835_wr_fifo(bs);
|
||||||
* The FIFO is 16-bytes deep. We check for this interrupt to keep the
|
|
||||||
* FIFO full; we have a 4-byte-time buffer for IRQ latency. We check
|
|
||||||
* this before DONE (TX empty) just in case we delayed processing this
|
|
||||||
* interrupt for some reason.
|
|
||||||
*
|
|
||||||
* We only check for this case if we have more bytes to TX; at the end
|
|
||||||
* of the transfer, we ignore this pipelining optimization, and let
|
|
||||||
* bcm2835_spi_finish_transfer() drain the RX FIFO.
|
|
||||||
*/
|
|
||||||
if (bs->len && (cs & BCM2835_SPI_CS_RXR)) {
|
|
||||||
/* Read 12 bytes of data */
|
|
||||||
bcm2835_rd_fifo(bs, 12);
|
|
||||||
|
|
||||||
/* Write up to 12 bytes */
|
/* based on flags decide if we can finish the transfer */
|
||||||
bcm2835_wr_fifo(bs, 12);
|
if (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) {
|
||||||
|
/* Transfer complete - reset SPI HW */
|
||||||
/*
|
bcm2835_spi_reset_hw(master);
|
||||||
* We must have written something to the TX FIFO due to the
|
/* wake up the framework */
|
||||||
* bs->len check above, so cannot be DONE. Hence, return
|
complete(&master->xfer_completion);
|
||||||
* early. Note that DONE could also be set if we serviced an
|
|
||||||
* RXR interrupt really late.
|
|
||||||
*/
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DONE - TX empty. This occurs when we first enable the transfer
|
|
||||||
* since we do not pre-fill the TX FIFO. At any other time, given that
|
|
||||||
* we refill the TX FIFO above based on RXR, and hence ignore DONE if
|
|
||||||
* RXR is set, DONE really does mean end-of-transfer.
|
|
||||||
*/
|
|
||||||
if (cs & BCM2835_SPI_CS_DONE) {
|
|
||||||
if (bs->len) { /* First interrupt in a transfer */
|
|
||||||
bcm2835_wr_fifo(bs, 16);
|
|
||||||
} else { /* Transfer complete */
|
|
||||||
/* Disable SPI interrupts */
|
|
||||||
cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD);
|
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wake up bcm2835_spi_transfer_one(), which will call
|
|
||||||
* bcm2835_spi_finish_transfer(), to drain the RX FIFO.
|
|
||||||
*/
|
|
||||||
complete(&bs->done);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_NONE;
|
static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
|
||||||
}
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *tfr,
|
||||||
static int bcm2835_spi_start_transfer(struct spi_device *spi,
|
u32 cs,
|
||||||
struct spi_transfer *tfr)
|
unsigned long xfer_time_us)
|
||||||
{
|
{
|
||||||
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
unsigned long spi_hz, clk_hz, cdiv;
|
unsigned long timeout = jiffies +
|
||||||
u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA;
|
max(4 * xfer_time_us * HZ / 1000000, 2uL);
|
||||||
|
|
||||||
spi_hz = tfr->speed_hz;
|
/* enable HW block without interrupts */
|
||||||
clk_hz = clk_get_rate(bs->clk);
|
bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
|
||||||
|
|
||||||
if (spi_hz >= clk_hz / 2) {
|
/* set timeout to 4x the expected time, or 2 jiffies */
|
||||||
cdiv = 2; /* clk_hz/2 is the fastest we can go */
|
/* loop until finished the transfer */
|
||||||
} else if (spi_hz) {
|
while (bs->rx_len) {
|
||||||
/* CDIV must be a power of two */
|
/* read from fifo as much as possible */
|
||||||
cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz));
|
bcm2835_rd_fifo(bs);
|
||||||
|
/* fill in tx fifo as much as possible */
|
||||||
if (cdiv >= 65536)
|
bcm2835_wr_fifo(bs);
|
||||||
cdiv = 0; /* 0 is the slowest we can go */
|
/* if we still expect some data after the read,
|
||||||
} else
|
* check for a possible timeout
|
||||||
cdiv = 0; /* 0 is the slowest we can go */
|
*/
|
||||||
|
if (bs->rx_len && time_after(jiffies, timeout)) {
|
||||||
if (spi->mode & SPI_CPOL)
|
/* Transfer complete - reset SPI HW */
|
||||||
cs |= BCM2835_SPI_CS_CPOL;
|
bcm2835_spi_reset_hw(master);
|
||||||
if (spi->mode & SPI_CPHA)
|
/* and return timeout */
|
||||||
cs |= BCM2835_SPI_CS_CPHA;
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
if (!(spi->mode & SPI_NO_CS)) {
|
|
||||||
if (spi->mode & SPI_CS_HIGH) {
|
|
||||||
cs |= BCM2835_SPI_CS_CSPOL;
|
|
||||||
cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cs |= spi->chip_select;
|
/* Transfer complete - reset SPI HW */
|
||||||
|
bcm2835_spi_reset_hw(master);
|
||||||
|
/* and return without waiting for completion */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
reinit_completion(&bs->done);
|
static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
|
||||||
bs->tx_buf = tfr->tx_buf;
|
struct spi_device *spi,
|
||||||
bs->rx_buf = tfr->rx_buf;
|
struct spi_transfer *tfr,
|
||||||
bs->len = tfr->len;
|
u32 cs)
|
||||||
|
{
|
||||||
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
/* fill in fifo if we have gpio-cs
|
||||||
|
* note that there have been rare events where the native-CS
|
||||||
|
* flapped for <1us which may change the behaviour
|
||||||
|
* with gpio-cs this does not happen, so it is implemented
|
||||||
|
* only for this case
|
||||||
|
*/
|
||||||
|
if (gpio_is_valid(spi->cs_gpio)) {
|
||||||
|
/* enable HW block, but without interrupts enabled
|
||||||
|
* this would triggern an immediate interrupt
|
||||||
|
*/
|
||||||
|
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||||
|
cs | BCM2835_SPI_CS_TA);
|
||||||
|
/* fill in tx fifo as much as possible */
|
||||||
|
bcm2835_wr_fifo(bs);
|
||||||
|
}
|
||||||
|
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
|
||||||
/*
|
/*
|
||||||
* Enable the HW block. This will immediately trigger a DONE (TX
|
* Enable the HW block. This will immediately trigger a DONE (TX
|
||||||
* empty) interrupt, upon which we will fill the TX FIFO with the
|
* empty) interrupt, upon which we will fill the TX FIFO with the
|
||||||
* first TX bytes. Pre-filling the TX FIFO here to avoid the
|
* first TX bytes. Pre-filling the TX FIFO here to avoid the
|
||||||
* interrupt doesn't work:-(
|
* interrupt doesn't work:-(
|
||||||
*/
|
*/
|
||||||
|
cs |= BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA;
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||||
|
|
||||||
return 0;
|
/* signal that we need to wait for completion */
|
||||||
}
|
return 1;
|
||||||
|
|
||||||
static int bcm2835_spi_finish_transfer(struct spi_device *spi,
|
|
||||||
struct spi_transfer *tfr, bool cs_change)
|
|
||||||
{
|
|
||||||
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
|
|
||||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
|
||||||
|
|
||||||
/* Drain RX FIFO */
|
|
||||||
while (cs & BCM2835_SPI_CS_RXD) {
|
|
||||||
bcm2835_rd_fifo(bs, 1);
|
|
||||||
cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tfr->delay_usecs)
|
|
||||||
udelay(tfr->delay_usecs);
|
|
||||||
|
|
||||||
if (cs_change)
|
|
||||||
/* Clear TA flag */
|
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm2835_spi_transfer_one(struct spi_master *master,
|
static int bcm2835_spi_transfer_one(struct spi_master *master,
|
||||||
struct spi_message *mesg)
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *tfr)
|
||||||
{
|
{
|
||||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
struct spi_transfer *tfr;
|
unsigned long spi_hz, clk_hz, cdiv;
|
||||||
struct spi_device *spi = mesg->spi;
|
unsigned long spi_used_hz, xfer_time_us;
|
||||||
int err = 0;
|
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||||
unsigned int timeout;
|
|
||||||
bool cs_change;
|
|
||||||
|
|
||||||
list_for_each_entry(tfr, &mesg->transfers, transfer_list) {
|
/* set clock */
|
||||||
err = bcm2835_spi_start_transfer(spi, tfr);
|
spi_hz = tfr->speed_hz;
|
||||||
if (err)
|
clk_hz = clk_get_rate(bs->clk);
|
||||||
goto out;
|
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&bs->done,
|
if (spi_hz >= clk_hz / 2) {
|
||||||
msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS));
|
cdiv = 2; /* clk_hz/2 is the fastest we can go */
|
||||||
if (!timeout) {
|
} else if (spi_hz) {
|
||||||
err = -ETIMEDOUT;
|
/* CDIV must be a multiple of two */
|
||||||
goto out;
|
cdiv = DIV_ROUND_UP(clk_hz, spi_hz);
|
||||||
|
cdiv += (cdiv % 2);
|
||||||
|
|
||||||
|
if (cdiv >= 65536)
|
||||||
|
cdiv = 0; /* 0 is the slowest we can go */
|
||||||
|
} else {
|
||||||
|
cdiv = 0; /* 0 is the slowest we can go */
|
||||||
|
}
|
||||||
|
spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
|
||||||
|
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
||||||
|
|
||||||
|
/* handle all the modes */
|
||||||
|
if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf))
|
||||||
|
cs |= BCM2835_SPI_CS_REN;
|
||||||
|
if (spi->mode & SPI_CPOL)
|
||||||
|
cs |= BCM2835_SPI_CS_CPOL;
|
||||||
|
if (spi->mode & SPI_CPHA)
|
||||||
|
cs |= BCM2835_SPI_CS_CPHA;
|
||||||
|
|
||||||
|
/* for gpio_cs set dummy CS so that no HW-CS get changed
|
||||||
|
* we can not run this in bcm2835_spi_set_cs, as it does
|
||||||
|
* not get called for cs_gpio cases, so we need to do it here
|
||||||
|
*/
|
||||||
|
if (gpio_is_valid(spi->cs_gpio) || (spi->mode & SPI_NO_CS))
|
||||||
|
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
|
||||||
|
|
||||||
|
/* set transmit buffers and length */
|
||||||
|
bs->tx_buf = tfr->tx_buf;
|
||||||
|
bs->rx_buf = tfr->rx_buf;
|
||||||
|
bs->tx_len = tfr->len;
|
||||||
|
bs->rx_len = tfr->len;
|
||||||
|
|
||||||
|
/* calculate the estimated time in us the transfer runs */
|
||||||
|
xfer_time_us = tfr->len
|
||||||
|
* 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */
|
||||||
|
* 1000000 / spi_used_hz;
|
||||||
|
|
||||||
|
/* for short requests run polling*/
|
||||||
|
if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US)
|
||||||
|
return bcm2835_spi_transfer_one_poll(master, spi, tfr,
|
||||||
|
cs, xfer_time_us);
|
||||||
|
|
||||||
|
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs_change = tfr->cs_change ||
|
static void bcm2835_spi_handle_err(struct spi_master *master,
|
||||||
list_is_last(&tfr->transfer_list, &mesg->transfers);
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
err = bcm2835_spi_finish_transfer(spi, tfr, cs_change);
|
bcm2835_spi_reset_hw(master);
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
mesg->actual_length += (tfr->len - bs->len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
static void bcm2835_spi_set_cs(struct spi_device *spi, bool gpio_level)
|
||||||
/* Clear FIFOs, and disable the HW block */
|
{
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
/*
|
||||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
* we can assume that we are "native" as per spi_set_cs
|
||||||
mesg->status = err;
|
* calling us ONLY when cs_gpio is not set
|
||||||
spi_finalize_current_message(master);
|
* we can also assume that we are CS < 3 as per bcm2835_spi_setup
|
||||||
|
* we would not get called because of error handling there.
|
||||||
|
* the level passed is the electrical level not enabled/disabled
|
||||||
|
* so it has to get translated back to enable/disable
|
||||||
|
* see spi_set_cs in spi.c for the implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct spi_master *master = spi->master;
|
||||||
|
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||||
|
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
/* calculate the enable flag from the passed gpio_level */
|
||||||
|
enable = (spi->mode & SPI_CS_HIGH) ? gpio_level : !gpio_level;
|
||||||
|
|
||||||
|
/* set flags for "reverse" polarity in the registers */
|
||||||
|
if (spi->mode & SPI_CS_HIGH) {
|
||||||
|
/* set the correct CS-bits */
|
||||||
|
cs |= BCM2835_SPI_CS_CSPOL;
|
||||||
|
cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select;
|
||||||
|
} else {
|
||||||
|
/* clean the CS-bits */
|
||||||
|
cs &= ~BCM2835_SPI_CS_CSPOL;
|
||||||
|
cs &= ~(BCM2835_SPI_CS_CSPOL0 << spi->chip_select);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select the correct chip_select depending on disabled/enabled */
|
||||||
|
if (enable) {
|
||||||
|
/* set cs correctly */
|
||||||
|
if (spi->mode & SPI_NO_CS) {
|
||||||
|
/* use the "undefined" chip-select */
|
||||||
|
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
|
||||||
|
} else {
|
||||||
|
/* set the chip select */
|
||||||
|
cs &= ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01);
|
||||||
|
cs |= spi->chip_select;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* disable CSPOL which puts HW-CS into deselected state */
|
||||||
|
cs &= ~BCM2835_SPI_CS_CSPOL;
|
||||||
|
/* use the "undefined" chip-select as precaution */
|
||||||
|
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* finally set the calculated flags in SPI_CS */
|
||||||
|
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chip_match_name(struct gpio_chip *chip, void *data)
|
||||||
|
{
|
||||||
|
return !strcmp(chip->label, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm2835_spi_setup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct gpio_chip *chip;
|
||||||
|
/*
|
||||||
|
* sanity checking the native-chipselects
|
||||||
|
*/
|
||||||
|
if (spi->mode & SPI_NO_CS)
|
||||||
|
return 0;
|
||||||
|
if (gpio_is_valid(spi->cs_gpio))
|
||||||
|
return 0;
|
||||||
|
if (spi->chip_select > 1) {
|
||||||
|
/* error in the case of native CS requested with CS > 1
|
||||||
|
* officially there is a CS2, but it is not documented
|
||||||
|
* which GPIO is connected with that...
|
||||||
|
*/
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"setup: only two native chip-selects are supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* now translate native cs to GPIO */
|
||||||
|
|
||||||
|
/* get the gpio chip for the base */
|
||||||
|
chip = gpiochip_find("pinctrl-bcm2835", chip_match_name);
|
||||||
|
if (!chip)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* and calculate the real CS */
|
||||||
|
spi->cs_gpio = chip->base + 8 - spi->chip_select;
|
||||||
|
|
||||||
|
/* and set up the "mode" and level */
|
||||||
|
dev_info(&spi->dev, "setting up native-CS%i as GPIO %i\n",
|
||||||
|
spi->chip_select, spi->cs_gpio);
|
||||||
|
|
||||||
|
/* set up GPIO as output and pull to the correct level */
|
||||||
|
err = gpio_direction_output(spi->cs_gpio,
|
||||||
|
(spi->mode & SPI_CS_HIGH) ? 0 : 1);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"could not set CS%i gpio %i as output: %i",
|
||||||
|
spi->chip_select, spi->cs_gpio, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/* the implementation of pinctrl-bcm2835 currently does not
|
||||||
|
* set the GPIO value when using gpio_direction_output
|
||||||
|
* so we are setting it here explicitly
|
||||||
|
*/
|
||||||
|
gpio_set_value(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? 0 : 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -312,13 +426,14 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||||
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
master->num_chipselect = 3;
|
master->num_chipselect = 3;
|
||||||
master->transfer_one_message = bcm2835_spi_transfer_one;
|
master->setup = bcm2835_spi_setup;
|
||||||
|
master->set_cs = bcm2835_spi_set_cs;
|
||||||
|
master->transfer_one = bcm2835_spi_transfer_one;
|
||||||
|
master->handle_err = bcm2835_spi_handle_err;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
bs = spi_master_get_devdata(master);
|
bs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
init_completion(&bs->done);
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
bs->regs = devm_ioremap_resource(&pdev->dev, res);
|
bs->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(bs->regs)) {
|
if (IS_ERR(bs->regs)) {
|
||||||
|
@ -349,7 +464,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||||
goto out_clk_disable;
|
goto out_clk_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialise the hardware */
|
/* initialise the hardware with the default polarities */
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
/* SPE bit has to be 0 before we read MSPI STATUS */
|
/* SPE bit has to be 0 before we read MSPI STATUS */
|
||||||
deadline = jiffies + BCM53XXSPI_SPE_TIMEOUT_MS * HZ / 1000;
|
deadline = jiffies + msecs_to_jiffies(BCM53XXSPI_SPE_TIMEOUT_MS);
|
||||||
do {
|
do {
|
||||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
|
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
|
||||||
if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
|
if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
|
||||||
|
@ -56,7 +56,7 @@ static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
|
||||||
goto spi_timeout;
|
goto spi_timeout;
|
||||||
|
|
||||||
/* Check status */
|
/* Check status */
|
||||||
deadline = jiffies + timeout_ms * HZ / 1000;
|
deadline = jiffies + msecs_to_jiffies(timeout_ms);
|
||||||
do {
|
do {
|
||||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
|
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
|
||||||
if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
|
if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
|
||||||
|
|
|
@ -559,7 +559,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
|
||||||
struct spi_transfer *previous = NULL;
|
struct spi_transfer *previous = NULL;
|
||||||
struct bfin_spi_slave_data *chip = NULL;
|
struct bfin_spi_slave_data *chip = NULL;
|
||||||
unsigned int bits_per_word;
|
unsigned int bits_per_word;
|
||||||
u16 cr, cr_width, dma_width, dma_config;
|
u16 cr, cr_width = 0, dma_width, dma_config;
|
||||||
u32 tranf_success = 1;
|
u32 tranf_success = 1;
|
||||||
u8 full_duplex = 0;
|
u8 full_duplex = 0;
|
||||||
|
|
||||||
|
@ -648,7 +648,6 @@ static void bfin_spi_pump_transfers(unsigned long data)
|
||||||
} else if (bits_per_word == 8) {
|
} else if (bits_per_word == 8) {
|
||||||
drv_data->n_bytes = bits_per_word/8;
|
drv_data->n_bytes = bits_per_word/8;
|
||||||
drv_data->len = transfer->len;
|
drv_data->len = transfer->len;
|
||||||
cr_width = 0;
|
|
||||||
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
|
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
|
||||||
}
|
}
|
||||||
cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
|
cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
|
||||||
|
|
|
@ -49,12 +49,17 @@ bitbang_txrx_be_cpha0(struct spi_device *spi,
|
||||||
{
|
{
|
||||||
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
|
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
|
||||||
|
|
||||||
|
bool oldbit = !(word & 1);
|
||||||
/* clock starts at inactive polarity */
|
/* clock starts at inactive polarity */
|
||||||
for (word <<= (32 - bits); likely(bits); bits--) {
|
for (word <<= (32 - bits); likely(bits); bits--) {
|
||||||
|
|
||||||
/* setup MSB (to slave) on trailing edge */
|
/* setup MSB (to slave) on trailing edge */
|
||||||
if ((flags & SPI_MASTER_NO_TX) == 0)
|
if ((flags & SPI_MASTER_NO_TX) == 0) {
|
||||||
|
if ((word & (1 << 31)) != oldbit) {
|
||||||
setmosi(spi, word & (1 << 31));
|
setmosi(spi, word & (1 << 31));
|
||||||
|
oldbit = word & (1 << 31);
|
||||||
|
}
|
||||||
|
}
|
||||||
spidelay(nsecs); /* T(setup) */
|
spidelay(nsecs); /* T(setup) */
|
||||||
|
|
||||||
setsck(spi, !cpol);
|
setsck(spi, !cpol);
|
||||||
|
@ -76,13 +81,18 @@ bitbang_txrx_be_cpha1(struct spi_device *spi,
|
||||||
{
|
{
|
||||||
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
|
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
|
||||||
|
|
||||||
|
bool oldbit = !(word & (1 << 31));
|
||||||
/* clock starts at inactive polarity */
|
/* clock starts at inactive polarity */
|
||||||
for (word <<= (32 - bits); likely(bits); bits--) {
|
for (word <<= (32 - bits); likely(bits); bits--) {
|
||||||
|
|
||||||
/* setup MSB (to slave) on leading edge */
|
/* setup MSB (to slave) on leading edge */
|
||||||
setsck(spi, !cpol);
|
setsck(spi, !cpol);
|
||||||
if ((flags & SPI_MASTER_NO_TX) == 0)
|
if ((flags & SPI_MASTER_NO_TX) == 0) {
|
||||||
|
if ((word & (1 << 31)) != oldbit) {
|
||||||
setmosi(spi, word & (1 << 31));
|
setmosi(spi, word & (1 << 31));
|
||||||
|
oldbit = word & (1 << 31);
|
||||||
|
}
|
||||||
|
}
|
||||||
spidelay(nsecs); /* T(setup) */
|
spidelay(nsecs); /* T(setup) */
|
||||||
|
|
||||||
setsck(spi, cpol);
|
setsck(spi, cpol);
|
||||||
|
|
|
@ -23,29 +23,31 @@
|
||||||
#include "spi-dw.h"
|
#include "spi-dw.h"
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_DW_MID_DMA
|
#ifdef CONFIG_SPI_DW_MID_DMA
|
||||||
#include <linux/intel_mid_dma.h>
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/platform_data/dma-dw.h>
|
||||||
|
|
||||||
#define RX_BUSY 0
|
#define RX_BUSY 0
|
||||||
#define TX_BUSY 1
|
#define TX_BUSY 1
|
||||||
|
|
||||||
struct mid_dma {
|
static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 };
|
||||||
struct intel_mid_dma_slave dmas_tx;
|
static struct dw_dma_slave mid_dma_rx = { .src_id = 0 };
|
||||||
struct intel_mid_dma_slave dmas_rx;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
|
static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
|
||||||
{
|
{
|
||||||
struct dw_spi *dws = param;
|
struct dw_dma_slave *s = param;
|
||||||
|
|
||||||
return dws->dma_dev == chan->device->dev;
|
if (s->dma_dev != chan->device->dev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chan->private = s;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mid_spi_dma_init(struct dw_spi *dws)
|
static int mid_spi_dma_init(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
struct mid_dma *dw_dma = dws->dma_priv;
|
|
||||||
struct pci_dev *dma_dev;
|
struct pci_dev *dma_dev;
|
||||||
struct intel_mid_dma_slave *rxs, *txs;
|
struct dw_dma_slave *tx = dws->dma_tx;
|
||||||
|
struct dw_dma_slave *rx = dws->dma_rx;
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -56,28 +58,22 @@ static int mid_spi_dma_init(struct dw_spi *dws)
|
||||||
if (!dma_dev)
|
if (!dma_dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
dws->dma_dev = &dma_dev->dev;
|
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
dma_cap_zero(mask);
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
/* 1. Init rx channel */
|
/* 1. Init rx channel */
|
||||||
dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
|
rx->dma_dev = &dma_dev->dev;
|
||||||
|
dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx);
|
||||||
if (!dws->rxchan)
|
if (!dws->rxchan)
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
rxs = &dw_dma->dmas_rx;
|
dws->master->dma_rx = dws->rxchan;
|
||||||
rxs->hs_mode = LNW_DMA_HW_HS;
|
|
||||||
rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
|
|
||||||
dws->rxchan->private = rxs;
|
|
||||||
|
|
||||||
/* 2. Init tx channel */
|
/* 2. Init tx channel */
|
||||||
dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
|
tx->dma_dev = &dma_dev->dev;
|
||||||
|
dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx);
|
||||||
if (!dws->txchan)
|
if (!dws->txchan)
|
||||||
goto free_rxchan;
|
goto free_rxchan;
|
||||||
txs = &dw_dma->dmas_tx;
|
dws->master->dma_tx = dws->txchan;
|
||||||
txs->hs_mode = LNW_DMA_HW_HS;
|
|
||||||
txs->cfg_mode = LNW_DMA_MEM_TO_PER;
|
|
||||||
dws->txchan->private = txs;
|
|
||||||
|
|
||||||
dws->dma_inited = 1;
|
dws->dma_inited = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -100,6 +96,42 @@ static void mid_spi_dma_exit(struct dw_spi *dws)
|
||||||
dma_release_channel(dws->rxchan);
|
dma_release_channel(dws->rxchan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t dma_transfer(struct dw_spi *dws)
|
||||||
|
{
|
||||||
|
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
|
||||||
|
|
||||||
|
if (!irq_status)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
dw_readl(dws, DW_SPI_ICR);
|
||||||
|
spi_reset_chip(dws);
|
||||||
|
|
||||||
|
dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
|
||||||
|
dws->master->cur_msg->status = -EIO;
|
||||||
|
spi_finalize_current_transfer(dws->master);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mid_spi_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct dw_spi *dws = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
if (!dws->dma_inited)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return xfer->len > dws->fifo_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum dma_slave_buswidth convert_dma_width(u32 dma_width) {
|
||||||
|
if (dma_width == 1)
|
||||||
|
return DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
else if (dma_width == 2)
|
||||||
|
return DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||||
|
|
||||||
|
return DMA_SLAVE_BUSWIDTH_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dws->dma_chan_busy is set before the dma transfer starts, callback for tx
|
* dws->dma_chan_busy is set before the dma transfer starts, callback for tx
|
||||||
* channel will clear a corresponding bit.
|
* channel will clear a corresponding bit.
|
||||||
|
@ -111,33 +143,30 @@ static void dw_spi_dma_tx_done(void *arg)
|
||||||
clear_bit(TX_BUSY, &dws->dma_chan_busy);
|
clear_bit(TX_BUSY, &dws->dma_chan_busy);
|
||||||
if (test_bit(RX_BUSY, &dws->dma_chan_busy))
|
if (test_bit(RX_BUSY, &dws->dma_chan_busy))
|
||||||
return;
|
return;
|
||||||
dw_spi_xfer_done(dws);
|
spi_finalize_current_transfer(dws->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws)
|
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct dma_slave_config txconf;
|
struct dma_slave_config txconf;
|
||||||
struct dma_async_tx_descriptor *txdesc;
|
struct dma_async_tx_descriptor *txdesc;
|
||||||
|
|
||||||
if (!dws->tx_dma)
|
if (!xfer->tx_buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
txconf.direction = DMA_MEM_TO_DEV;
|
txconf.direction = DMA_MEM_TO_DEV;
|
||||||
txconf.dst_addr = dws->dma_addr;
|
txconf.dst_addr = dws->dma_addr;
|
||||||
txconf.dst_maxburst = LNW_DMA_MSIZE_16;
|
txconf.dst_maxburst = 16;
|
||||||
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
txconf.dst_addr_width = dws->dma_width;
|
txconf.dst_addr_width = convert_dma_width(dws->dma_width);
|
||||||
txconf.device_fc = false;
|
txconf.device_fc = false;
|
||||||
|
|
||||||
dmaengine_slave_config(dws->txchan, &txconf);
|
dmaengine_slave_config(dws->txchan, &txconf);
|
||||||
|
|
||||||
memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
|
|
||||||
dws->tx_sgl.dma_address = dws->tx_dma;
|
|
||||||
dws->tx_sgl.length = dws->len;
|
|
||||||
|
|
||||||
txdesc = dmaengine_prep_slave_sg(dws->txchan,
|
txdesc = dmaengine_prep_slave_sg(dws->txchan,
|
||||||
&dws->tx_sgl,
|
xfer->tx_sg.sgl,
|
||||||
1,
|
xfer->tx_sg.nents,
|
||||||
DMA_MEM_TO_DEV,
|
DMA_MEM_TO_DEV,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!txdesc)
|
if (!txdesc)
|
||||||
|
@ -160,33 +189,30 @@ static void dw_spi_dma_rx_done(void *arg)
|
||||||
clear_bit(RX_BUSY, &dws->dma_chan_busy);
|
clear_bit(RX_BUSY, &dws->dma_chan_busy);
|
||||||
if (test_bit(TX_BUSY, &dws->dma_chan_busy))
|
if (test_bit(TX_BUSY, &dws->dma_chan_busy))
|
||||||
return;
|
return;
|
||||||
dw_spi_xfer_done(dws);
|
spi_finalize_current_transfer(dws->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
|
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct dma_slave_config rxconf;
|
struct dma_slave_config rxconf;
|
||||||
struct dma_async_tx_descriptor *rxdesc;
|
struct dma_async_tx_descriptor *rxdesc;
|
||||||
|
|
||||||
if (!dws->rx_dma)
|
if (!xfer->rx_buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
rxconf.direction = DMA_DEV_TO_MEM;
|
rxconf.direction = DMA_DEV_TO_MEM;
|
||||||
rxconf.src_addr = dws->dma_addr;
|
rxconf.src_addr = dws->dma_addr;
|
||||||
rxconf.src_maxburst = LNW_DMA_MSIZE_16;
|
rxconf.src_maxburst = 16;
|
||||||
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||||
rxconf.src_addr_width = dws->dma_width;
|
rxconf.src_addr_width = convert_dma_width(dws->dma_width);
|
||||||
rxconf.device_fc = false;
|
rxconf.device_fc = false;
|
||||||
|
|
||||||
dmaengine_slave_config(dws->rxchan, &rxconf);
|
dmaengine_slave_config(dws->rxchan, &rxconf);
|
||||||
|
|
||||||
memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
|
|
||||||
dws->rx_sgl.dma_address = dws->rx_dma;
|
|
||||||
dws->rx_sgl.length = dws->len;
|
|
||||||
|
|
||||||
rxdesc = dmaengine_prep_slave_sg(dws->rxchan,
|
rxdesc = dmaengine_prep_slave_sg(dws->rxchan,
|
||||||
&dws->rx_sgl,
|
xfer->rx_sg.sgl,
|
||||||
1,
|
xfer->rx_sg.nents,
|
||||||
DMA_DEV_TO_MEM,
|
DMA_DEV_TO_MEM,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!rxdesc)
|
if (!rxdesc)
|
||||||
|
@ -198,37 +224,36 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
|
||||||
return rxdesc;
|
return rxdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_spi_dma_setup(struct dw_spi *dws)
|
static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
u16 dma_ctrl = 0;
|
u16 dma_ctrl = 0;
|
||||||
|
|
||||||
spi_enable_chip(dws, 0);
|
dw_writel(dws, DW_SPI_DMARDLR, 0xf);
|
||||||
|
dw_writel(dws, DW_SPI_DMATDLR, 0x10);
|
||||||
|
|
||||||
dw_writew(dws, DW_SPI_DMARDLR, 0xf);
|
if (xfer->tx_buf)
|
||||||
dw_writew(dws, DW_SPI_DMATDLR, 0x10);
|
|
||||||
|
|
||||||
if (dws->tx_dma)
|
|
||||||
dma_ctrl |= SPI_DMA_TDMAE;
|
dma_ctrl |= SPI_DMA_TDMAE;
|
||||||
if (dws->rx_dma)
|
if (xfer->rx_buf)
|
||||||
dma_ctrl |= SPI_DMA_RDMAE;
|
dma_ctrl |= SPI_DMA_RDMAE;
|
||||||
dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
|
dw_writel(dws, DW_SPI_DMACR, dma_ctrl);
|
||||||
|
|
||||||
spi_enable_chip(dws, 1);
|
/* Set the interrupt mask */
|
||||||
|
spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI);
|
||||||
|
|
||||||
|
dws->transfer_handler = dma_transfer;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
|
static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct dma_async_tx_descriptor *txdesc, *rxdesc;
|
struct dma_async_tx_descriptor *txdesc, *rxdesc;
|
||||||
|
|
||||||
/* 1. setup DMA related registers */
|
/* Prepare the TX dma transfer */
|
||||||
if (cs_change)
|
txdesc = dw_spi_dma_prepare_tx(dws, xfer);
|
||||||
dw_spi_dma_setup(dws);
|
|
||||||
|
|
||||||
/* 2. Prepare the TX dma transfer */
|
/* Prepare the RX dma transfer */
|
||||||
txdesc = dw_spi_dma_prepare_tx(dws);
|
rxdesc = dw_spi_dma_prepare_rx(dws, xfer);
|
||||||
|
|
||||||
/* 3. Prepare the RX dma transfer */
|
|
||||||
rxdesc = dw_spi_dma_prepare_rx(dws);
|
|
||||||
|
|
||||||
/* rx must be started before tx due to spi instinct */
|
/* rx must be started before tx due to spi instinct */
|
||||||
if (rxdesc) {
|
if (rxdesc) {
|
||||||
|
@ -246,10 +271,25 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mid_spi_dma_stop(struct dw_spi *dws)
|
||||||
|
{
|
||||||
|
if (test_bit(TX_BUSY, &dws->dma_chan_busy)) {
|
||||||
|
dmaengine_terminate_all(dws->txchan);
|
||||||
|
clear_bit(TX_BUSY, &dws->dma_chan_busy);
|
||||||
|
}
|
||||||
|
if (test_bit(RX_BUSY, &dws->dma_chan_busy)) {
|
||||||
|
dmaengine_terminate_all(dws->rxchan);
|
||||||
|
clear_bit(RX_BUSY, &dws->dma_chan_busy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct dw_spi_dma_ops mid_dma_ops = {
|
static struct dw_spi_dma_ops mid_dma_ops = {
|
||||||
.dma_init = mid_spi_dma_init,
|
.dma_init = mid_spi_dma_init,
|
||||||
.dma_exit = mid_spi_dma_exit,
|
.dma_exit = mid_spi_dma_exit,
|
||||||
|
.dma_setup = mid_spi_dma_setup,
|
||||||
|
.can_dma = mid_spi_can_dma,
|
||||||
.dma_transfer = mid_spi_dma_transfer,
|
.dma_transfer = mid_spi_dma_transfer,
|
||||||
|
.dma_stop = mid_spi_dma_stop,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -282,9 +322,8 @@ int dw_spi_mid_init(struct dw_spi *dws)
|
||||||
iounmap(clk_reg);
|
iounmap(clk_reg);
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_DW_MID_DMA
|
#ifdef CONFIG_SPI_DW_MID_DMA
|
||||||
dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL);
|
dws->dma_tx = &mid_dma_tx;
|
||||||
if (!dws->dma_priv)
|
dws->dma_rx = &mid_dma_rx;
|
||||||
return -ENOMEM;
|
|
||||||
dws->dma_ops = &mid_dma_ops;
|
dws->dma_ops = &mid_dma_ops;
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -28,11 +28,6 @@
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define START_STATE ((void *)0)
|
|
||||||
#define RUNNING_STATE ((void *)1)
|
|
||||||
#define DONE_STATE ((void *)2)
|
|
||||||
#define ERROR_STATE ((void *)-1)
|
|
||||||
|
|
||||||
/* Slave spi_dev related */
|
/* Slave spi_dev related */
|
||||||
struct chip_data {
|
struct chip_data {
|
||||||
u16 cr0;
|
u16 cr0;
|
||||||
|
@ -143,13 +138,26 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_DEBUG_FS */
|
#endif /* CONFIG_DEBUG_FS */
|
||||||
|
|
||||||
|
static void dw_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
struct dw_spi *dws = spi_master_get_devdata(spi->master);
|
||||||
|
struct chip_data *chip = spi_get_ctldata(spi);
|
||||||
|
|
||||||
|
/* Chip select logic is inverted from spi_set_cs() */
|
||||||
|
if (chip && chip->cs_control)
|
||||||
|
chip->cs_control(!enable);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the max entries we can fill into tx fifo */
|
/* Return the max entries we can fill into tx fifo */
|
||||||
static inline u32 tx_max(struct dw_spi *dws)
|
static inline u32 tx_max(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
u32 tx_left, tx_room, rxtx_gap;
|
u32 tx_left, tx_room, rxtx_gap;
|
||||||
|
|
||||||
tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
|
tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
|
||||||
tx_room = dws->fifo_len - dw_readw(dws, DW_SPI_TXFLR);
|
tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Another concern is about the tx/rx mismatch, we
|
* Another concern is about the tx/rx mismatch, we
|
||||||
|
@ -170,7 +178,7 @@ static inline u32 rx_max(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
|
u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
|
||||||
|
|
||||||
return min_t(u32, rx_left, dw_readw(dws, DW_SPI_RXFLR));
|
return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_writer(struct dw_spi *dws)
|
static void dw_writer(struct dw_spi *dws)
|
||||||
|
@ -186,7 +194,7 @@ static void dw_writer(struct dw_spi *dws)
|
||||||
else
|
else
|
||||||
txw = *(u16 *)(dws->tx);
|
txw = *(u16 *)(dws->tx);
|
||||||
}
|
}
|
||||||
dw_writew(dws, DW_SPI_DR, txw);
|
dw_writel(dws, DW_SPI_DR, txw);
|
||||||
dws->tx += dws->n_bytes;
|
dws->tx += dws->n_bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +205,7 @@ static void dw_reader(struct dw_spi *dws)
|
||||||
u16 rxw;
|
u16 rxw;
|
||||||
|
|
||||||
while (max--) {
|
while (max--) {
|
||||||
rxw = dw_readw(dws, DW_SPI_DR);
|
rxw = dw_readl(dws, DW_SPI_DR);
|
||||||
/* Care rx only if the transfer's original "rx" is not null */
|
/* Care rx only if the transfer's original "rx" is not null */
|
||||||
if (dws->rx_end - dws->len) {
|
if (dws->rx_end - dws->len) {
|
||||||
if (dws->n_bytes == 1)
|
if (dws->n_bytes == 1)
|
||||||
|
@ -209,103 +217,22 @@ static void dw_reader(struct dw_spi *dws)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *next_transfer(struct dw_spi *dws)
|
|
||||||
{
|
|
||||||
struct spi_message *msg = dws->cur_msg;
|
|
||||||
struct spi_transfer *trans = dws->cur_transfer;
|
|
||||||
|
|
||||||
/* Move to next transfer */
|
|
||||||
if (trans->transfer_list.next != &msg->transfers) {
|
|
||||||
dws->cur_transfer =
|
|
||||||
list_entry(trans->transfer_list.next,
|
|
||||||
struct spi_transfer,
|
|
||||||
transfer_list);
|
|
||||||
return RUNNING_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DONE_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: first step is the protocol driver prepares
|
|
||||||
* a dma-capable memory, and this func just need translate
|
|
||||||
* the virt addr to physical
|
|
||||||
*/
|
|
||||||
static int map_dma_buffers(struct dw_spi *dws)
|
|
||||||
{
|
|
||||||
if (!dws->cur_msg->is_dma_mapped
|
|
||||||
|| !dws->dma_inited
|
|
||||||
|| !dws->cur_chip->enable_dma
|
|
||||||
|| !dws->dma_ops)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (dws->cur_transfer->tx_dma)
|
|
||||||
dws->tx_dma = dws->cur_transfer->tx_dma;
|
|
||||||
|
|
||||||
if (dws->cur_transfer->rx_dma)
|
|
||||||
dws->rx_dma = dws->cur_transfer->rx_dma;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caller already set message->status; dma and pio irqs are blocked */
|
|
||||||
static void giveback(struct dw_spi *dws)
|
|
||||||
{
|
|
||||||
struct spi_transfer *last_transfer;
|
|
||||||
struct spi_message *msg;
|
|
||||||
|
|
||||||
msg = dws->cur_msg;
|
|
||||||
dws->cur_msg = NULL;
|
|
||||||
dws->cur_transfer = NULL;
|
|
||||||
dws->prev_chip = dws->cur_chip;
|
|
||||||
dws->cur_chip = NULL;
|
|
||||||
dws->dma_mapped = 0;
|
|
||||||
|
|
||||||
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
|
||||||
transfer_list);
|
|
||||||
|
|
||||||
if (!last_transfer->cs_change)
|
|
||||||
spi_chip_sel(dws, msg->spi, 0);
|
|
||||||
|
|
||||||
spi_finalize_current_message(dws->master);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void int_error_stop(struct dw_spi *dws, const char *msg)
|
static void int_error_stop(struct dw_spi *dws, const char *msg)
|
||||||
{
|
{
|
||||||
/* Stop the hw */
|
spi_reset_chip(dws);
|
||||||
spi_enable_chip(dws, 0);
|
|
||||||
|
|
||||||
dev_err(&dws->master->dev, "%s\n", msg);
|
dev_err(&dws->master->dev, "%s\n", msg);
|
||||||
dws->cur_msg->state = ERROR_STATE;
|
dws->master->cur_msg->status = -EIO;
|
||||||
tasklet_schedule(&dws->pump_transfers);
|
spi_finalize_current_transfer(dws->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dw_spi_xfer_done(struct dw_spi *dws)
|
|
||||||
{
|
|
||||||
/* Update total byte transferred return count actual bytes read */
|
|
||||||
dws->cur_msg->actual_length += dws->len;
|
|
||||||
|
|
||||||
/* Move to next transfer */
|
|
||||||
dws->cur_msg->state = next_transfer(dws);
|
|
||||||
|
|
||||||
/* Handle end of message */
|
|
||||||
if (dws->cur_msg->state == DONE_STATE) {
|
|
||||||
dws->cur_msg->status = 0;
|
|
||||||
giveback(dws);
|
|
||||||
} else
|
|
||||||
tasklet_schedule(&dws->pump_transfers);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
|
|
||||||
|
|
||||||
static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
u16 irq_status = dw_readw(dws, DW_SPI_ISR);
|
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
|
||||||
|
|
||||||
/* Error handling */
|
/* Error handling */
|
||||||
if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
|
if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
|
||||||
dw_readw(dws, DW_SPI_TXOICR);
|
dw_readl(dws, DW_SPI_ICR);
|
||||||
dw_readw(dws, DW_SPI_RXOICR);
|
|
||||||
dw_readw(dws, DW_SPI_RXUICR);
|
|
||||||
int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
|
int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -313,7 +240,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
||||||
dw_reader(dws);
|
dw_reader(dws);
|
||||||
if (dws->rx_end == dws->rx) {
|
if (dws->rx_end == dws->rx) {
|
||||||
spi_mask_intr(dws, SPI_INT_TXEI);
|
spi_mask_intr(dws, SPI_INT_TXEI);
|
||||||
dw_spi_xfer_done(dws);
|
spi_finalize_current_transfer(dws->master);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
if (irq_status & SPI_INT_TXEI) {
|
if (irq_status & SPI_INT_TXEI) {
|
||||||
|
@ -328,13 +255,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
||||||
|
|
||||||
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct dw_spi *dws = dev_id;
|
struct spi_master *master = dev_id;
|
||||||
u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f;
|
struct dw_spi *dws = spi_master_get_devdata(master);
|
||||||
|
u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f;
|
||||||
|
|
||||||
if (!irq_status)
|
if (!irq_status)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
if (!dws->cur_msg) {
|
if (!master->cur_msg) {
|
||||||
spi_mask_intr(dws, SPI_INT_TXEI);
|
spi_mask_intr(dws, SPI_INT_TXEI);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -343,7 +271,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called inside pump_transfers() */
|
/* Must be called inside pump_transfers() */
|
||||||
static void poll_transfer(struct dw_spi *dws)
|
static int poll_transfer(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
dw_writer(dws);
|
dw_writer(dws);
|
||||||
|
@ -351,64 +279,32 @@ static void poll_transfer(struct dw_spi *dws)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
} while (dws->rx_end > dws->rx);
|
} while (dws->rx_end > dws->rx);
|
||||||
|
|
||||||
dw_spi_xfer_done(dws);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pump_transfers(unsigned long data)
|
static int dw_spi_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi, struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
struct dw_spi *dws = (struct dw_spi *)data;
|
struct dw_spi *dws = spi_master_get_devdata(master);
|
||||||
struct spi_message *message = NULL;
|
struct chip_data *chip = spi_get_ctldata(spi);
|
||||||
struct spi_transfer *transfer = NULL;
|
|
||||||
struct spi_transfer *previous = NULL;
|
|
||||||
struct spi_device *spi = NULL;
|
|
||||||
struct chip_data *chip = NULL;
|
|
||||||
u8 bits = 0;
|
|
||||||
u8 imask = 0;
|
u8 imask = 0;
|
||||||
u8 cs_change = 0;
|
u16 txlevel = 0;
|
||||||
u16 txint_level = 0;
|
|
||||||
u16 clk_div = 0;
|
u16 clk_div = 0;
|
||||||
u32 speed = 0;
|
u32 speed = 0;
|
||||||
u32 cr0 = 0;
|
u32 cr0 = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Get current state information */
|
dws->dma_mapped = 0;
|
||||||
message = dws->cur_msg;
|
|
||||||
transfer = dws->cur_transfer;
|
|
||||||
chip = dws->cur_chip;
|
|
||||||
spi = message->spi;
|
|
||||||
|
|
||||||
if (message->state == ERROR_STATE) {
|
|
||||||
message->status = -EIO;
|
|
||||||
goto early_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle end of message */
|
|
||||||
if (message->state == DONE_STATE) {
|
|
||||||
message->status = 0;
|
|
||||||
goto early_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delay if requested at end of transfer */
|
|
||||||
if (message->state == RUNNING_STATE) {
|
|
||||||
previous = list_entry(transfer->transfer_list.prev,
|
|
||||||
struct spi_transfer,
|
|
||||||
transfer_list);
|
|
||||||
if (previous->delay_usecs)
|
|
||||||
udelay(previous->delay_usecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
dws->n_bytes = chip->n_bytes;
|
dws->n_bytes = chip->n_bytes;
|
||||||
dws->dma_width = chip->dma_width;
|
dws->dma_width = chip->dma_width;
|
||||||
dws->cs_control = chip->cs_control;
|
|
||||||
|
|
||||||
dws->rx_dma = transfer->rx_dma;
|
|
||||||
dws->tx_dma = transfer->tx_dma;
|
|
||||||
dws->tx = (void *)transfer->tx_buf;
|
dws->tx = (void *)transfer->tx_buf;
|
||||||
dws->tx_end = dws->tx + transfer->len;
|
dws->tx_end = dws->tx + transfer->len;
|
||||||
dws->rx = transfer->rx_buf;
|
dws->rx = transfer->rx_buf;
|
||||||
dws->rx_end = dws->rx + transfer->len;
|
dws->rx_end = dws->rx + transfer->len;
|
||||||
dws->len = dws->cur_transfer->len;
|
dws->len = transfer->len;
|
||||||
if (chip != dws->prev_chip)
|
|
||||||
cs_change = 1;
|
spi_enable_chip(dws, 0);
|
||||||
|
|
||||||
cr0 = chip->cr0;
|
cr0 = chip->cr0;
|
||||||
|
|
||||||
|
@ -416,32 +312,37 @@ static void pump_transfers(unsigned long data)
|
||||||
if (transfer->speed_hz) {
|
if (transfer->speed_hz) {
|
||||||
speed = chip->speed_hz;
|
speed = chip->speed_hz;
|
||||||
|
|
||||||
if ((transfer->speed_hz != speed) || (!chip->clk_div)) {
|
if ((transfer->speed_hz != speed) || !chip->clk_div) {
|
||||||
speed = transfer->speed_hz;
|
speed = transfer->speed_hz;
|
||||||
|
|
||||||
/* clk_div doesn't support odd number */
|
/* clk_div doesn't support odd number */
|
||||||
clk_div = dws->max_freq / speed;
|
clk_div = (dws->max_freq / speed + 1) & 0xfffe;
|
||||||
clk_div = (clk_div + 1) & 0xfffe;
|
|
||||||
|
|
||||||
chip->speed_hz = speed;
|
chip->speed_hz = speed;
|
||||||
chip->clk_div = clk_div;
|
chip->clk_div = clk_div;
|
||||||
|
|
||||||
|
spi_set_clk(dws, chip->clk_div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transfer->bits_per_word) {
|
if (transfer->bits_per_word) {
|
||||||
bits = transfer->bits_per_word;
|
if (transfer->bits_per_word == 8) {
|
||||||
dws->n_bytes = dws->dma_width = bits >> 3;
|
dws->n_bytes = 1;
|
||||||
cr0 = (bits - 1)
|
dws->dma_width = 1;
|
||||||
|
} else if (transfer->bits_per_word == 16) {
|
||||||
|
dws->n_bytes = 2;
|
||||||
|
dws->dma_width = 2;
|
||||||
|
}
|
||||||
|
cr0 = (transfer->bits_per_word - 1)
|
||||||
| (chip->type << SPI_FRF_OFFSET)
|
| (chip->type << SPI_FRF_OFFSET)
|
||||||
| (spi->mode << SPI_MODE_OFFSET)
|
| (spi->mode << SPI_MODE_OFFSET)
|
||||||
| (chip->tmode << SPI_TMOD_OFFSET);
|
| (chip->tmode << SPI_TMOD_OFFSET);
|
||||||
}
|
}
|
||||||
message->state = RUNNING_STATE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust transfer mode if necessary. Requires platform dependent
|
* Adjust transfer mode if necessary. Requires platform dependent
|
||||||
* chipselect mechanism.
|
* chipselect mechanism.
|
||||||
*/
|
*/
|
||||||
if (dws->cs_control) {
|
if (chip->cs_control) {
|
||||||
if (dws->rx && dws->tx)
|
if (dws->rx && dws->tx)
|
||||||
chip->tmode = SPI_TMOD_TR;
|
chip->tmode = SPI_TMOD_TR;
|
||||||
else if (dws->rx)
|
else if (dws->rx)
|
||||||
|
@ -453,80 +354,60 @@ static void pump_transfers(unsigned long data)
|
||||||
cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
|
cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dw_writel(dws, DW_SPI_CTRL0, cr0);
|
||||||
|
|
||||||
/* Check if current transfer is a DMA transaction */
|
/* Check if current transfer is a DMA transaction */
|
||||||
dws->dma_mapped = map_dma_buffers(dws);
|
if (master->can_dma && master->can_dma(master, spi, transfer))
|
||||||
|
dws->dma_mapped = master->cur_msg_mapped;
|
||||||
|
|
||||||
|
/* For poll mode just disable all interrupts */
|
||||||
|
spi_mask_intr(dws, 0xff);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interrupt mode
|
* Interrupt mode
|
||||||
* we only need set the TXEI IRQ, as TX/RX always happen syncronizely
|
* we only need set the TXEI IRQ, as TX/RX always happen syncronizely
|
||||||
*/
|
*/
|
||||||
if (!dws->dma_mapped && !chip->poll_mode) {
|
if (dws->dma_mapped) {
|
||||||
int templen = dws->len / dws->n_bytes;
|
ret = dws->dma_ops->dma_setup(dws, transfer);
|
||||||
|
if (ret < 0) {
|
||||||
txint_level = dws->fifo_len / 2;
|
spi_enable_chip(dws, 1);
|
||||||
txint_level = (templen > txint_level) ? txint_level : templen;
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (!chip->poll_mode) {
|
||||||
|
txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes);
|
||||||
|
dw_writel(dws, DW_SPI_TXFLTR, txlevel);
|
||||||
|
|
||||||
|
/* Set the interrupt mask */
|
||||||
imask |= SPI_INT_TXEI | SPI_INT_TXOI |
|
imask |= SPI_INT_TXEI | SPI_INT_TXOI |
|
||||||
SPI_INT_RXUI | SPI_INT_RXOI;
|
SPI_INT_RXUI | SPI_INT_RXOI;
|
||||||
|
spi_umask_intr(dws, imask);
|
||||||
|
|
||||||
dws->transfer_handler = interrupt_transfer;
|
dws->transfer_handler = interrupt_transfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Reprogram registers only if
|
|
||||||
* 1. chip select changes
|
|
||||||
* 2. clk_div is changed
|
|
||||||
* 3. control value changes
|
|
||||||
*/
|
|
||||||
if (dw_readw(dws, DW_SPI_CTRL0) != cr0 || cs_change || clk_div || imask) {
|
|
||||||
spi_enable_chip(dws, 0);
|
|
||||||
|
|
||||||
if (dw_readw(dws, DW_SPI_CTRL0) != cr0)
|
|
||||||
dw_writew(dws, DW_SPI_CTRL0, cr0);
|
|
||||||
|
|
||||||
spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
|
|
||||||
spi_chip_sel(dws, spi, 1);
|
|
||||||
|
|
||||||
/* Set the interrupt mask, for poll mode just disable all int */
|
|
||||||
spi_mask_intr(dws, 0xff);
|
|
||||||
if (imask)
|
|
||||||
spi_umask_intr(dws, imask);
|
|
||||||
if (txint_level)
|
|
||||||
dw_writew(dws, DW_SPI_TXFLTR, txint_level);
|
|
||||||
|
|
||||||
spi_enable_chip(dws, 1);
|
spi_enable_chip(dws, 1);
|
||||||
if (cs_change)
|
|
||||||
dws->prev_chip = chip;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dws->dma_mapped)
|
if (dws->dma_mapped) {
|
||||||
dws->dma_ops->dma_transfer(dws, cs_change);
|
ret = dws->dma_ops->dma_transfer(dws, transfer);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (chip->poll_mode)
|
if (chip->poll_mode)
|
||||||
poll_transfer(dws);
|
return poll_transfer(dws);
|
||||||
|
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
early_exit:
|
|
||||||
giveback(dws);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dw_spi_transfer_one_message(struct spi_master *master,
|
static void dw_spi_handle_err(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
struct dw_spi *dws = spi_master_get_devdata(master);
|
struct dw_spi *dws = spi_master_get_devdata(master);
|
||||||
|
|
||||||
dws->cur_msg = msg;
|
if (dws->dma_mapped)
|
||||||
/* Initial message state */
|
dws->dma_ops->dma_stop(dws);
|
||||||
dws->cur_msg->state = START_STATE;
|
|
||||||
dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
|
|
||||||
struct spi_transfer,
|
|
||||||
transfer_list);
|
|
||||||
dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
|
|
||||||
|
|
||||||
/* Launch transfers */
|
spi_reset_chip(dws);
|
||||||
tasklet_schedule(&dws->pump_transfers);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This may be called twice for each spi dev */
|
/* This may be called twice for each spi dev */
|
||||||
|
@ -561,8 +442,6 @@ static int dw_spi_setup(struct spi_device *spi)
|
||||||
|
|
||||||
chip->rx_threshold = 0;
|
chip->rx_threshold = 0;
|
||||||
chip->tx_threshold = 0;
|
chip->tx_threshold = 0;
|
||||||
|
|
||||||
chip->enable_dma = chip_info->enable_dma;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi->bits_per_word == 8) {
|
if (spi->bits_per_word == 8) {
|
||||||
|
@ -610,9 +489,7 @@ static void dw_spi_cleanup(struct spi_device *spi)
|
||||||
/* Restart the controller, disable all interrupts, clean rx fifo */
|
/* Restart the controller, disable all interrupts, clean rx fifo */
|
||||||
static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
spi_enable_chip(dws, 0);
|
spi_reset_chip(dws);
|
||||||
spi_mask_intr(dws, 0xff);
|
|
||||||
spi_enable_chip(dws, 1);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to detect the FIFO depth if not set by interface driver,
|
* Try to detect the FIFO depth if not set by interface driver,
|
||||||
|
@ -622,11 +499,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||||
u32 fifo;
|
u32 fifo;
|
||||||
|
|
||||||
for (fifo = 1; fifo < 256; fifo++) {
|
for (fifo = 1; fifo < 256; fifo++) {
|
||||||
dw_writew(dws, DW_SPI_TXFLTR, fifo);
|
dw_writel(dws, DW_SPI_TXFLTR, fifo);
|
||||||
if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
|
if (fifo != dw_readl(dws, DW_SPI_TXFLTR))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dw_writew(dws, DW_SPI_TXFLTR, 0);
|
dw_writel(dws, DW_SPI_TXFLTR, 0);
|
||||||
|
|
||||||
dws->fifo_len = (fifo == 1) ? 0 : fifo;
|
dws->fifo_len = (fifo == 1) ? 0 : fifo;
|
||||||
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
||||||
|
@ -646,13 +523,12 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||||
|
|
||||||
dws->master = master;
|
dws->master = master;
|
||||||
dws->type = SSI_MOTO_SPI;
|
dws->type = SSI_MOTO_SPI;
|
||||||
dws->prev_chip = NULL;
|
|
||||||
dws->dma_inited = 0;
|
dws->dma_inited = 0;
|
||||||
dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
|
dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
|
||||||
snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
|
snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
|
||||||
|
|
||||||
ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
|
ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
|
||||||
dws->name, dws);
|
dws->name, master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&master->dev, "can not get IRQ\n");
|
dev_err(&master->dev, "can not get IRQ\n");
|
||||||
goto err_free_master;
|
goto err_free_master;
|
||||||
|
@ -664,7 +540,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||||
master->num_chipselect = dws->num_cs;
|
master->num_chipselect = dws->num_cs;
|
||||||
master->setup = dw_spi_setup;
|
master->setup = dw_spi_setup;
|
||||||
master->cleanup = dw_spi_cleanup;
|
master->cleanup = dw_spi_cleanup;
|
||||||
master->transfer_one_message = dw_spi_transfer_one_message;
|
master->set_cs = dw_spi_set_cs;
|
||||||
|
master->transfer_one = dw_spi_transfer_one;
|
||||||
|
master->handle_err = dw_spi_handle_err;
|
||||||
master->max_speed_hz = dws->max_freq;
|
master->max_speed_hz = dws->max_freq;
|
||||||
master->dev.of_node = dev->of_node;
|
master->dev.of_node = dev->of_node;
|
||||||
|
|
||||||
|
@ -676,11 +554,11 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_warn(dev, "DMA init failed\n");
|
dev_warn(dev, "DMA init failed\n");
|
||||||
dws->dma_inited = 0;
|
dws->dma_inited = 0;
|
||||||
|
} else {
|
||||||
|
master->can_dma = dws->dma_ops->can_dma;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws);
|
|
||||||
|
|
||||||
spi_master_set_devdata(master, dws);
|
spi_master_set_devdata(master, dws);
|
||||||
ret = devm_spi_register_master(dev, master);
|
ret = devm_spi_register_master(dev, master);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -91,12 +91,15 @@ struct dw_spi;
|
||||||
struct dw_spi_dma_ops {
|
struct dw_spi_dma_ops {
|
||||||
int (*dma_init)(struct dw_spi *dws);
|
int (*dma_init)(struct dw_spi *dws);
|
||||||
void (*dma_exit)(struct dw_spi *dws);
|
void (*dma_exit)(struct dw_spi *dws);
|
||||||
int (*dma_transfer)(struct dw_spi *dws, int cs_change);
|
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
|
||||||
|
bool (*can_dma)(struct spi_master *master, struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer);
|
||||||
|
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
|
||||||
|
void (*dma_stop)(struct dw_spi *dws);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dw_spi {
|
struct dw_spi {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct spi_device *cur_dev;
|
|
||||||
enum dw_ssi_type type;
|
enum dw_ssi_type type;
|
||||||
char name[16];
|
char name[16];
|
||||||
|
|
||||||
|
@ -109,41 +112,26 @@ struct dw_spi {
|
||||||
u16 bus_num;
|
u16 bus_num;
|
||||||
u16 num_cs; /* supported slave numbers */
|
u16 num_cs; /* supported slave numbers */
|
||||||
|
|
||||||
/* Message Transfer pump */
|
|
||||||
struct tasklet_struct pump_transfers;
|
|
||||||
|
|
||||||
/* Current message transfer state info */
|
/* Current message transfer state info */
|
||||||
struct spi_message *cur_msg;
|
|
||||||
struct spi_transfer *cur_transfer;
|
|
||||||
struct chip_data *cur_chip;
|
|
||||||
struct chip_data *prev_chip;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
void *tx;
|
void *tx;
|
||||||
void *tx_end;
|
void *tx_end;
|
||||||
void *rx;
|
void *rx;
|
||||||
void *rx_end;
|
void *rx_end;
|
||||||
int dma_mapped;
|
int dma_mapped;
|
||||||
dma_addr_t rx_dma;
|
|
||||||
dma_addr_t tx_dma;
|
|
||||||
size_t rx_map_len;
|
|
||||||
size_t tx_map_len;
|
|
||||||
u8 n_bytes; /* current is a 1/2 bytes op */
|
u8 n_bytes; /* current is a 1/2 bytes op */
|
||||||
u8 max_bits_per_word; /* maxim is 16b */
|
|
||||||
u32 dma_width;
|
u32 dma_width;
|
||||||
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
|
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
|
||||||
void (*cs_control)(u32 command);
|
|
||||||
|
|
||||||
/* Dma info */
|
/* DMA info */
|
||||||
int dma_inited;
|
int dma_inited;
|
||||||
struct dma_chan *txchan;
|
struct dma_chan *txchan;
|
||||||
struct scatterlist tx_sgl;
|
|
||||||
struct dma_chan *rxchan;
|
struct dma_chan *rxchan;
|
||||||
struct scatterlist rx_sgl;
|
|
||||||
unsigned long dma_chan_busy;
|
unsigned long dma_chan_busy;
|
||||||
struct device *dma_dev;
|
|
||||||
dma_addr_t dma_addr; /* phy address of the Data register */
|
dma_addr_t dma_addr; /* phy address of the Data register */
|
||||||
struct dw_spi_dma_ops *dma_ops;
|
struct dw_spi_dma_ops *dma_ops;
|
||||||
void *dma_priv; /* platform relate info */
|
void *dma_tx;
|
||||||
|
void *dma_rx;
|
||||||
|
|
||||||
/* Bus interface info */
|
/* Bus interface info */
|
||||||
void *priv;
|
void *priv;
|
||||||
|
@ -162,16 +150,6 @@ static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
|
||||||
__raw_writel(val, dws->regs + offset);
|
__raw_writel(val, dws->regs + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u16 dw_readw(struct dw_spi *dws, u32 offset)
|
|
||||||
{
|
|
||||||
return __raw_readw(dws->regs + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val)
|
|
||||||
{
|
|
||||||
__raw_writew(val, dws->regs + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
|
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
|
||||||
{
|
{
|
||||||
dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
|
dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
|
||||||
|
@ -182,22 +160,6 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
|
||||||
dw_writel(dws, DW_SPI_BAUDR, div);
|
dw_writel(dws, DW_SPI_BAUDR, div);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
|
|
||||||
int active)
|
|
||||||
{
|
|
||||||
u16 cs = spi->chip_select;
|
|
||||||
int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
|
|
||||||
!(spi->mode & SPI_CS_HIGH);
|
|
||||||
|
|
||||||
if (dws->cs_control)
|
|
||||||
dws->cs_control(active);
|
|
||||||
if (gpio_is_valid(spi->cs_gpio))
|
|
||||||
gpio_set_value(spi->cs_gpio, gpio_val);
|
|
||||||
|
|
||||||
if (active)
|
|
||||||
dw_writel(dws, DW_SPI_SER, 1 << cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable IRQ bits */
|
/* Disable IRQ bits */
|
||||||
static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
|
static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
|
||||||
{
|
{
|
||||||
|
@ -216,16 +178,27 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
|
||||||
dw_writel(dws, DW_SPI_IMR, new_mask);
|
dw_writel(dws, DW_SPI_IMR, new_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This does disable the SPI controller, interrupts, and re-enable the
|
||||||
|
* controller back. Transmit and receive FIFO buffers are cleared when the
|
||||||
|
* device is disabled.
|
||||||
|
*/
|
||||||
|
static inline void spi_reset_chip(struct dw_spi *dws)
|
||||||
|
{
|
||||||
|
spi_enable_chip(dws, 0);
|
||||||
|
spi_mask_intr(dws, 0xff);
|
||||||
|
spi_enable_chip(dws, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each SPI slave device to work with dw_api controller should
|
* Each SPI slave device to work with dw_api controller should
|
||||||
* has such a structure claiming its working mode (PIO/DMA etc),
|
* has such a structure claiming its working mode (poll or PIO/DMA),
|
||||||
* which can be save in the "controller_data" member of the
|
* which can be save in the "controller_data" member of the
|
||||||
* struct spi_device.
|
* struct spi_device.
|
||||||
*/
|
*/
|
||||||
struct dw_spi_chip {
|
struct dw_spi_chip {
|
||||||
u8 poll_mode; /* 1 for controller polling mode */
|
u8 poll_mode; /* 1 for controller polling mode */
|
||||||
u8 type; /* SPI/SSP/MicroWire */
|
u8 type; /* SPI/SSP/MicroWire */
|
||||||
u8 enable_dma;
|
|
||||||
void (*cs_control)(u32 command);
|
void (*cs_control)(u32 command);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,7 +206,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
|
||||||
extern void dw_spi_remove_host(struct dw_spi *dws);
|
extern void dw_spi_remove_host(struct dw_spi *dws);
|
||||||
extern int dw_spi_suspend_host(struct dw_spi *dws);
|
extern int dw_spi_suspend_host(struct dw_spi *dws);
|
||||||
extern int dw_spi_resume_host(struct dw_spi *dws);
|
extern int dw_spi_resume_host(struct dw_spi *dws);
|
||||||
extern void dw_spi_xfer_done(struct dw_spi *dws);
|
|
||||||
|
|
||||||
/* platform related setup */
|
/* platform related setup */
|
||||||
extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */
|
extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/math64.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "fsl-dspi"
|
#define DRIVER_NAME "fsl-dspi"
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@
|
||||||
#define SPI_CTAR_CPOL(x) ((x) << 26)
|
#define SPI_CTAR_CPOL(x) ((x) << 26)
|
||||||
#define SPI_CTAR_CPHA(x) ((x) << 25)
|
#define SPI_CTAR_CPHA(x) ((x) << 25)
|
||||||
#define SPI_CTAR_LSBFE(x) ((x) << 24)
|
#define SPI_CTAR_LSBFE(x) ((x) << 24)
|
||||||
#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22)
|
#define SPI_CTAR_PCSSCK(x) (((x) & 0x00000003) << 22)
|
||||||
#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20)
|
#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20)
|
||||||
#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18)
|
#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18)
|
||||||
#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16)
|
#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16)
|
||||||
|
@ -59,6 +61,7 @@
|
||||||
#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8)
|
#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8)
|
||||||
#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4)
|
#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4)
|
||||||
#define SPI_CTAR_BR(x) ((x) & 0x0000000f)
|
#define SPI_CTAR_BR(x) ((x) & 0x0000000f)
|
||||||
|
#define SPI_CTAR_SCALE_BITS 0xf
|
||||||
|
|
||||||
#define SPI_CTAR0_SLAVE 0x0c
|
#define SPI_CTAR0_SLAVE 0x0c
|
||||||
|
|
||||||
|
@ -148,24 +151,67 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
||||||
16, 32, 64, 128,
|
16, 32, 64, 128,
|
||||||
256, 512, 1024, 2048,
|
256, 512, 1024, 2048,
|
||||||
4096, 8192, 16384, 32768 };
|
4096, 8192, 16384, 32768 };
|
||||||
int temp, i = 0, j = 0;
|
int scale_needed, scale, minscale = INT_MAX;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
temp = clkrate / 2 / speed_hz;
|
scale_needed = clkrate / speed_hz;
|
||||||
|
if (clkrate % speed_hz)
|
||||||
|
scale_needed++;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++)
|
for (i = 0; i < ARRAY_SIZE(brs); i++)
|
||||||
for (j = 0; j < ARRAY_SIZE(brs); j++) {
|
for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) {
|
||||||
if (pbr_tbl[i] * brs[j] >= temp) {
|
scale = brs[i] * pbr_tbl[j];
|
||||||
*pbr = i;
|
if (scale >= scale_needed) {
|
||||||
*br = j;
|
if (scale < minscale) {
|
||||||
return;
|
minscale = scale;
|
||||||
|
*br = i;
|
||||||
|
*pbr = j;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld\
|
if (minscale == INT_MAX) {
|
||||||
,we use the max prescaler value.\n", speed_hz, clkrate);
|
pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld, we use the max prescaler value.\n",
|
||||||
|
speed_hz, clkrate);
|
||||||
*pbr = ARRAY_SIZE(pbr_tbl) - 1;
|
*pbr = ARRAY_SIZE(pbr_tbl) - 1;
|
||||||
*br = ARRAY_SIZE(brs) - 1;
|
*br = ARRAY_SIZE(brs) - 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ns_delay_scale(char *psc, char *sc, int delay_ns,
|
||||||
|
unsigned long clkrate)
|
||||||
|
{
|
||||||
|
int pscale_tbl[4] = {1, 3, 5, 7};
|
||||||
|
int scale_needed, scale, minscale = INT_MAX;
|
||||||
|
int i, j;
|
||||||
|
u32 remainder;
|
||||||
|
|
||||||
|
scale_needed = div_u64_rem((u64)delay_ns * clkrate, NSEC_PER_SEC,
|
||||||
|
&remainder);
|
||||||
|
if (remainder)
|
||||||
|
scale_needed++;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pscale_tbl); i++)
|
||||||
|
for (j = 0; j <= SPI_CTAR_SCALE_BITS; j++) {
|
||||||
|
scale = pscale_tbl[i] * (2 << j);
|
||||||
|
if (scale >= scale_needed) {
|
||||||
|
if (scale < minscale) {
|
||||||
|
minscale = scale;
|
||||||
|
*psc = i;
|
||||||
|
*sc = j;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minscale == INT_MAX) {
|
||||||
|
pr_warn("Cannot find correct scale values for %dns delay at clkrate %ld, using max prescaler value",
|
||||||
|
delay_ns, clkrate);
|
||||||
|
*psc = ARRAY_SIZE(pscale_tbl) - 1;
|
||||||
|
*sc = SPI_CTAR_SCALE_BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int dspi_transfer_write(struct fsl_dspi *dspi)
|
static int dspi_transfer_write(struct fsl_dspi *dspi)
|
||||||
{
|
{
|
||||||
|
@ -345,7 +391,10 @@ static int dspi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||||
unsigned char br = 0, pbr = 0, fmsz = 0;
|
u32 cs_sck_delay = 0, sck_cs_delay = 0;
|
||||||
|
unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0;
|
||||||
|
unsigned char pasc = 0, asc = 0, fmsz = 0;
|
||||||
|
unsigned long clkrate;
|
||||||
|
|
||||||
if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
|
if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
|
||||||
fmsz = spi->bits_per_word - 1;
|
fmsz = spi->bits_per_word - 1;
|
||||||
|
@ -362,18 +411,34 @@ static int dspi_setup(struct spi_device *spi)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
|
||||||
|
&cs_sck_delay);
|
||||||
|
|
||||||
|
of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
|
||||||
|
&sck_cs_delay);
|
||||||
|
|
||||||
chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS |
|
chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS |
|
||||||
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
|
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
|
||||||
|
|
||||||
chip->void_write_data = 0;
|
chip->void_write_data = 0;
|
||||||
|
|
||||||
hz_to_spi_baud(&pbr, &br,
|
clkrate = clk_get_rate(dspi->clk);
|
||||||
spi->max_speed_hz, clk_get_rate(dspi->clk));
|
hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
|
||||||
|
|
||||||
|
/* Set PCS to SCK delay scale values */
|
||||||
|
ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate);
|
||||||
|
|
||||||
|
/* Set After SCK delay scale values */
|
||||||
|
ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate);
|
||||||
|
|
||||||
chip->ctar_val = SPI_CTAR_FMSZ(fmsz)
|
chip->ctar_val = SPI_CTAR_FMSZ(fmsz)
|
||||||
| SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0)
|
| SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0)
|
||||||
| SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0)
|
| SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0)
|
||||||
| SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0)
|
| SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0)
|
||||||
|
| SPI_CTAR_PCSSCK(pcssck)
|
||||||
|
| SPI_CTAR_CSSCK(cssck)
|
||||||
|
| SPI_CTAR_PASC(pasc)
|
||||||
|
| SPI_CTAR_ASC(asc)
|
||||||
| SPI_CTAR_PBR(pbr)
|
| SPI_CTAR_PBR(pbr)
|
||||||
| SPI_CTAR_BR(br);
|
| SPI_CTAR_BR(br);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -122,36 +123,31 @@ static inline void spfi_start(struct img_spfi *spfi)
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
spfi_writel(spfi, val, SPFI_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spfi_stop(struct img_spfi *spfi)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_CONTROL);
|
|
||||||
val &= ~SPFI_CONTROL_SPFI_EN;
|
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void spfi_reset(struct img_spfi *spfi)
|
static inline void spfi_reset(struct img_spfi *spfi)
|
||||||
{
|
{
|
||||||
spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL);
|
spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL);
|
||||||
udelay(1);
|
|
||||||
spfi_writel(spfi, 0, SPFI_CONTROL);
|
spfi_writel(spfi, 0, SPFI_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spfi_flush_tx_fifo(struct img_spfi *spfi)
|
static int spfi_wait_all_done(struct img_spfi *spfi)
|
||||||
{
|
{
|
||||||
unsigned long timeout = jiffies + msecs_to_jiffies(10);
|
unsigned long timeout = jiffies + msecs_to_jiffies(50);
|
||||||
|
|
||||||
spfi_writel(spfi, SPFI_INTERRUPT_SDE, SPFI_INTERRUPT_CLEAR);
|
|
||||||
while (time_before(jiffies, timeout)) {
|
while (time_before(jiffies, timeout)) {
|
||||||
if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) &
|
u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS);
|
||||||
SPFI_INTERRUPT_SDE)
|
|
||||||
return;
|
if (status & SPFI_INTERRUPT_ALLDONETRIG) {
|
||||||
|
spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG,
|
||||||
|
SPFI_INTERRUPT_CLEAR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_err(spfi->dev, "Timed out waiting for FIFO to drain\n");
|
dev_err(spfi->dev, "Timed out waiting for transaction to complete\n");
|
||||||
spfi_reset(spfi);
|
spfi_reset(spfi);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf,
|
static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf,
|
||||||
|
@ -237,6 +233,7 @@ static int img_spfi_start_pio(struct spi_master *master,
|
||||||
const void *tx_buf = xfer->tx_buf;
|
const void *tx_buf = xfer->tx_buf;
|
||||||
void *rx_buf = xfer->rx_buf;
|
void *rx_buf = xfer->rx_buf;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (tx_buf)
|
if (tx_buf)
|
||||||
tx_bytes = xfer->len;
|
tx_bytes = xfer->len;
|
||||||
|
@ -269,16 +266,15 @@ static int img_spfi_start_pio(struct spi_master *master,
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = spfi_wait_all_done(spfi);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (rx_bytes > 0 || tx_bytes > 0) {
|
if (rx_bytes > 0 || tx_bytes > 0) {
|
||||||
dev_err(spfi->dev, "PIO transfer timed out\n");
|
dev_err(spfi->dev, "PIO transfer timed out\n");
|
||||||
spfi_reset(spfi);
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx_buf)
|
|
||||||
spfi_flush_tx_fifo(spfi);
|
|
||||||
spfi_stop(spfi);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,14 +283,12 @@ static void img_spfi_dma_rx_cb(void *data)
|
||||||
struct img_spfi *spfi = data;
|
struct img_spfi *spfi = data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
spfi_wait_all_done(spfi);
|
||||||
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
|
||||||
spfi->rx_dma_busy = false;
|
spfi->rx_dma_busy = false;
|
||||||
if (!spfi->tx_dma_busy) {
|
if (!spfi->tx_dma_busy)
|
||||||
spfi_stop(spfi);
|
|
||||||
spi_finalize_current_transfer(spfi->master);
|
spi_finalize_current_transfer(spfi->master);
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,16 +297,12 @@ static void img_spfi_dma_tx_cb(void *data)
|
||||||
struct img_spfi *spfi = data;
|
struct img_spfi *spfi = data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spfi_flush_tx_fifo(spfi);
|
spfi_wait_all_done(spfi);
|
||||||
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
|
||||||
spfi->tx_dma_busy = false;
|
spfi->tx_dma_busy = false;
|
||||||
if (!spfi->rx_dma_busy) {
|
if (!spfi->rx_dma_busy)
|
||||||
spfi_stop(spfi);
|
|
||||||
spi_finalize_current_transfer(spfi->master);
|
spi_finalize_current_transfer(spfi->master);
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +387,75 @@ static int img_spfi_start_dma(struct spi_master *master,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void img_spfi_handle_err(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop all DMA and reset the controller if the previous transaction
|
||||||
|
* timed-out and never completed it's DMA.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&spfi->lock, flags);
|
||||||
|
if (spfi->tx_dma_busy || spfi->rx_dma_busy) {
|
||||||
|
spfi->tx_dma_busy = false;
|
||||||
|
spfi->rx_dma_busy = false;
|
||||||
|
|
||||||
|
dmaengine_terminate_all(spfi->tx_ch);
|
||||||
|
dmaengine_terminate_all(spfi->rx_ch);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&spfi->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
||||||
|
if (msg->spi->mode & SPI_CPHA)
|
||||||
|
val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
|
||||||
|
else
|
||||||
|
val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
|
||||||
|
if (msg->spi->mode & SPI_CPOL)
|
||||||
|
val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
|
||||||
|
else
|
||||||
|
val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
|
||||||
|
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_unprepare(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct img_spfi *spfi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
spfi_reset(spfi);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_spfi_setup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ?
|
||||||
|
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
|
||||||
|
dev_name(&spi->dev));
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev, "can't request chipselect gpio %d\n",
|
||||||
|
spi->cs_gpio);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void img_spfi_cleanup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
gpio_free(spi->cs_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
@ -405,10 +464,10 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* output = spfi_clk * (BITCLK / 512), where BITCLK must be a
|
* output = spfi_clk * (BITCLK / 512), where BITCLK must be a
|
||||||
* power of 2 up to 256 (where 255 == 256 since BITCLK is 8 bits)
|
* power of 2 up to 128
|
||||||
*/
|
*/
|
||||||
div = DIV_ROUND_UP(master->max_speed_hz, xfer->speed_hz);
|
div = DIV_ROUND_UP(clk_get_rate(spfi->spfi_clk), xfer->speed_hz);
|
||||||
div = clamp(512 / (1 << get_count_order(div)), 1, 255);
|
div = clamp(512 / (1 << get_count_order(div)), 1, 128);
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
||||||
val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK <<
|
val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK <<
|
||||||
|
@ -416,6 +475,9 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
|
val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
|
||||||
spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
|
||||||
|
|
||||||
|
spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT,
|
||||||
|
SPFI_TRANSACTION);
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_CONTROL);
|
val = spfi_readl(spfi, SPFI_CONTROL);
|
||||||
val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA);
|
val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA);
|
||||||
if (xfer->tx_buf)
|
if (xfer->tx_buf)
|
||||||
|
@ -429,25 +491,7 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||||
else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
|
else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
|
||||||
xfer->rx_nbits == SPI_NBITS_QUAD)
|
xfer->rx_nbits == SPI_NBITS_QUAD)
|
||||||
val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
|
val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
|
||||||
val &= ~SPFI_CONTROL_CONTINUE;
|
|
||||||
if (!xfer->cs_change && !list_is_last(&xfer->transfer_list,
|
|
||||||
&master->cur_msg->transfers))
|
|
||||||
val |= SPFI_CONTROL_CONTINUE;
|
|
||||||
spfi_writel(spfi, val, SPFI_CONTROL);
|
spfi_writel(spfi, val, SPFI_CONTROL);
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
|
||||||
if (spi->mode & SPI_CPHA)
|
|
||||||
val |= SPFI_PORT_STATE_CK_PHASE(spi->chip_select);
|
|
||||||
else
|
|
||||||
val &= ~SPFI_PORT_STATE_CK_PHASE(spi->chip_select);
|
|
||||||
if (spi->mode & SPI_CPOL)
|
|
||||||
val |= SPFI_PORT_STATE_CK_POL(spi->chip_select);
|
|
||||||
else
|
|
||||||
val &= ~SPFI_PORT_STATE_CK_POL(spi->chip_select);
|
|
||||||
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
|
||||||
|
|
||||||
spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT,
|
|
||||||
SPFI_TRANSACTION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int img_spfi_transfer_one(struct spi_master *master,
|
static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
|
@ -455,8 +499,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
||||||
bool dma_reset = false;
|
|
||||||
unsigned long flags;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
|
if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
|
||||||
|
@ -466,23 +508,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Stop all DMA and reset the controller if the previous transaction
|
|
||||||
* timed-out and never completed it's DMA.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&spfi->lock, flags);
|
|
||||||
if (spfi->tx_dma_busy || spfi->rx_dma_busy) {
|
|
||||||
dev_err(spfi->dev, "SPI DMA still busy\n");
|
|
||||||
dma_reset = true;
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&spfi->lock, flags);
|
|
||||||
|
|
||||||
if (dma_reset) {
|
|
||||||
dmaengine_terminate_all(spfi->tx_ch);
|
|
||||||
dmaengine_terminate_all(spfi->rx_ch);
|
|
||||||
spfi_reset(spfi);
|
|
||||||
}
|
|
||||||
|
|
||||||
img_spfi_config(master, spi, xfer);
|
img_spfi_config(master, spi, xfer);
|
||||||
if (master->can_dma && master->can_dma(master, spi, xfer))
|
if (master->can_dma && master->can_dma(master, spi, xfer))
|
||||||
ret = img_spfi_start_dma(master, spi, xfer);
|
ret = img_spfi_start_dma(master, spi, xfer);
|
||||||
|
@ -492,17 +517,6 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void img_spfi_set_cs(struct spi_device *spi, bool enable)
|
|
||||||
{
|
|
||||||
struct img_spfi *spfi = spi_master_get_devdata(spi->master);
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
val = spfi_readl(spfi, SPFI_PORT_STATE);
|
|
||||||
val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << SPFI_PORT_STATE_DEV_SEL_SHIFT);
|
|
||||||
val |= spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT;
|
|
||||||
spfi_writel(spfi, val, SPFI_PORT_STATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi,
|
static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
@ -591,14 +605,17 @@ static int img_spfi_probe(struct platform_device *pdev)
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL;
|
||||||
if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode"))
|
if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode"))
|
||||||
master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD;
|
master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD;
|
||||||
master->num_chipselect = 5;
|
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8);
|
||||||
master->max_speed_hz = clk_get_rate(spfi->spfi_clk);
|
master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4;
|
||||||
master->min_speed_hz = master->max_speed_hz / 512;
|
master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512;
|
||||||
|
|
||||||
master->set_cs = img_spfi_set_cs;
|
master->setup = img_spfi_setup;
|
||||||
|
master->cleanup = img_spfi_cleanup;
|
||||||
master->transfer_one = img_spfi_transfer_one;
|
master->transfer_one = img_spfi_transfer_one;
|
||||||
|
master->prepare_message = img_spfi_prepare;
|
||||||
|
master->unprepare_message = img_spfi_unprepare;
|
||||||
|
master->handle_err = img_spfi_handle_err;
|
||||||
|
|
||||||
spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx");
|
spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx");
|
||||||
spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx");
|
spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx");
|
||||||
|
|
|
@ -370,8 +370,6 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||||
if (spi_imx->dma_is_inited) {
|
if (spi_imx->dma_is_inited) {
|
||||||
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
||||||
|
|
||||||
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
||||||
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
||||||
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
||||||
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
||||||
|
@ -868,6 +866,8 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
||||||
master->max_dma_len = MAX_SDMA_BD_BYTES;
|
master->max_dma_len = MAX_SDMA_BD_BYTES;
|
||||||
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
|
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
|
||||||
SPI_MASTER_MUST_TX;
|
SPI_MASTER_MUST_TX;
|
||||||
|
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
|
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||||
spi_imx->dma_is_inited = 1;
|
spi_imx->dma_is_inited = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -903,7 +903,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
||||||
|
|
||||||
if (tx) {
|
if (tx) {
|
||||||
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
|
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
|
||||||
tx->sgl, tx->nents, DMA_TO_DEVICE,
|
tx->sgl, tx->nents, DMA_MEM_TO_DEV,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_tx)
|
if (!desc_tx)
|
||||||
goto no_dma;
|
goto no_dma;
|
||||||
|
@ -915,7 +915,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
||||||
|
|
||||||
if (rx) {
|
if (rx) {
|
||||||
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
|
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
|
||||||
rx->sgl, rx->nents, DMA_FROM_DEVICE,
|
rx->sgl, rx->nents, DMA_DEV_TO_MEM,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_rx)
|
if (!desc_rx)
|
||||||
goto no_dma;
|
goto no_dma;
|
||||||
|
|
|
@ -588,7 +588,7 @@ static int mpc512x_psc_spi_of_remove(struct platform_device *op)
|
||||||
return mpc512x_psc_spi_do_remove(&op->dev);
|
return mpc512x_psc_spi_do_remove(&op->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id mpc512x_psc_spi_of_match[] = {
|
static const struct of_device_id mpc512x_psc_spi_of_match[] = {
|
||||||
{ .compatible = "fsl,mpc5121-psc-spi", },
|
{ .compatible = "fsl,mpc5121-psc-spi", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -238,7 +238,7 @@ static int octeon_spi_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id octeon_spi_match[] = {
|
static const struct of_device_id octeon_spi_match[] = {
|
||||||
{ .compatible = "cavium,octeon-3010-spi", },
|
{ .compatible = "cavium,octeon-3010-spi", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -294,16 +295,6 @@ static int omap1_spi100k_setup(struct spi_device *spi)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap1_spi100k_prepare_hardware(struct spi_master *master)
|
|
||||||
{
|
|
||||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
|
||||||
|
|
||||||
clk_prepare_enable(spi100k->ick);
|
|
||||||
clk_prepare_enable(spi100k->fck);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int omap1_spi100k_transfer_one_message(struct spi_master *master,
|
static int omap1_spi100k_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *m)
|
struct spi_message *m)
|
||||||
{
|
{
|
||||||
|
@ -372,16 +363,6 @@ static int omap1_spi100k_transfer_one_message(struct spi_master *master,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap1_spi100k_unprepare_hardware(struct spi_master *master)
|
|
||||||
{
|
|
||||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
|
||||||
|
|
||||||
clk_disable_unprepare(spi100k->ick);
|
|
||||||
clk_disable_unprepare(spi100k->fck);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int omap1_spi100k_probe(struct platform_device *pdev)
|
static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
@ -402,14 +383,12 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
master->setup = omap1_spi100k_setup;
|
master->setup = omap1_spi100k_setup;
|
||||||
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
||||||
master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware;
|
|
||||||
master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware;
|
|
||||||
master->cleanup = NULL;
|
|
||||||
master->num_chipselect = 2;
|
master->num_chipselect = 2;
|
||||||
master->mode_bits = MODEBITS;
|
master->mode_bits = MODEBITS;
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||||
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
|
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
|
||||||
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(master);
|
spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
@ -434,22 +413,96 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status = clk_prepare_enable(spi100k->ick);
|
||||||
|
if (status != 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to enable ick: %d\n", status);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = clk_prepare_enable(spi100k->fck);
|
||||||
|
if (status != 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to enable fck: %d\n", status);
|
||||||
|
goto err_ick;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
|
||||||
status = devm_spi_register_master(&pdev->dev, master);
|
status = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto err;
|
goto err_fck;
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
err_fck:
|
||||||
|
clk_disable_unprepare(spi100k->fck);
|
||||||
|
err_ick:
|
||||||
|
clk_disable_unprepare(spi100k->ick);
|
||||||
err:
|
err:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int omap1_spi100k_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||||
|
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(spi100k->fck);
|
||||||
|
clk_disable_unprepare(spi100k->ick);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int omap1_spi100k_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||||
|
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
clk_disable_unprepare(spi100k->ick);
|
||||||
|
clk_disable_unprepare(spi100k->fck);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int omap1_spi100k_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||||
|
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(spi100k->ick);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(dev, "Failed to enable ick: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(spi100k->fck);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(dev, "Failed to enable fck: %d\n", ret);
|
||||||
|
clk_disable_unprepare(spi100k->ick);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops omap1_spi100k_pm = {
|
||||||
|
SET_RUNTIME_PM_OPS(omap1_spi100k_runtime_suspend,
|
||||||
|
omap1_spi100k_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver omap1_spi100k_driver = {
|
static struct platform_driver omap1_spi100k_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "omap1_spi100k",
|
.name = "omap1_spi100k",
|
||||||
|
.pm = &omap1_spi100k_pm,
|
||||||
},
|
},
|
||||||
.probe = omap1_spi100k_probe,
|
.probe = omap1_spi100k_probe,
|
||||||
|
.remove = omap1_spi100k_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(omap1_spi100k_driver);
|
module_platform_driver(omap1_spi100k_driver);
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,12 @@
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_SSP_REG_IMSC 0x0UL
|
#define DEFAULT_SSP_REG_IMSC 0x0UL
|
||||||
#define DISABLE_ALL_INTERRUPTS DEFAULT_SSP_REG_IMSC
|
#define DISABLE_ALL_INTERRUPTS DEFAULT_SSP_REG_IMSC
|
||||||
#define ENABLE_ALL_INTERRUPTS (~DEFAULT_SSP_REG_IMSC)
|
#define ENABLE_ALL_INTERRUPTS ( \
|
||||||
|
SSP_IMSC_MASK_RORIM | \
|
||||||
|
SSP_IMSC_MASK_RTIM | \
|
||||||
|
SSP_IMSC_MASK_RXIM | \
|
||||||
|
SSP_IMSC_MASK_TXIM \
|
||||||
|
)
|
||||||
|
|
||||||
#define CLEAR_ALL_INTERRUPTS 0x3
|
#define CLEAR_ALL_INTERRUPTS 0x3
|
||||||
|
|
||||||
|
@ -1251,7 +1256,6 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||||
struct pl022 *pl022 = dev_id;
|
struct pl022 *pl022 = dev_id;
|
||||||
struct spi_message *msg = pl022->cur_msg;
|
struct spi_message *msg = pl022->cur_msg;
|
||||||
u16 irq_status = 0;
|
u16 irq_status = 0;
|
||||||
u16 flag = 0;
|
|
||||||
|
|
||||||
if (unlikely(!msg)) {
|
if (unlikely(!msg)) {
|
||||||
dev_err(&pl022->adev->dev,
|
dev_err(&pl022->adev->dev,
|
||||||
|
@ -1280,9 +1284,6 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||||
if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
|
if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
|
||||||
dev_err(&pl022->adev->dev,
|
dev_err(&pl022->adev->dev,
|
||||||
"RXFIFO is full\n");
|
"RXFIFO is full\n");
|
||||||
if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF)
|
|
||||||
dev_err(&pl022->adev->dev,
|
|
||||||
"TXFIFO is full\n");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable and clear interrupts, disable SSP,
|
* Disable and clear interrupts, disable SSP,
|
||||||
|
@ -1303,8 +1304,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||||
|
|
||||||
readwriter(pl022);
|
readwriter(pl022);
|
||||||
|
|
||||||
if ((pl022->tx == pl022->tx_end) && (flag == 0)) {
|
if (pl022->tx == pl022->tx_end) {
|
||||||
flag = 1;
|
|
||||||
/* Disable Transmit interrupt, enable receive interrupt */
|
/* Disable Transmit interrupt, enable receive interrupt */
|
||||||
writew((readw(SSP_IMSC(pl022->virtbase)) &
|
writew((readw(SSP_IMSC(pl022->virtbase)) &
|
||||||
~SSP_IMSC_MASK_TXIM) | SSP_IMSC_MASK_RXIM,
|
~SSP_IMSC_MASK_TXIM) | SSP_IMSC_MASK_RXIM,
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
#include <linux/spi/pxa2xx_spi.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
@ -30,10 +31,6 @@
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
|
||||||
#include <asm/irq.h>
|
|
||||||
#include <asm/delay.h>
|
|
||||||
|
|
||||||
#include "spi-pxa2xx.h"
|
#include "spi-pxa2xx.h"
|
||||||
|
|
||||||
MODULE_AUTHOR("Stephen Street");
|
MODULE_AUTHOR("Stephen Street");
|
||||||
|
@ -67,54 +64,6 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
||||||
#define LPSS_TX_LOTHRESH_DFLT 160
|
#define LPSS_TX_LOTHRESH_DFLT 160
|
||||||
#define LPSS_TX_HITHRESH_DFLT 224
|
#define LPSS_TX_HITHRESH_DFLT 224
|
||||||
|
|
||||||
struct quark_spi_rate {
|
|
||||||
u32 bitrate;
|
|
||||||
u32 dds_clk_rate;
|
|
||||||
u32 clk_div;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 'rate', 'dds', 'clk_div' lookup table, which is defined in
|
|
||||||
* the Quark SPI datasheet.
|
|
||||||
*/
|
|
||||||
static const struct quark_spi_rate quark_spi_rate_table[] = {
|
|
||||||
/* bitrate, dds_clk_rate, clk_div */
|
|
||||||
{50000000, 0x800000, 0},
|
|
||||||
{40000000, 0x666666, 0},
|
|
||||||
{25000000, 0x400000, 0},
|
|
||||||
{20000000, 0x666666, 1},
|
|
||||||
{16667000, 0x800000, 2},
|
|
||||||
{13333000, 0x666666, 2},
|
|
||||||
{12500000, 0x200000, 0},
|
|
||||||
{10000000, 0x800000, 4},
|
|
||||||
{8000000, 0x666666, 4},
|
|
||||||
{6250000, 0x400000, 3},
|
|
||||||
{5000000, 0x400000, 4},
|
|
||||||
{4000000, 0x666666, 9},
|
|
||||||
{3125000, 0x80000, 0},
|
|
||||||
{2500000, 0x400000, 9},
|
|
||||||
{2000000, 0x666666, 19},
|
|
||||||
{1563000, 0x40000, 0},
|
|
||||||
{1250000, 0x200000, 9},
|
|
||||||
{1000000, 0x400000, 24},
|
|
||||||
{800000, 0x666666, 49},
|
|
||||||
{781250, 0x20000, 0},
|
|
||||||
{625000, 0x200000, 19},
|
|
||||||
{500000, 0x400000, 49},
|
|
||||||
{400000, 0x666666, 99},
|
|
||||||
{390625, 0x10000, 0},
|
|
||||||
{250000, 0x400000, 99},
|
|
||||||
{200000, 0x666666, 199},
|
|
||||||
{195313, 0x8000, 0},
|
|
||||||
{125000, 0x100000, 49},
|
|
||||||
{100000, 0x200000, 124},
|
|
||||||
{50000, 0x100000, 124},
|
|
||||||
{25000, 0x80000, 124},
|
|
||||||
{10016, 0x20000, 77},
|
|
||||||
{5040, 0x20000, 154},
|
|
||||||
{1002, 0x8000, 194},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Offset from drv_data->lpss_base */
|
/* Offset from drv_data->lpss_base */
|
||||||
#define GENERAL_REG 0x08
|
#define GENERAL_REG 0x08
|
||||||
#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
||||||
|
@ -701,25 +650,124 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Quark SPI data sheet gives a table, and for the given 'rate',
|
* The Quark SPI has an additional 24 bit register (DDS_CLK_RATE) to multiply
|
||||||
* the 'dds' and 'clk_div' can be found in the table.
|
* input frequency by fractions of 2^24. It also has a divider by 5.
|
||||||
|
*
|
||||||
|
* There are formulas to get baud rate value for given input frequency and
|
||||||
|
* divider parameters, such as DDS_CLK_RATE and SCR:
|
||||||
|
*
|
||||||
|
* Fsys = 200MHz
|
||||||
|
*
|
||||||
|
* Fssp = Fsys * DDS_CLK_RATE / 2^24 (1)
|
||||||
|
* Baud rate = Fsclk = Fssp / (2 * (SCR + 1)) (2)
|
||||||
|
*
|
||||||
|
* DDS_CLK_RATE either 2^n or 2^n / 5.
|
||||||
|
* SCR is in range 0 .. 255
|
||||||
|
*
|
||||||
|
* Divisor = 5^i * 2^j * 2 * k
|
||||||
|
* i = [0, 1] i = 1 iff j = 0 or j > 3
|
||||||
|
* j = [0, 23] j = 0 iff i = 1
|
||||||
|
* k = [1, 256]
|
||||||
|
* Special case: j = 0, i = 1: Divisor = 2 / 5
|
||||||
|
*
|
||||||
|
* Accordingly to the specification the recommended values for DDS_CLK_RATE
|
||||||
|
* are:
|
||||||
|
* Case 1: 2^n, n = [0, 23]
|
||||||
|
* Case 2: 2^24 * 2 / 5 (0x666666)
|
||||||
|
* Case 3: less than or equal to 2^24 / 5 / 16 (0x33333)
|
||||||
|
*
|
||||||
|
* In all cases the lowest possible value is better.
|
||||||
|
*
|
||||||
|
* The function calculates parameters for all cases and chooses the one closest
|
||||||
|
* to the asked baud rate.
|
||||||
*/
|
*/
|
||||||
static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div)
|
static unsigned int quark_x1000_get_clk_div(int rate, u32 *dds)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned long xtal = 200000000;
|
||||||
|
unsigned long fref = xtal / 2; /* mandatory division by 2,
|
||||||
|
see (2) */
|
||||||
|
/* case 3 */
|
||||||
|
unsigned long fref1 = fref / 2; /* case 1 */
|
||||||
|
unsigned long fref2 = fref * 2 / 5; /* case 2 */
|
||||||
|
unsigned long scale;
|
||||||
|
unsigned long q, q1, q2;
|
||||||
|
long r, r1, r2;
|
||||||
|
u32 mul;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) {
|
/* Case 1 */
|
||||||
if (rate >= quark_spi_rate_table[i].bitrate) {
|
|
||||||
*dds = quark_spi_rate_table[i].dds_clk_rate;
|
/* Set initial value for DDS_CLK_RATE */
|
||||||
*clk_div = quark_spi_rate_table[i].clk_div;
|
mul = (1 << 24) >> 1;
|
||||||
return quark_spi_rate_table[i].bitrate;
|
|
||||||
|
/* Calculate initial quot */
|
||||||
|
q1 = DIV_ROUND_CLOSEST(fref1, rate);
|
||||||
|
|
||||||
|
/* Scale q1 if it's too big */
|
||||||
|
if (q1 > 256) {
|
||||||
|
/* Scale q1 to range [1, 512] */
|
||||||
|
scale = fls_long(q1 - 1);
|
||||||
|
if (scale > 9) {
|
||||||
|
q1 >>= scale - 9;
|
||||||
|
mul >>= scale - 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Round the result if we have a remainder */
|
||||||
|
q1 += q1 & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrease DDS_CLK_RATE as much as we can without loss in precision */
|
||||||
|
scale = __ffs(q1);
|
||||||
|
q1 >>= scale;
|
||||||
|
mul >>= scale;
|
||||||
|
|
||||||
|
/* Get the remainder */
|
||||||
|
r1 = abs(fref1 / (1 << (24 - fls_long(mul))) / q1 - rate);
|
||||||
|
|
||||||
|
/* Case 2 */
|
||||||
|
|
||||||
|
q2 = DIV_ROUND_CLOSEST(fref2, rate);
|
||||||
|
r2 = abs(fref2 / q2 - rate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Choose the best between two: less remainder we have the better. We
|
||||||
|
* can't go case 2 if q2 is greater than 256 since SCR register can
|
||||||
|
* hold only values 0 .. 255.
|
||||||
|
*/
|
||||||
|
if (r2 >= r1 || q2 > 256) {
|
||||||
|
/* case 1 is better */
|
||||||
|
r = r1;
|
||||||
|
q = q1;
|
||||||
|
} else {
|
||||||
|
/* case 2 is better */
|
||||||
|
r = r2;
|
||||||
|
q = q2;
|
||||||
|
mul = (1 << 24) * 2 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check case 3 only If the divisor is big enough */
|
||||||
|
if (fref / rate >= 80) {
|
||||||
|
u64 fssp;
|
||||||
|
u32 m;
|
||||||
|
|
||||||
|
/* Calculate initial quot */
|
||||||
|
q1 = DIV_ROUND_CLOSEST(fref, rate);
|
||||||
|
m = (1 << 24) / q1;
|
||||||
|
|
||||||
|
/* Get the remainder */
|
||||||
|
fssp = (u64)fref * m;
|
||||||
|
do_div(fssp, 1 << 24);
|
||||||
|
r1 = abs(fssp - rate);
|
||||||
|
|
||||||
|
/* Choose this one if it suits better */
|
||||||
|
if (r1 < r) {
|
||||||
|
/* case 3 is better */
|
||||||
|
q = 1;
|
||||||
|
mul = m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*dds = quark_spi_rate_table[i-1].dds_clk_rate;
|
*dds = mul;
|
||||||
*clk_div = quark_spi_rate_table[i-1].clk_div;
|
return q - 1;
|
||||||
|
|
||||||
return quark_spi_rate_table[i-1].bitrate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
|
static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
|
||||||
|
@ -730,23 +778,25 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
|
||||||
rate = min_t(int, ssp_clk, rate);
|
rate = min_t(int, ssp_clk, rate);
|
||||||
|
|
||||||
if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP)
|
if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP)
|
||||||
return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8;
|
return (ssp_clk / (2 * rate) - 1) & 0xff;
|
||||||
else
|
else
|
||||||
return ((ssp_clk / rate - 1) & 0xfff) << 8;
|
return (ssp_clk / rate - 1) & 0xfff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
|
static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
|
||||||
struct chip_data *chip, int rate)
|
struct chip_data *chip, int rate)
|
||||||
{
|
{
|
||||||
u32 clk_div;
|
unsigned int clk_div;
|
||||||
|
|
||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div);
|
clk_div = quark_x1000_get_clk_div(rate, &chip->dds_rate);
|
||||||
return clk_div << 8;
|
break;
|
||||||
default:
|
default:
|
||||||
return ssp_get_clk_div(drv_data, rate);
|
clk_div = ssp_get_clk_div(drv_data, rate);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return clk_div << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pump_transfers(unsigned long data)
|
static void pump_transfers(unsigned long data)
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
|
||||||
#define QUP_CONFIG 0x0000
|
#define QUP_CONFIG 0x0000
|
||||||
#define QUP_STATE 0x0004
|
#define QUP_STATE 0x0004
|
||||||
|
@ -116,6 +118,8 @@
|
||||||
|
|
||||||
#define SPI_NUM_CHIPSELECTS 4
|
#define SPI_NUM_CHIPSELECTS 4
|
||||||
|
|
||||||
|
#define SPI_MAX_DMA_XFER (SZ_64K - 64)
|
||||||
|
|
||||||
/* high speed mode is when bus rate is greater then 26MHz */
|
/* high speed mode is when bus rate is greater then 26MHz */
|
||||||
#define SPI_HS_MIN_RATE 26000000
|
#define SPI_HS_MIN_RATE 26000000
|
||||||
#define SPI_MAX_RATE 50000000
|
#define SPI_MAX_RATE 50000000
|
||||||
|
@ -140,9 +144,14 @@ struct spi_qup {
|
||||||
struct completion done;
|
struct completion done;
|
||||||
int error;
|
int error;
|
||||||
int w_size; /* bytes per SPI word */
|
int w_size; /* bytes per SPI word */
|
||||||
|
int n_words;
|
||||||
int tx_bytes;
|
int tx_bytes;
|
||||||
int rx_bytes;
|
int rx_bytes;
|
||||||
int qup_v1;
|
int qup_v1;
|
||||||
|
|
||||||
|
int use_dma;
|
||||||
|
struct dma_slave_config rx_conf;
|
||||||
|
struct dma_slave_config tx_conf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,7 +207,6 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void spi_qup_fifo_read(struct spi_qup *controller,
|
static void spi_qup_fifo_read(struct spi_qup *controller,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
@ -266,6 +274,107 @@ static void spi_qup_fifo_write(struct spi_qup *controller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spi_qup_dma_done(void *data)
|
||||||
|
{
|
||||||
|
struct spi_qup *qup = data;
|
||||||
|
|
||||||
|
complete(&qup->done);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
|
||||||
|
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) {
|
||||||
|
chan = master->dma_tx;
|
||||||
|
nents = xfer->tx_sg.nents;
|
||||||
|
sgl = xfer->tx_sg.sgl;
|
||||||
|
} 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;
|
||||||
|
|
||||||
|
desc->callback = callback;
|
||||||
|
desc->callback_param = qup;
|
||||||
|
|
||||||
|
cookie = dmaengine_submit(desc);
|
||||||
|
|
||||||
|
return dma_submit_error(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_dma_terminate(struct spi_master *master,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
if (xfer->tx_buf)
|
||||||
|
dmaengine_terminate_all(master->dma_tx);
|
||||||
|
if (xfer->rx_buf)
|
||||||
|
dmaengine_terminate_all(master->dma_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (xfer->rx_buf)
|
||||||
|
rx_done = spi_qup_dma_done;
|
||||||
|
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);
|
||||||
|
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)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dma_async_issue_pending(master->dma_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||||
|
int 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_qup_fifo_write(qup, xfer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct spi_qup *controller = dev_id;
|
struct spi_qup *controller = dev_id;
|
||||||
|
@ -315,11 +424,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||||
error = -EIO;
|
error = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!controller->use_dma) {
|
||||||
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||||
spi_qup_fifo_read(controller, xfer);
|
spi_qup_fifo_read(controller, xfer);
|
||||||
|
|
||||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||||
spi_qup_fifo_write(controller, xfer);
|
spi_qup_fifo_write(controller, xfer);
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&controller->lock, flags);
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
controller->error = error;
|
controller->error = error;
|
||||||
|
@ -332,13 +443,35 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
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 */
|
/* set clock freq ... bits per word */
|
||||||
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||||
u32 config, iomode, mode, control;
|
u32 config, iomode, mode, control;
|
||||||
int ret, n_words, w_size;
|
int ret, n_words;
|
||||||
|
|
||||||
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||||
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
||||||
|
@ -358,35 +491,54 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
w_size = 4;
|
mode = spi_qup_get_mode(spi->master, xfer);
|
||||||
if (xfer->bits_per_word <= 8)
|
n_words = controller->n_words;
|
||||||
w_size = 1;
|
|
||||||
else if (xfer->bits_per_word <= 16)
|
|
||||||
w_size = 2;
|
|
||||||
|
|
||||||
n_words = xfer->len / w_size;
|
if (mode == QUP_IO_M_MODE_FIFO) {
|
||||||
controller->w_size = w_size;
|
|
||||||
|
|
||||||
if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
|
|
||||||
mode = QUP_IO_M_MODE_FIFO;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
||||||
/* must be zero for FIFO */
|
/* must be zero for FIFO */
|
||||||
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||||
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
} else {
|
} else if (!controller->use_dma) {
|
||||||
mode = QUP_IO_M_MODE_BLOCK;
|
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
||||||
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
/* must be zero for BLOCK and BAM */
|
/* must be zero for BLOCK and BAM */
|
||||||
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||||
writel_relaxed(0, controller->base + QUP_MX_WRITE_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;
|
||||||
|
|
||||||
|
input_cnt = controller->base + QUP_MX_INPUT_CNT;
|
||||||
|
/*
|
||||||
|
* for DMA transfers, both QUP_MX_INPUT_CNT and
|
||||||
|
* QUP_MX_OUTPUT_CNT must be zero to all cases but one.
|
||||||
|
* That case is a non-balanced transfer when there is
|
||||||
|
* only a rx_buf.
|
||||||
|
*/
|
||||||
|
if (xfer->tx_buf)
|
||||||
|
writel_relaxed(0, input_cnt);
|
||||||
|
else
|
||||||
|
writel_relaxed(n_words, input_cnt);
|
||||||
|
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
||||||
/* Set input and output transfer mode */
|
/* Set input and output transfer mode */
|
||||||
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
||||||
|
|
||||||
|
if (!controller->use_dma)
|
||||||
iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
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_OUTPUT_MODE_MASK_SHIFT);
|
||||||
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
||||||
|
|
||||||
|
@ -428,11 +580,31 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
||||||
config |= xfer->bits_per_word - 1;
|
config |= xfer->bits_per_word - 1;
|
||||||
config |= QUP_CONFIG_SPI_MODE;
|
config |= QUP_CONFIG_SPI_MODE;
|
||||||
|
|
||||||
|
if (controller->use_dma) {
|
||||||
|
if (!xfer->tx_buf)
|
||||||
|
config |= QUP_CONFIG_NO_OUTPUT;
|
||||||
|
if (!xfer->rx_buf)
|
||||||
|
config |= QUP_CONFIG_NO_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
writel_relaxed(config, controller->base + QUP_CONFIG);
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
|
||||||
/* only write to OPERATIONAL_MASK when register is present */
|
/* only write to OPERATIONAL_MASK when register is present */
|
||||||
if (!controller->qup_v1)
|
if (!controller->qup_v1) {
|
||||||
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
u32 mask = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
|
||||||
|
* status change in BAM mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (mode == QUP_IO_M_MODE_BAM)
|
||||||
|
mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
|
||||||
|
|
||||||
|
writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,17 +633,13 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
||||||
controller->tx_bytes = 0;
|
controller->tx_bytes = 0;
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
if (controller->use_dma)
|
||||||
dev_warn(controller->dev, "cannot set RUN state\n");
|
ret = spi_qup_do_dma(master, xfer);
|
||||||
goto exit;
|
else
|
||||||
}
|
ret = spi_qup_do_pio(master, xfer);
|
||||||
|
|
||||||
if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
|
if (ret)
|
||||||
dev_warn(controller->dev, "cannot set PAUSE state\n");
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
|
|
||||||
spi_qup_fifo_write(controller, xfer);
|
|
||||||
|
|
||||||
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||||
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
||||||
|
@ -480,6 +648,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
||||||
|
|
||||||
if (!wait_for_completion_timeout(&controller->done, timeout))
|
if (!wait_for_completion_timeout(&controller->done, timeout))
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
spin_lock_irqsave(&controller->lock, flags);
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
@ -487,6 +656,97 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = controller->error;
|
ret = controller->error;
|
||||||
spin_unlock_irqrestore(&controller->lock, flags);
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
if (ret && controller->use_dma)
|
||||||
|
spi_qup_dma_terminate(master, xfer);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *qup = spi_master_get_devdata(master);
|
||||||
|
size_t dma_align = dma_get_cache_alignment();
|
||||||
|
u32 mode;
|
||||||
|
|
||||||
|
qup->use_dma = 0;
|
||||||
|
|
||||||
|
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)))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_release_dma(struct spi_master *master)
|
||||||
|
{
|
||||||
|
if (!IS_ERR_OR_NULL(master->dma_rx))
|
||||||
|
dma_release_channel(master->dma_rx);
|
||||||
|
if (!IS_ERR_OR_NULL(master->dma_tx))
|
||||||
|
dma_release_channel(master->dma_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_init_dma(struct spi_master *master, resource_size_t base)
|
||||||
|
{
|
||||||
|
struct spi_qup *spi = spi_master_get_devdata(master);
|
||||||
|
struct dma_slave_config *rx_conf = &spi->rx_conf,
|
||||||
|
*tx_conf = &spi->tx_conf;
|
||||||
|
struct device *dev = spi->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* allocate dma resources, if available */
|
||||||
|
master->dma_rx = dma_request_slave_channel_reason(dev, "rx");
|
||||||
|
if (IS_ERR(master->dma_rx))
|
||||||
|
return PTR_ERR(master->dma_rx);
|
||||||
|
|
||||||
|
master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
|
||||||
|
if (IS_ERR(master->dma_tx)) {
|
||||||
|
ret = PTR_ERR(master->dma_tx);
|
||||||
|
goto err_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set DMA parameters */
|
||||||
|
rx_conf->direction = DMA_DEV_TO_MEM;
|
||||||
|
rx_conf->device_fc = 1;
|
||||||
|
rx_conf->src_addr = base + QUP_INPUT_FIFO;
|
||||||
|
rx_conf->src_maxburst = spi->in_blk_sz;
|
||||||
|
|
||||||
|
tx_conf->direction = DMA_MEM_TO_DEV;
|
||||||
|
tx_conf->device_fc = 1;
|
||||||
|
tx_conf->dst_addr = base + QUP_OUTPUT_FIFO;
|
||||||
|
tx_conf->dst_maxburst = spi->out_blk_sz;
|
||||||
|
|
||||||
|
ret = dmaengine_slave_config(master->dma_rx, rx_conf);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to configure RX channel\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dmaengine_slave_config(master->dma_tx, tx_conf);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to configure TX channel\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
dma_release_channel(master->dma_tx);
|
||||||
|
err_tx:
|
||||||
|
dma_release_channel(master->dma_rx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,6 +823,8 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||||
master->transfer_one = spi_qup_transfer_one;
|
master->transfer_one = spi_qup_transfer_one;
|
||||||
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->dma_alignment = dma_get_cache_alignment();
|
||||||
|
master->max_dma_len = SPI_MAX_DMA_XFER;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
@ -574,6 +836,12 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||||
controller->cclk = cclk;
|
controller->cclk = cclk;
|
||||||
controller->irq = irq;
|
controller->irq = irq;
|
||||||
|
|
||||||
|
ret = spi_qup_init_dma(master, res->start);
|
||||||
|
if (ret == -EPROBE_DEFER)
|
||||||
|
goto error;
|
||||||
|
else if (!ret)
|
||||||
|
master->can_dma = spi_qup_can_dma;
|
||||||
|
|
||||||
/* set v1 flag if device is version 1 */
|
/* set v1 flag if device is version 1 */
|
||||||
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
|
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
|
||||||
controller->qup_v1 = 1;
|
controller->qup_v1 = 1;
|
||||||
|
@ -610,7 +878,7 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||||
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "cannot set RESET state\n");
|
dev_err(dev, "cannot set RESET state\n");
|
||||||
goto error;
|
goto error_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
writel_relaxed(0, base + QUP_OPERATIONAL);
|
writel_relaxed(0, base + QUP_OPERATIONAL);
|
||||||
|
@ -634,7 +902,7 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||||
ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
|
ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
|
||||||
IRQF_TRIGGER_HIGH, pdev->name, controller);
|
IRQF_TRIGGER_HIGH, pdev->name, controller);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error_dma;
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
|
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
|
||||||
pm_runtime_use_autosuspend(dev);
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
@ -649,6 +917,8 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
disable_pm:
|
disable_pm:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
error_dma:
|
||||||
|
spi_qup_release_dma(master);
|
||||||
error:
|
error:
|
||||||
clk_disable_unprepare(cclk);
|
clk_disable_unprepare(cclk);
|
||||||
clk_disable_unprepare(iclk);
|
clk_disable_unprepare(iclk);
|
||||||
|
@ -740,6 +1010,8 @@ static int spi_qup_remove(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
spi_qup_release_dma(master);
|
||||||
|
|
||||||
clk_disable_unprepare(controller->cclk);
|
clk_disable_unprepare(controller->cclk);
|
||||||
clk_disable_unprepare(controller->iclk);
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,7 @@ struct rockchip_spi {
|
||||||
u8 tmode;
|
u8 tmode;
|
||||||
u8 bpw;
|
u8 bpw;
|
||||||
u8 n_bytes;
|
u8 n_bytes;
|
||||||
|
u8 rsd_nsecs;
|
||||||
unsigned len;
|
unsigned len;
|
||||||
u32 speed;
|
u32 speed;
|
||||||
|
|
||||||
|
@ -302,7 +303,7 @@ static int rockchip_spi_prepare_message(struct spi_master *master,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rockchip_spi_unprepare_message(struct spi_master *master,
|
static void rockchip_spi_handle_err(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -313,8 +314,8 @@ static int rockchip_spi_unprepare_message(struct spi_master *master,
|
||||||
/*
|
/*
|
||||||
* For DMA mode, we need terminate DMA channel and flush
|
* For DMA mode, we need terminate DMA channel and flush
|
||||||
* fifo for the next transfer if DMA thansfer timeout.
|
* fifo for the next transfer if DMA thansfer timeout.
|
||||||
* unprepare_message() was called by core if transfer complete
|
* handle_err() was called by core if transfer failed.
|
||||||
* or timeout. Maybe it is reasonable for error handling here.
|
* Maybe it is reasonable for error handling here.
|
||||||
*/
|
*/
|
||||||
if (rs->use_dma) {
|
if (rs->use_dma) {
|
||||||
if (rs->state & RXBUSY) {
|
if (rs->state & RXBUSY) {
|
||||||
|
@ -327,6 +328,12 @@ static int rockchip_spi_unprepare_message(struct spi_master *master,
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rs->lock, flags);
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_unprepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_enable_chip(rs, 0);
|
spi_enable_chip(rs, 0);
|
||||||
|
|
||||||
|
@ -493,6 +500,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||||
{
|
{
|
||||||
u32 div = 0;
|
u32 div = 0;
|
||||||
u32 dmacr = 0;
|
u32 dmacr = 0;
|
||||||
|
int rsd = 0;
|
||||||
|
|
||||||
u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET)
|
u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET)
|
||||||
| (CR0_SSD_ONE << CR0_SSD_OFFSET);
|
| (CR0_SSD_ONE << CR0_SSD_OFFSET);
|
||||||
|
@ -519,9 +527,23 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* div doesn't support odd number */
|
/* div doesn't support odd number */
|
||||||
div = max_t(u32, rs->max_freq / rs->speed, 1);
|
div = DIV_ROUND_UP(rs->max_freq, rs->speed);
|
||||||
div = (div + 1) & 0xfffe;
|
div = (div + 1) & 0xfffe;
|
||||||
|
|
||||||
|
/* Rx sample delay is expressed in parent clock cycles (max 3) */
|
||||||
|
rsd = DIV_ROUND_CLOSEST(rs->rsd_nsecs * (rs->max_freq >> 8),
|
||||||
|
1000000000 >> 8);
|
||||||
|
if (!rsd && rs->rsd_nsecs) {
|
||||||
|
pr_warn_once("rockchip-spi: %u Hz are too slow to express %u ns delay\n",
|
||||||
|
rs->max_freq, rs->rsd_nsecs);
|
||||||
|
} else if (rsd > 3) {
|
||||||
|
rsd = 3;
|
||||||
|
pr_warn_once("rockchip-spi: %u Hz are too fast to express %u ns delay, clamping at %u ns\n",
|
||||||
|
rs->max_freq, rs->rsd_nsecs,
|
||||||
|
rsd * 1000000000U / rs->max_freq);
|
||||||
|
}
|
||||||
|
cr0 |= rsd << CR0_RSD_OFFSET;
|
||||||
|
|
||||||
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
||||||
|
|
||||||
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||||
|
@ -614,6 +636,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||||
struct rockchip_spi *rs;
|
struct rockchip_spi *rs;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
|
u32 rsd_nsecs;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
|
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
|
||||||
if (!master)
|
if (!master)
|
||||||
|
@ -665,6 +688,10 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||||
rs->dev = &pdev->dev;
|
rs->dev = &pdev->dev;
|
||||||
rs->max_freq = clk_get_rate(rs->spiclk);
|
rs->max_freq = clk_get_rate(rs->spiclk);
|
||||||
|
|
||||||
|
if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
|
||||||
|
&rsd_nsecs))
|
||||||
|
rs->rsd_nsecs = rsd_nsecs;
|
||||||
|
|
||||||
rs->fifo_len = get_fifo_len(rs);
|
rs->fifo_len = get_fifo_len(rs);
|
||||||
if (!rs->fifo_len) {
|
if (!rs->fifo_len) {
|
||||||
dev_err(&pdev->dev, "Failed to get fifo length\n");
|
dev_err(&pdev->dev, "Failed to get fifo length\n");
|
||||||
|
@ -688,6 +715,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||||
master->prepare_message = rockchip_spi_prepare_message;
|
master->prepare_message = rockchip_spi_prepare_message;
|
||||||
master->unprepare_message = rockchip_spi_unprepare_message;
|
master->unprepare_message = rockchip_spi_unprepare_message;
|
||||||
master->transfer_one = rockchip_spi_transfer_one;
|
master->transfer_one = rockchip_spi_transfer_one;
|
||||||
|
master->handle_err = rockchip_spi_handle_err;
|
||||||
|
|
||||||
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
|
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
|
||||||
if (!rs->dma_tx.ch)
|
if (!rs->dma_tx.ch)
|
||||||
|
|
|
@ -177,6 +177,13 @@
|
||||||
#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */
|
#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */
|
||||||
#define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */
|
#define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */
|
||||||
#define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */
|
#define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */
|
||||||
|
/* QSPI on R-Car Gen2 */
|
||||||
|
#define SPBFCR_TXTRG_1B 0x00 /* 31 bytes (1 byte available) */
|
||||||
|
#define SPBFCR_TXTRG_32B 0x30 /* 0 byte (32 bytes available) */
|
||||||
|
#define SPBFCR_RXTRG_1B 0x00 /* 1 byte (31 bytes available) */
|
||||||
|
#define SPBFCR_RXTRG_32B 0x07 /* 32 bytes (0 byte available) */
|
||||||
|
|
||||||
|
#define QSPI_BUFFER_SIZE 32u
|
||||||
|
|
||||||
struct rspi_data {
|
struct rspi_data {
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
|
@ -366,6 +373,52 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qspi_update(const struct rspi_data *rspi, u8 mask, u8 val, u8 reg)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
data = rspi_read8(rspi, reg);
|
||||||
|
data &= ~mask;
|
||||||
|
data |= (val & mask);
|
||||||
|
rspi_write8(rspi, data, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qspi_set_send_trigger(struct rspi_data *rspi, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
|
||||||
|
n = min(len, QSPI_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (len >= QSPI_BUFFER_SIZE) {
|
||||||
|
/* sets triggering number to 32 bytes */
|
||||||
|
qspi_update(rspi, SPBFCR_TXTRG_MASK,
|
||||||
|
SPBFCR_TXTRG_32B, QSPI_SPBFCR);
|
||||||
|
} else {
|
||||||
|
/* sets triggering number to 1 byte */
|
||||||
|
qspi_update(rspi, SPBFCR_TXTRG_MASK,
|
||||||
|
SPBFCR_TXTRG_1B, QSPI_SPBFCR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qspi_set_receive_trigger(struct rspi_data *rspi, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
|
||||||
|
n = min(len, QSPI_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (len >= QSPI_BUFFER_SIZE) {
|
||||||
|
/* sets triggering number to 32 bytes */
|
||||||
|
qspi_update(rspi, SPBFCR_RXTRG_MASK,
|
||||||
|
SPBFCR_RXTRG_32B, QSPI_SPBFCR);
|
||||||
|
} else {
|
||||||
|
/* sets triggering number to 1 byte */
|
||||||
|
qspi_update(rspi, SPBFCR_RXTRG_MASK,
|
||||||
|
SPBFCR_RXTRG_1B, QSPI_SPBFCR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define set_config_register(spi, n) spi->ops->set_config_register(spi, n)
|
#define set_config_register(spi, n) spi->ops->set_config_register(spi, n)
|
||||||
|
|
||||||
static void rspi_enable_irq(const struct rspi_data *rspi, u8 enable)
|
static void rspi_enable_irq(const struct rspi_data *rspi, u8 enable)
|
||||||
|
@ -609,18 +662,28 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||||
return __rspi_can_dma(rspi, xfer);
|
return __rspi_can_dma(rspi, xfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||||
|
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */
|
||||||
|
int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
|
||||||
|
xfer->rx_buf ? &xfer->rx_sg : NULL);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
static int rspi_common_transfer(struct rspi_data *rspi,
|
static int rspi_common_transfer(struct rspi_data *rspi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
ret = rspi_dma_check_then_transfer(rspi, xfer);
|
||||||
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */
|
|
||||||
ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
|
|
||||||
xfer->rx_buf ? &xfer->rx_sg : NULL);
|
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
|
ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -661,12 +724,59 @@ static int rspi_rz_transfer_one(struct spi_master *master,
|
||||||
return rspi_common_transfer(rspi, xfer);
|
return rspi_common_transfer(rspi, xfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx,
|
||||||
|
u8 *rx, unsigned int len)
|
||||||
|
{
|
||||||
|
int i, n, ret;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
n = qspi_set_send_trigger(rspi, len);
|
||||||
|
qspi_set_receive_trigger(rspi, len);
|
||||||
|
if (n == QSPI_BUFFER_SIZE) {
|
||||||
|
error = rspi_wait_for_tx_empty(rspi);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&rspi->master->dev, "transmit timeout\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
rspi_write_data(rspi, *tx++);
|
||||||
|
|
||||||
|
error = rspi_wait_for_rx_full(rspi);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&rspi->master->dev, "receive timeout\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
*rx++ = rspi_read_data(rspi);
|
||||||
|
} else {
|
||||||
|
ret = rspi_pio_transfer(rspi, tx, rx, n);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
len -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int qspi_transfer_out_in(struct rspi_data *rspi,
|
static int qspi_transfer_out_in(struct rspi_data *rspi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
qspi_receive_init(rspi);
|
qspi_receive_init(rspi);
|
||||||
|
|
||||||
return rspi_common_transfer(rspi, xfer);
|
ret = rspi_dma_check_then_transfer(rspi, xfer);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf,
|
||||||
|
xfer->rx_buf, xfer->len);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
|
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
|
||||||
|
|
|
@ -324,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||||
|
|
||||||
/* Acquire DMA channels */
|
/* Acquire DMA channels */
|
||||||
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||||
(void *)sdd->rx_dma.dmach, dev, "rx");
|
(void *)(long)sdd->rx_dma.dmach, dev, "rx");
|
||||||
if (!sdd->rx_dma.ch) {
|
if (!sdd->rx_dma.ch) {
|
||||||
dev_err(dev, "Failed to get RX DMA channel\n");
|
dev_err(dev, "Failed to get RX DMA channel\n");
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
|
@ -333,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||||
spi->dma_rx = sdd->rx_dma.ch;
|
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 *)(long)sdd->tx_dma.dmach, dev, "tx");
|
||||||
if (!sdd->tx_dma.ch) {
|
if (!sdd->tx_dma.ch) {
|
||||||
dev_err(dev, "Failed to get TX DMA channel\n");
|
dev_err(dev, "Failed to get TX DMA channel\n");
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
|
|
|
@ -286,7 +286,7 @@ static int sc18is602_probe(struct i2c_client *client,
|
||||||
hw->freq = SC18IS602_CLOCK;
|
hw->freq = SC18IS602_CLOCK;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
master->bus_num = client->adapter->nr;
|
master->bus_num = np ? -1 : client->adapter->nr;
|
||||||
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
|
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
master->setup = sc18is602_setup;
|
master->setup = sc18is602_setup;
|
||||||
|
|
|
@ -482,7 +482,7 @@ static const struct dev_pm_ops spi_st_pm = {
|
||||||
SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
|
SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct of_device_id stm_spi_match[] = {
|
static const struct of_device_id stm_spi_match[] = {
|
||||||
{ .compatible = "st,comms-ssc4-spi", },
|
{ .compatible = "st,comms-ssc4-spi", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/kmod.h>
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/cache.h>
|
#include <linux/cache.h>
|
||||||
|
@ -129,125 +128,11 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int spi_legacy_suspend(struct device *dev, pm_message_t message)
|
|
||||||
{
|
|
||||||
int value = 0;
|
|
||||||
struct spi_driver *drv = to_spi_driver(dev->driver);
|
|
||||||
|
|
||||||
/* suspend will stop irqs and dma; no more i/o */
|
|
||||||
if (drv) {
|
|
||||||
if (drv->suspend)
|
|
||||||
value = drv->suspend(to_spi_device(dev), message);
|
|
||||||
else
|
|
||||||
dev_dbg(dev, "... can't suspend\n");
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_legacy_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
int value = 0;
|
|
||||||
struct spi_driver *drv = to_spi_driver(dev->driver);
|
|
||||||
|
|
||||||
/* resume may restart the i/o queue */
|
|
||||||
if (drv) {
|
|
||||||
if (drv->resume)
|
|
||||||
value = drv->resume(to_spi_device(dev));
|
|
||||||
else
|
|
||||||
dev_dbg(dev, "... can't resume\n");
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_suspend(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_suspend(dev, PMSG_SUSPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_resume(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_freeze(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_freeze(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_suspend(dev, PMSG_FREEZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_thaw(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_thaw(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_poweroff(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_poweroff(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_suspend(dev, PMSG_HIBERNATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_pm_restore(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_restore(dev);
|
|
||||||
else
|
|
||||||
return spi_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define spi_pm_suspend NULL
|
|
||||||
#define spi_pm_resume NULL
|
|
||||||
#define spi_pm_freeze NULL
|
|
||||||
#define spi_pm_thaw NULL
|
|
||||||
#define spi_pm_poweroff NULL
|
|
||||||
#define spi_pm_restore NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct dev_pm_ops spi_pm = {
|
|
||||||
.suspend = spi_pm_suspend,
|
|
||||||
.resume = spi_pm_resume,
|
|
||||||
.freeze = spi_pm_freeze,
|
|
||||||
.thaw = spi_pm_thaw,
|
|
||||||
.poweroff = spi_pm_poweroff,
|
|
||||||
.restore = spi_pm_restore,
|
|
||||||
SET_RUNTIME_PM_OPS(
|
|
||||||
pm_generic_runtime_suspend,
|
|
||||||
pm_generic_runtime_resume,
|
|
||||||
NULL
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bus_type spi_bus_type = {
|
struct bus_type spi_bus_type = {
|
||||||
.name = "spi",
|
.name = "spi",
|
||||||
.dev_groups = spi_dev_groups,
|
.dev_groups = spi_dev_groups,
|
||||||
.match = spi_match_device,
|
.match = spi_match_device,
|
||||||
.uevent = spi_uevent,
|
.uevent = spi_uevent,
|
||||||
.pm = &spi_pm,
|
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(spi_bus_type);
|
EXPORT_SYMBOL_GPL(spi_bus_type);
|
||||||
|
|
||||||
|
@ -698,6 +583,15 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
|
||||||
rx_dev = master->dma_rx->device->dev;
|
rx_dev = master->dma_rx->device->dev;
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
|
/*
|
||||||
|
* Restore the original value of tx_buf or rx_buf if they are
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
if (xfer->tx_buf == master->dummy_tx)
|
||||||
|
xfer->tx_buf = NULL;
|
||||||
|
if (xfer->rx_buf == master->dummy_rx)
|
||||||
|
xfer->rx_buf = NULL;
|
||||||
|
|
||||||
if (!master->can_dma(master, msg->spi, xfer))
|
if (!master->can_dma(master, msg->spi, xfer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -851,6 +745,9 @@ static int spi_transfer_one_message(struct spi_master *master,
|
||||||
if (msg->status == -EINPROGRESS)
|
if (msg->status == -EINPROGRESS)
|
||||||
msg->status = ret;
|
msg->status = ret;
|
||||||
|
|
||||||
|
if (msg->status && master->handle_err)
|
||||||
|
master->handle_err(master, msg);
|
||||||
|
|
||||||
spi_finalize_current_message(master);
|
spi_finalize_current_message(master);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1360,7 +1257,6 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc)
|
||||||
spi->dev.of_node = nc;
|
spi->dev.of_node = nc;
|
||||||
|
|
||||||
/* Register the new device */
|
/* Register the new device */
|
||||||
request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
|
||||||
rc = spi_add_device(spi);
|
rc = spi_add_device(spi);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(&master->dev, "spi_device register error %s\n",
|
dev_err(&master->dev, "spi_device register error %s\n",
|
||||||
|
@ -1894,6 +1790,8 @@ int spi_setup(struct spi_device *spi)
|
||||||
if (!spi->max_speed_hz)
|
if (!spi->max_speed_hz)
|
||||||
spi->max_speed_hz = spi->master->max_speed_hz;
|
spi->max_speed_hz = spi->master->max_speed_hz;
|
||||||
|
|
||||||
|
spi_set_cs(spi, false);
|
||||||
|
|
||||||
if (spi->master->setup)
|
if (spi->master->setup)
|
||||||
status = spi->master->setup(spi);
|
status = spi->master->setup(spi);
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ static int spidev_message(struct spidev_data *spidev,
|
||||||
struct spi_transfer *k_xfers;
|
struct spi_transfer *k_xfers;
|
||||||
struct spi_transfer *k_tmp;
|
struct spi_transfer *k_tmp;
|
||||||
struct spi_ioc_transfer *u_tmp;
|
struct spi_ioc_transfer *u_tmp;
|
||||||
unsigned n, total;
|
unsigned n, total, tx_total, rx_total;
|
||||||
u8 *tx_buf, *rx_buf;
|
u8 *tx_buf, *rx_buf;
|
||||||
int status = -EFAULT;
|
int status = -EFAULT;
|
||||||
|
|
||||||
|
@ -239,33 +239,52 @@ static int spidev_message(struct spidev_data *spidev,
|
||||||
tx_buf = spidev->tx_buffer;
|
tx_buf = spidev->tx_buffer;
|
||||||
rx_buf = spidev->rx_buffer;
|
rx_buf = spidev->rx_buffer;
|
||||||
total = 0;
|
total = 0;
|
||||||
|
tx_total = 0;
|
||||||
|
rx_total = 0;
|
||||||
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
||||||
n;
|
n;
|
||||||
n--, k_tmp++, u_tmp++) {
|
n--, k_tmp++, u_tmp++) {
|
||||||
k_tmp->len = u_tmp->len;
|
k_tmp->len = u_tmp->len;
|
||||||
|
|
||||||
total += k_tmp->len;
|
total += k_tmp->len;
|
||||||
if (total > bufsiz) {
|
/* Since the function returns the total length of transfers
|
||||||
|
* on success, restrict the total to positive int values to
|
||||||
|
* avoid the return value looking like an error. Also check
|
||||||
|
* each transfer length to avoid arithmetic overflow.
|
||||||
|
*/
|
||||||
|
if (total > INT_MAX || k_tmp->len > INT_MAX) {
|
||||||
status = -EMSGSIZE;
|
status = -EMSGSIZE;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u_tmp->rx_buf) {
|
if (u_tmp->rx_buf) {
|
||||||
|
/* this transfer needs space in RX bounce buffer */
|
||||||
|
rx_total += k_tmp->len;
|
||||||
|
if (rx_total > bufsiz) {
|
||||||
|
status = -EMSGSIZE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
k_tmp->rx_buf = rx_buf;
|
k_tmp->rx_buf = rx_buf;
|
||||||
if (!access_ok(VERIFY_WRITE, (u8 __user *)
|
if (!access_ok(VERIFY_WRITE, (u8 __user *)
|
||||||
(uintptr_t) u_tmp->rx_buf,
|
(uintptr_t) u_tmp->rx_buf,
|
||||||
u_tmp->len))
|
u_tmp->len))
|
||||||
goto done;
|
goto done;
|
||||||
|
rx_buf += k_tmp->len;
|
||||||
}
|
}
|
||||||
if (u_tmp->tx_buf) {
|
if (u_tmp->tx_buf) {
|
||||||
|
/* this transfer needs space in TX bounce buffer */
|
||||||
|
tx_total += k_tmp->len;
|
||||||
|
if (tx_total > bufsiz) {
|
||||||
|
status = -EMSGSIZE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
k_tmp->tx_buf = tx_buf;
|
k_tmp->tx_buf = tx_buf;
|
||||||
if (copy_from_user(tx_buf, (const u8 __user *)
|
if (copy_from_user(tx_buf, (const u8 __user *)
|
||||||
(uintptr_t) u_tmp->tx_buf,
|
(uintptr_t) u_tmp->tx_buf,
|
||||||
u_tmp->len))
|
u_tmp->len))
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
tx_buf += k_tmp->len;
|
tx_buf += k_tmp->len;
|
||||||
rx_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->tx_nbits = u_tmp->tx_nbits;
|
||||||
|
@ -303,9 +322,9 @@ static int spidev_message(struct spidev_data *spidev,
|
||||||
status = -EFAULT;
|
status = -EFAULT;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
rx_buf += u_tmp->len;
|
rx_buf += u_tmp->len;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
status = total;
|
status = total;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
@ -684,6 +703,14 @@ static const struct file_operations spidev_fops = {
|
||||||
|
|
||||||
static struct class *spidev_class;
|
static struct class *spidev_class;
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id spidev_dt_ids[] = {
|
||||||
|
{ .compatible = "rohm,dh2228fv" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static int spidev_probe(struct spi_device *spi)
|
static int spidev_probe(struct spi_device *spi)
|
||||||
|
@ -692,6 +719,17 @@ static int spidev_probe(struct spi_device *spi)
|
||||||
int status;
|
int status;
|
||||||
unsigned long minor;
|
unsigned long minor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* spidev should never be referenced in DT without a specific
|
||||||
|
* compatbile string, it is a Linux implementation thing
|
||||||
|
* rather than a description of the hardware.
|
||||||
|
*/
|
||||||
|
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
|
||||||
|
dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
|
||||||
|
WARN_ON(spi->dev.of_node &&
|
||||||
|
!of_match_device(spidev_dt_ids, &spi->dev));
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate driver data */
|
/* Allocate driver data */
|
||||||
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
|
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
|
||||||
if (!spidev)
|
if (!spidev)
|
||||||
|
@ -758,13 +796,6 @@ static int spidev_remove(struct spi_device *spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id spidev_dt_ids[] = {
|
|
||||||
{ .compatible = "rohm,dh2228fv" },
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
|
||||||
|
|
||||||
static struct spi_driver spidev_spi_driver = {
|
static struct spi_driver spidev_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "spidev",
|
.name = "spidev",
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* intel_mid_dma.h - Intel MID DMA Drivers
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008-10 Intel Corp
|
|
||||||
* Author: Vinod Koul <vinod.koul@intel.com>
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef __INTEL_MID_DMA_H__
|
|
||||||
#define __INTEL_MID_DMA_H__
|
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
|
||||||
|
|
||||||
#define DMA_PREP_CIRCULAR_LIST (1 << 10)
|
|
||||||
|
|
||||||
/*DMA mode configurations*/
|
|
||||||
enum intel_mid_dma_mode {
|
|
||||||
LNW_DMA_PER_TO_MEM = 0, /*periphral to memory configuration*/
|
|
||||||
LNW_DMA_MEM_TO_PER, /*memory to periphral configuration*/
|
|
||||||
LNW_DMA_MEM_TO_MEM, /*mem to mem confg (testing only)*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*DMA handshaking*/
|
|
||||||
enum intel_mid_dma_hs_mode {
|
|
||||||
LNW_DMA_HW_HS = 0, /*HW Handshaking only*/
|
|
||||||
LNW_DMA_SW_HS = 1, /*SW Handshaking not recommended*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*Burst size configuration*/
|
|
||||||
enum intel_mid_dma_msize {
|
|
||||||
LNW_DMA_MSIZE_1 = 0x0,
|
|
||||||
LNW_DMA_MSIZE_4 = 0x1,
|
|
||||||
LNW_DMA_MSIZE_8 = 0x2,
|
|
||||||
LNW_DMA_MSIZE_16 = 0x3,
|
|
||||||
LNW_DMA_MSIZE_32 = 0x4,
|
|
||||||
LNW_DMA_MSIZE_64 = 0x5,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct intel_mid_dma_slave - DMA slave structure
|
|
||||||
*
|
|
||||||
* @dirn: DMA trf direction
|
|
||||||
* @src_width: tx register width
|
|
||||||
* @dst_width: rx register width
|
|
||||||
* @hs_mode: HW/SW handshaking mode
|
|
||||||
* @cfg_mode: DMA data transfer mode (per-per/mem-per/mem-mem)
|
|
||||||
* @src_msize: Source DMA burst size
|
|
||||||
* @dst_msize: Dst DMA burst size
|
|
||||||
* @per_addr: Periphral address
|
|
||||||
* @device_instance: DMA peripheral device instance, we can have multiple
|
|
||||||
* peripheral device connected to single DMAC
|
|
||||||
*/
|
|
||||||
struct intel_mid_dma_slave {
|
|
||||||
enum intel_mid_dma_hs_mode hs_mode; /*handshaking*/
|
|
||||||
enum intel_mid_dma_mode cfg_mode; /*mode configuration*/
|
|
||||||
unsigned int device_instance; /*0, 1 for periphral instance*/
|
|
||||||
struct dma_slave_config dma_slave;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /*__INTEL_MID_DMA_H__*/
|
|
|
@ -162,8 +162,6 @@ struct spi_transfer;
|
||||||
* @remove: Unbinds this driver from the spi device
|
* @remove: Unbinds this driver from the spi device
|
||||||
* @shutdown: Standard shutdown callback used during system state
|
* @shutdown: Standard shutdown callback used during system state
|
||||||
* transitions such as powerdown/halt and kexec
|
* transitions such as powerdown/halt and kexec
|
||||||
* @suspend: Standard suspend callback used during system state transitions
|
|
||||||
* @resume: Standard resume callback used during system state transitions
|
|
||||||
* @driver: SPI device drivers should initialize the name and owner
|
* @driver: SPI device drivers should initialize the name and owner
|
||||||
* field of this structure.
|
* field of this structure.
|
||||||
*
|
*
|
||||||
|
@ -184,8 +182,6 @@ struct spi_driver {
|
||||||
int (*probe)(struct spi_device *spi);
|
int (*probe)(struct spi_device *spi);
|
||||||
int (*remove)(struct spi_device *spi);
|
int (*remove)(struct spi_device *spi);
|
||||||
void (*shutdown)(struct spi_device *spi);
|
void (*shutdown)(struct spi_device *spi);
|
||||||
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
|
|
||||||
int (*resume)(struct spi_device *spi);
|
|
||||||
struct device_driver driver;
|
struct device_driver driver;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -294,6 +290,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
||||||
* transfer_one_message are mutually exclusive; when both
|
* transfer_one_message are mutually exclusive; when both
|
||||||
* are set, the generic subsystem does not call your
|
* are set, the generic subsystem does not call your
|
||||||
* transfer_one callback.
|
* transfer_one callback.
|
||||||
|
* @handle_err: the subsystem calls the driver to handle an error that occurs
|
||||||
|
* in the generic implementation of transfer_one_message().
|
||||||
* @unprepare_message: undo any work done by prepare_message().
|
* @unprepare_message: undo any work done by prepare_message().
|
||||||
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
|
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
|
||||||
* number. Any individual value may be -ENOENT for CS lines that
|
* number. Any individual value may be -ENOENT for CS lines that
|
||||||
|
@ -448,6 +446,8 @@ struct spi_master {
|
||||||
void (*set_cs)(struct spi_device *spi, bool enable);
|
void (*set_cs)(struct spi_device *spi, bool enable);
|
||||||
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
|
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
|
||||||
struct spi_transfer *transfer);
|
struct spi_transfer *transfer);
|
||||||
|
void (*handle_err)(struct spi_master *master,
|
||||||
|
struct spi_message *message);
|
||||||
|
|
||||||
/* gpio chip select */
|
/* gpio chip select */
|
||||||
int *cs_gpios;
|
int *cs_gpios;
|
||||||
|
|
Loading…
Reference in a new issue