mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-26 04:40:55 +07:00
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (53 commits) spi/omap2_mcspi: Verify TX reg is empty after TX only xfer with DMA spi/omap2_mcspi: disable channel after TX_ONLY transfer in PIO mode spi/bfin_spi: namespace local structs spi/bfin_spi: init early spi/bfin_spi: check per-transfer bits_per_word spi/bfin_spi: warn when CS is driven by hardware (CPHA=0) spi/bfin_spi: cs should be always low when a new transfer begins spi/bfin_spi: fix typo in comment spi/bfin_spi: reject unsupported SPI modes spi/bfin_spi: use dma_disable_irq_nosync() in irq handler spi/bfin_spi: combine duplicate SPI_CTL read/write logic spi/bfin_spi: reset ctl_reg bits when setup is run again on a device spi/bfin_spi: push all size checks into the transfer function spi/bfin_spi: use nosync when disabling the IRQ from the IRQ handler spi/bfin_spi: sync hardware state before reprogramming everything spi/bfin_spi: save/restore state when suspending/resuming spi/bfin_spi: redo GPIO CS handling Blackfin: SPI: expand SPI bitmasks spi/bfin_spi: use the SPI namespaced bit names spi/bfin_spi: drop extra memory we don't need ...
This commit is contained in:
commit
70ada77920
@ -1,7 +1,9 @@
|
||||
* SPI (Serial Peripheral Interface)
|
||||
|
||||
Required properties:
|
||||
- cell-index : SPI controller index.
|
||||
- cell-index : QE SPI subblock index.
|
||||
0: QE subblock SPI1
|
||||
1: QE subblock SPI2
|
||||
- compatible : should be "fsl,spi".
|
||||
- mode : the SPI operation mode, it can be "cpu" or "cpu-qe".
|
||||
- reg : Offset and length of the register set for the device
|
||||
@ -29,3 +31,23 @@ Example:
|
||||
gpios = <&gpio 18 1 // device reg=<0>
|
||||
&gpio 19 1>; // device reg=<1>
|
||||
};
|
||||
|
||||
|
||||
* eSPI (Enhanced Serial Peripheral Interface)
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "fsl,mpc8536-espi".
|
||||
- reg : Offset and length of the register set for the device.
|
||||
- interrupts : should contain eSPI interrupt, the device has one interrupt.
|
||||
- fsl,espi-num-chipselects : the number of the chipselect signals.
|
||||
|
||||
Example:
|
||||
spi@110000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc8536-espi";
|
||||
reg = <0x110000 0x1000>;
|
||||
interrupts = <53 0x2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
fsl,espi-num-chipselects = <4>;
|
||||
};
|
||||
|
@ -172,18 +172,12 @@ static void phy3250_spi_cs_set(u32 control)
|
||||
}
|
||||
|
||||
static struct pl022_config_chip spi0_chip_info = {
|
||||
.lbm = LOOPBACK_DISABLED,
|
||||
.com_mode = INTERRUPT_TRANSFER,
|
||||
.iface = SSP_INTERFACE_MOTOROLA_SPI,
|
||||
.hierarchy = SSP_MASTER,
|
||||
.slave_tx_disable = 0,
|
||||
.endian_tx = SSP_TX_LSB,
|
||||
.endian_rx = SSP_RX_LSB,
|
||||
.data_size = SSP_DATA_BITS_8,
|
||||
.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
|
||||
.clk_phase = SSP_CLK_FIRST_EDGE,
|
||||
.clk_pol = SSP_CLK_POL_IDLE_LOW,
|
||||
.ctrl_len = SSP_BITS_8,
|
||||
.wait_state = SSP_MWIRE_WAIT_ZERO,
|
||||
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
|
||||
@ -239,6 +233,7 @@ static int __init phy3250_spi_board_register(void)
|
||||
.max_speed_hz = 5000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = SPI_MODE_0,
|
||||
.platform_data = &eeprom,
|
||||
.controller_data = &spi0_chip_info,
|
||||
},
|
||||
|
@ -46,7 +46,6 @@ static ssize_t dummy_looptest(struct device *dev,
|
||||
* struct, this is just used here to alter the behaviour of the chip
|
||||
* in order to perform tests.
|
||||
*/
|
||||
struct pl022_config_chip *chip_info = spi->controller_data;
|
||||
int status;
|
||||
u8 txbuf[14] = {0xDE, 0xAD, 0xBE, 0xEF, 0x2B, 0xAD,
|
||||
0xCA, 0xFE, 0xBA, 0xBE, 0xB1, 0x05,
|
||||
@ -72,7 +71,7 @@ static ssize_t dummy_looptest(struct device *dev,
|
||||
* Force chip to 8 bit mode
|
||||
* WARNING: NEVER DO THIS IN REAL DRIVER CODE, THIS SHOULD BE STATIC!
|
||||
*/
|
||||
chip_info->data_size = SSP_DATA_BITS_8;
|
||||
spi->bits_per_word = 8;
|
||||
/* You should NOT DO THIS EITHER */
|
||||
spi->master->setup(spi);
|
||||
|
||||
@ -159,7 +158,7 @@ static ssize_t dummy_looptest(struct device *dev,
|
||||
* Force chip to 16 bit mode
|
||||
* WARNING: NEVER DO THIS IN REAL DRIVER CODE, THIS SHOULD BE STATIC!
|
||||
*/
|
||||
chip_info->data_size = SSP_DATA_BITS_16;
|
||||
spi->bits_per_word = 16;
|
||||
/* You should NOT DO THIS EITHER */
|
||||
spi->master->setup(spi);
|
||||
|
||||
|
@ -30,8 +30,6 @@ static void select_dummy_chip(u32 chipselect)
|
||||
}
|
||||
|
||||
struct pl022_config_chip dummy_chip_info = {
|
||||
/* Nominally this is LOOPBACK_DISABLED, but this is our dummy chip! */
|
||||
.lbm = LOOPBACK_ENABLED,
|
||||
/*
|
||||
* available POLLING_TRANSFER and INTERRUPT_TRANSFER,
|
||||
* DMA_TRANSFER does not work
|
||||
@ -42,14 +40,8 @@ struct pl022_config_chip dummy_chip_info = {
|
||||
.hierarchy = SSP_MASTER,
|
||||
/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
|
||||
.slave_tx_disable = 0,
|
||||
/* LSB first */
|
||||
.endian_tx = SSP_TX_LSB,
|
||||
.endian_rx = SSP_RX_LSB,
|
||||
.data_size = SSP_DATA_BITS_8, /* used to be 12 in some default */
|
||||
.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
|
||||
.clk_phase = SSP_CLK_SECOND_EDGE,
|
||||
.clk_pol = SSP_CLK_POL_IDLE_LOW,
|
||||
.ctrl_len = SSP_BITS_12,
|
||||
.wait_state = SSP_MWIRE_WAIT_ZERO,
|
||||
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
|
||||
@ -75,7 +67,7 @@ static struct spi_board_info u300_spi_devices[] = {
|
||||
.bus_num = 0, /* Only one bus on this chip */
|
||||
.chip_select = 0,
|
||||
/* Means SPI_CS_HIGH, change if e.g low CS */
|
||||
.mode = 0,
|
||||
.mode = SPI_MODE_1 | SPI_LSB_FIRST | SPI_LOOP,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
@ -55,19 +55,13 @@ static void ab4500_spi_cs_control(u32 command)
|
||||
}
|
||||
|
||||
struct pl022_config_chip ab4500_chip_info = {
|
||||
.lbm = LOOPBACK_DISABLED,
|
||||
.com_mode = INTERRUPT_TRANSFER,
|
||||
.iface = SSP_INTERFACE_MOTOROLA_SPI,
|
||||
/* we can act as master only */
|
||||
.hierarchy = SSP_MASTER,
|
||||
.slave_tx_disable = 0,
|
||||
.endian_rx = SSP_RX_MSB,
|
||||
.endian_tx = SSP_TX_MSB,
|
||||
.data_size = SSP_DATA_BITS_24,
|
||||
.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
|
||||
.clk_phase = SSP_CLK_SECOND_EDGE,
|
||||
.clk_pol = SSP_CLK_POL_IDLE_HIGH,
|
||||
.cs_control = ab4500_spi_cs_control,
|
||||
};
|
||||
|
||||
@ -83,7 +77,7 @@ static struct spi_board_info u8500_spi_devices[] = {
|
||||
.max_speed_hz = 12000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
.mode = SPI_MODE_0,
|
||||
.mode = SPI_MODE_3,
|
||||
.irq = IRQ_DB8500_AB8500,
|
||||
},
|
||||
};
|
||||
|
@ -32,6 +32,8 @@ struct s3c64xx_spi_csinfo {
|
||||
* struct s3c64xx_spi_info - SPI Controller defining structure
|
||||
* @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.
|
||||
* @src_clk_name: Platform name of the corresponding clock.
|
||||
* @clk_from_cmu: If the SPI clock/prescalar control block is present
|
||||
* by the platform's clock-management-unit and not in SPI controller.
|
||||
* @num_cs: Number of CS this controller emulates.
|
||||
* @cfg_gpio: Configure pins for this SPI controller.
|
||||
* @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6
|
||||
@ -41,6 +43,7 @@ struct s3c64xx_spi_csinfo {
|
||||
struct s3c64xx_spi_info {
|
||||
int src_clk_nr;
|
||||
char *src_clk_name;
|
||||
bool clk_from_cmu;
|
||||
|
||||
int num_cs;
|
||||
|
||||
|
@ -11,26 +11,17 @@
|
||||
|
||||
#define MIN_SPI_BAUD_VAL 2
|
||||
|
||||
#define SPI_READ 0
|
||||
#define SPI_WRITE 1
|
||||
|
||||
#define SPI_CTRL_OFF 0x0
|
||||
#define SPI_FLAG_OFF 0x4
|
||||
#define SPI_STAT_OFF 0x8
|
||||
#define SPI_TXBUFF_OFF 0xc
|
||||
#define SPI_RXBUFF_OFF 0x10
|
||||
#define SPI_BAUD_OFF 0x14
|
||||
#define SPI_SHAW_OFF 0x18
|
||||
|
||||
|
||||
#define BIT_CTL_ENABLE 0x4000
|
||||
#define BIT_CTL_OPENDRAIN 0x2000
|
||||
#define BIT_CTL_MASTER 0x1000
|
||||
#define BIT_CTL_POLAR 0x0800
|
||||
#define BIT_CTL_PHASE 0x0400
|
||||
#define BIT_CTL_BITORDER 0x0200
|
||||
#define BIT_CTL_CPOL 0x0800
|
||||
#define BIT_CTL_CPHA 0x0400
|
||||
#define BIT_CTL_LSBF 0x0200
|
||||
#define BIT_CTL_WORDSIZE 0x0100
|
||||
#define BIT_CTL_MISOENABLE 0x0020
|
||||
#define BIT_CTL_EMISO 0x0020
|
||||
#define BIT_CTL_PSSE 0x0010
|
||||
#define BIT_CTL_GM 0x0008
|
||||
#define BIT_CTL_SZ 0x0004
|
||||
#define BIT_CTL_RXMOD 0x0000
|
||||
#define BIT_CTL_TXMOD 0x0001
|
||||
#define BIT_CTL_TIMOD_DMA_TX 0x0003
|
||||
@ -50,61 +41,7 @@
|
||||
#define BIT_STU_SENDOVER 0x0001
|
||||
#define BIT_STU_RECVFULL 0x0020
|
||||
|
||||
#define CFG_SPI_ENABLE 1
|
||||
#define CFG_SPI_DISABLE 0
|
||||
|
||||
#define CFG_SPI_OUTENABLE 1
|
||||
#define CFG_SPI_OUTDISABLE 0
|
||||
|
||||
#define CFG_SPI_ACTLOW 1
|
||||
#define CFG_SPI_ACTHIGH 0
|
||||
|
||||
#define CFG_SPI_PHASESTART 1
|
||||
#define CFG_SPI_PHASEMID 0
|
||||
|
||||
#define CFG_SPI_MASTER 1
|
||||
#define CFG_SPI_SLAVE 0
|
||||
|
||||
#define CFG_SPI_SENELAST 0
|
||||
#define CFG_SPI_SENDZERO 1
|
||||
|
||||
#define CFG_SPI_RCVFLUSH 1
|
||||
#define CFG_SPI_RCVDISCARD 0
|
||||
|
||||
#define CFG_SPI_LSBFIRST 1
|
||||
#define CFG_SPI_MSBFIRST 0
|
||||
|
||||
#define CFG_SPI_WORDSIZE16 1
|
||||
#define CFG_SPI_WORDSIZE8 0
|
||||
|
||||
#define CFG_SPI_MISOENABLE 1
|
||||
#define CFG_SPI_MISODISABLE 0
|
||||
|
||||
#define CFG_SPI_READ 0x00
|
||||
#define CFG_SPI_WRITE 0x01
|
||||
#define CFG_SPI_DMAREAD 0x02
|
||||
#define CFG_SPI_DMAWRITE 0x03
|
||||
|
||||
#define CFG_SPI_CSCLEARALL 0
|
||||
#define CFG_SPI_CHIPSEL1 1
|
||||
#define CFG_SPI_CHIPSEL2 2
|
||||
#define CFG_SPI_CHIPSEL3 3
|
||||
#define CFG_SPI_CHIPSEL4 4
|
||||
#define CFG_SPI_CHIPSEL5 5
|
||||
#define CFG_SPI_CHIPSEL6 6
|
||||
#define CFG_SPI_CHIPSEL7 7
|
||||
|
||||
#define CFG_SPI_CS1VALUE 1
|
||||
#define CFG_SPI_CS2VALUE 2
|
||||
#define CFG_SPI_CS3VALUE 3
|
||||
#define CFG_SPI_CS4VALUE 4
|
||||
#define CFG_SPI_CS5VALUE 5
|
||||
#define CFG_SPI_CS6VALUE 6
|
||||
#define CFG_SPI_CS7VALUE 7
|
||||
|
||||
#define CMD_SPI_SET_BAUDRATE 2
|
||||
#define CMD_SPI_GET_SYSTEMCLOCK 25
|
||||
#define CMD_SPI_SET_WRITECONTINUOUS 26
|
||||
#define MAX_CTRL_CS 8 /* cs in spi controller */
|
||||
|
||||
/* device.platform_data for SSP controller devices */
|
||||
struct bfin5xx_spi_master {
|
||||
@ -120,9 +57,7 @@ struct bfin5xx_spi_chip {
|
||||
u16 ctl_reg;
|
||||
u8 enable_dma;
|
||||
u8 bits_per_word;
|
||||
u8 cs_change_per_word;
|
||||
u16 cs_chg_udelay; /* Some devices require 16-bit delays */
|
||||
u32 cs_gpio;
|
||||
/* Value to send if no TX value is supplied, usually 0x0 or 0xFFFF */
|
||||
u16 idle_tx_val;
|
||||
u8 pio_interrupt; /* Enable spi data irq */
|
||||
|
@ -108,6 +108,58 @@ rtc@68 {
|
||||
};
|
||||
};
|
||||
|
||||
spi@7000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc8536-espi";
|
||||
reg = <0x7000 0x1000>;
|
||||
interrupts = <59 0x2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
fsl,espi-num-chipselects = <4>;
|
||||
|
||||
flash@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spansion,s25sl12801";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
partition@u-boot {
|
||||
label = "u-boot";
|
||||
reg = <0x00000000 0x00100000>;
|
||||
read-only;
|
||||
};
|
||||
partition@kernel {
|
||||
label = "kernel";
|
||||
reg = <0x00100000 0x00500000>;
|
||||
read-only;
|
||||
};
|
||||
partition@dtb {
|
||||
label = "dtb";
|
||||
reg = <0x00600000 0x00100000>;
|
||||
read-only;
|
||||
};
|
||||
partition@fs {
|
||||
label = "file system";
|
||||
reg = <0x00700000 0x00900000>;
|
||||
};
|
||||
};
|
||||
flash@1 {
|
||||
compatible = "spansion,s25sl12801";
|
||||
reg = <1>;
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
flash@2 {
|
||||
compatible = "spansion,s25sl12801";
|
||||
reg = <2>;
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
flash@3 {
|
||||
compatible = "spansion,s25sl12801";
|
||||
reg = <3>;
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
};
|
||||
|
||||
dma@21300 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
@ -236,22 +236,19 @@ dma-channel@180 {
|
||||
};
|
||||
|
||||
spi@110000 {
|
||||
cell-index = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,espi";
|
||||
compatible = "fsl,p4080-espi", "fsl,mpc8536-espi";
|
||||
reg = <0x110000 0x1000>;
|
||||
interrupts = <53 0x2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
espi,num-ss-bits = <4>;
|
||||
mode = "cpu";
|
||||
fsl,espi-num-chipselects = <4>;
|
||||
|
||||
fsl_m25p80@0 {
|
||||
flash@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "fsl,espi-flash";
|
||||
compatible = "spansion,s25sl12801";
|
||||
reg = <0>;
|
||||
linux,modalias = "fsl_m25p80";
|
||||
spi-max-frequency = <40000000>; /* input clock */
|
||||
partition@u-boot {
|
||||
label = "u-boot";
|
||||
|
@ -83,6 +83,11 @@ static int __devinit ab8500_spi_probe(struct spi_device *spi)
|
||||
struct ab8500 *ab8500;
|
||||
int ret;
|
||||
|
||||
spi->bits_per_word = 24;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
|
||||
if (!ab8500)
|
||||
return -ENOMEM;
|
||||
|
@ -182,12 +182,27 @@ config SPI_MPC512x_PSC
|
||||
This enables using the Freescale MPC5121 Programmable Serial
|
||||
Controller in SPI master mode.
|
||||
|
||||
config SPI_MPC8xxx
|
||||
tristate "Freescale MPC8xxx SPI controller"
|
||||
config SPI_FSL_LIB
|
||||
tristate
|
||||
depends on FSL_SOC
|
||||
|
||||
config SPI_FSL_SPI
|
||||
tristate "Freescale SPI controller"
|
||||
depends on FSL_SOC
|
||||
select SPI_FSL_LIB
|
||||
help
|
||||
This enables using the Freescale MPC8xxx SPI controllers in master
|
||||
mode.
|
||||
This enables using the Freescale SPI controllers in master mode.
|
||||
MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
|
||||
MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
|
||||
|
||||
config SPI_FSL_ESPI
|
||||
tristate "Freescale eSPI controller"
|
||||
depends on FSL_SOC
|
||||
select SPI_FSL_LIB
|
||||
help
|
||||
This enables using the Freescale eSPI controllers in master mode.
|
||||
From MPC8536, 85xx platform uses the controller, and all P10xx,
|
||||
P20xx, P30xx,P40xx, P50xx uses this controller.
|
||||
|
||||
config SPI_OMAP_UWIRE
|
||||
tristate "OMAP1 MicroWire"
|
||||
@ -298,6 +313,13 @@ config SPI_STMP3XXX
|
||||
help
|
||||
SPI driver for Freescale STMP37xx/378x SoC SSP interface
|
||||
|
||||
config SPI_TOPCLIFF_PCH
|
||||
tristate "Topcliff PCH SPI Controller"
|
||||
depends on PCI
|
||||
help
|
||||
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
|
||||
used in some x86 embedded processors.
|
||||
|
||||
config SPI_TXX9
|
||||
tristate "Toshiba TXx9 SPI controller"
|
||||
depends on GENERIC_GPIO && CPU_TX49XX
|
||||
|
@ -2,9 +2,7 @@
|
||||
# Makefile for kernel SPI drivers.
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_SPI_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
|
||||
|
||||
# small core, mostly translating board-specific
|
||||
# config declarations into driver model code
|
||||
@ -34,11 +32,14 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o
|
||||
obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
|
||||
obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
|
||||
obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o
|
||||
obj-$(CONFIG_SPI_FSL_ESPI) += spi_fsl_espi.o
|
||||
obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o
|
||||
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
|
||||
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
|
||||
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
|
||||
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
|
||||
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o
|
||||
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
|
||||
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
|
||||
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -654,6 +654,8 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
struct spi_transfer *xfer;
|
||||
unsigned long flags;
|
||||
struct device *controller = spi->master->dev.parent;
|
||||
u8 bits;
|
||||
struct atmel_spi_device *asd;
|
||||
|
||||
as = spi_master_get_devdata(spi->master);
|
||||
|
||||
@ -672,8 +674,18 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xfer->bits_per_word) {
|
||||
asd = spi->controller_state;
|
||||
bits = (asd->csr >> 4) & 0xf;
|
||||
if (bits != xfer->bits_per_word - 8) {
|
||||
dev_dbg(&spi->dev, "you can't yet change "
|
||||
"bits_per_word in transfers\n");
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME implement these protocol options!! */
|
||||
if (xfer->bits_per_word || xfer->speed_hz) {
|
||||
if (xfer->speed_hz) {
|
||||
dev_dbg(&spi->dev, "no protocol options yet\n");
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
@ -296,6 +296,19 @@ static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
while (!(__raw_readl(reg) & bit)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -1;
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
@ -309,11 +322,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
u32 l;
|
||||
u8 * rx;
|
||||
const u8 * tx;
|
||||
void __iomem *chstat_reg;
|
||||
|
||||
mcspi = spi_master_get_devdata(spi->master);
|
||||
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
|
||||
l = mcspi_cached_chconf0(spi);
|
||||
|
||||
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
|
||||
|
||||
count = xfer->len;
|
||||
c = count;
|
||||
word_len = cs->word_len;
|
||||
@ -382,6 +398,16 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
if (tx != NULL) {
|
||||
wait_for_completion(&mcspi_dma->dma_tx_completion);
|
||||
dma_unmap_single(NULL, xfer->tx_dma, count, DMA_TO_DEVICE);
|
||||
|
||||
/* for TX_ONLY mode, be sure all words have shifted out */
|
||||
if (rx == NULL) {
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_TXS) < 0)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
else if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_EOT) < 0)
|
||||
dev_err(&spi->dev, "EOT timed out\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (rx != NULL) {
|
||||
@ -435,19 +461,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
return count;
|
||||
}
|
||||
|
||||
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
while (!(__raw_readl(reg) & bit)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -1;
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
@ -489,10 +502,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
goto out;
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "write-%d %02x\n",
|
||||
dev_vdbg(&spi->dev, "write-%d %02x\n",
|
||||
word_len, *tx);
|
||||
#endif
|
||||
__raw_writel(*tx++, tx_reg);
|
||||
}
|
||||
if (rx != NULL) {
|
||||
@ -506,10 +517,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %02x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %02x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
|
||||
dev_err(&spi->dev,
|
||||
@ -522,10 +531,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
}
|
||||
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %02x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %02x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
}
|
||||
} while (c);
|
||||
} else if (word_len <= 16) {
|
||||
@ -542,10 +549,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
goto out;
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "write-%d %04x\n",
|
||||
dev_vdbg(&spi->dev, "write-%d %04x\n",
|
||||
word_len, *tx);
|
||||
#endif
|
||||
__raw_writel(*tx++, tx_reg);
|
||||
}
|
||||
if (rx != NULL) {
|
||||
@ -559,10 +564,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %04x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %04x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
|
||||
dev_err(&spi->dev,
|
||||
@ -575,10 +578,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
}
|
||||
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %04x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %04x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
}
|
||||
} while (c);
|
||||
} else if (word_len <= 32) {
|
||||
@ -595,10 +596,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
goto out;
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "write-%d %08x\n",
|
||||
dev_vdbg(&spi->dev, "write-%d %08x\n",
|
||||
word_len, *tx);
|
||||
#endif
|
||||
__raw_writel(*tx++, tx_reg);
|
||||
}
|
||||
if (rx != NULL) {
|
||||
@ -612,10 +611,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %08x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %08x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
|
||||
dev_err(&spi->dev,
|
||||
@ -628,10 +625,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
}
|
||||
|
||||
*rx++ = __raw_readl(rx_reg);
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(&spi->dev, "read-%d %08x\n",
|
||||
dev_vdbg(&spi->dev, "read-%d %08x\n",
|
||||
word_len, *(rx - 1));
|
||||
#endif
|
||||
}
|
||||
} while (c);
|
||||
}
|
||||
@ -644,6 +639,12 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
} else if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_EOT) < 0)
|
||||
dev_err(&spi->dev, "EOT timed out\n");
|
||||
|
||||
/* disable chan to purge rx datas received in TX_ONLY transfer,
|
||||
* otherwise these rx datas will affect the direct following
|
||||
* RX_ONLY transfer.
|
||||
*/
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
}
|
||||
out:
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
|
@ -404,7 +404,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
goto msg_rejected;
|
||||
}
|
||||
|
||||
if ((t != NULL) && t->bits_per_word)
|
||||
if (t->bits_per_word)
|
||||
bits_per_word = t->bits_per_word;
|
||||
|
||||
if ((bits_per_word != 8) && (bits_per_word != 16)) {
|
||||
@ -415,7 +415,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
goto msg_rejected;
|
||||
}
|
||||
/*make sure buffer length is even when working in 16 bit mode*/
|
||||
if ((t != NULL) && (t->bits_per_word == 16) && (t->len & 1)) {
|
||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
||||
dev_err(&spi->dev,
|
||||
"message rejected : "
|
||||
"odd data length (%d) while in 16 bit mode\n",
|
||||
|
File diff suppressed because it is too large
Load Diff
748
drivers/spi/spi_fsl_espi.c
Normal file
748
drivers/spi/spi_fsl_espi.c
Normal file
@ -0,0 +1,748 @@
|
||||
/*
|
||||
* Freescale eSPI controller driver.
|
||||
*
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_spi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
|
||||
#include "spi_fsl_lib.h"
|
||||
|
||||
/* eSPI Controller registers */
|
||||
struct fsl_espi_reg {
|
||||
__be32 mode; /* 0x000 - eSPI mode register */
|
||||
__be32 event; /* 0x004 - eSPI event register */
|
||||
__be32 mask; /* 0x008 - eSPI mask register */
|
||||
__be32 command; /* 0x00c - eSPI command register */
|
||||
__be32 transmit; /* 0x010 - eSPI transmit FIFO access register*/
|
||||
__be32 receive; /* 0x014 - eSPI receive FIFO access register*/
|
||||
u8 res[8]; /* 0x018 - 0x01c reserved */
|
||||
__be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */
|
||||
};
|
||||
|
||||
struct fsl_espi_transfer {
|
||||
const void *tx_buf;
|
||||
void *rx_buf;
|
||||
unsigned len;
|
||||
unsigned n_tx;
|
||||
unsigned n_rx;
|
||||
unsigned actual_length;
|
||||
int status;
|
||||
};
|
||||
|
||||
/* eSPI Controller mode register definitions */
|
||||
#define SPMODE_ENABLE (1 << 31)
|
||||
#define SPMODE_LOOP (1 << 30)
|
||||
#define SPMODE_TXTHR(x) ((x) << 8)
|
||||
#define SPMODE_RXTHR(x) ((x) << 0)
|
||||
|
||||
/* eSPI Controller CS mode register definitions */
|
||||
#define CSMODE_CI_INACTIVEHIGH (1 << 31)
|
||||
#define CSMODE_CP_BEGIN_EDGECLK (1 << 30)
|
||||
#define CSMODE_REV (1 << 29)
|
||||
#define CSMODE_DIV16 (1 << 28)
|
||||
#define CSMODE_PM(x) ((x) << 24)
|
||||
#define CSMODE_POL_1 (1 << 20)
|
||||
#define CSMODE_LEN(x) ((x) << 16)
|
||||
#define CSMODE_BEF(x) ((x) << 12)
|
||||
#define CSMODE_AFT(x) ((x) << 8)
|
||||
#define CSMODE_CG(x) ((x) << 3)
|
||||
|
||||
/* Default mode/csmode for eSPI controller */
|
||||
#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3))
|
||||
#define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \
|
||||
| CSMODE_AFT(0) | CSMODE_CG(1))
|
||||
|
||||
/* SPIE register values */
|
||||
#define SPIE_NE 0x00000200 /* Not empty */
|
||||
#define SPIE_NF 0x00000100 /* Not full */
|
||||
|
||||
/* SPIM register values */
|
||||
#define SPIM_NE 0x00000200 /* Not empty */
|
||||
#define SPIM_NF 0x00000100 /* Not full */
|
||||
#define SPIE_RXCNT(reg) ((reg >> 24) & 0x3F)
|
||||
#define SPIE_TXCNT(reg) ((reg >> 16) & 0x3F)
|
||||
|
||||
/* SPCOM register values */
|
||||
#define SPCOM_CS(x) ((x) << 30)
|
||||
#define SPCOM_TRANLEN(x) ((x) << 0)
|
||||
#define SPCOM_TRANLEN_MAX 0xFFFF /* Max transaction length */
|
||||
|
||||
static void fsl_espi_change_mode(struct spi_device *spi)
|
||||
{
|
||||
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
|
||||
struct spi_mpc8xxx_cs *cs = spi->controller_state;
|
||||
struct fsl_espi_reg *reg_base = mspi->reg_base;
|
||||
__be32 __iomem *mode = ®_base->csmode[spi->chip_select];
|
||||
__be32 __iomem *espi_mode = ®_base->mode;
|
||||
u32 tmp;
|
||||
unsigned long flags;
|
||||
|
||||
/* Turn off IRQs locally to minimize time that SPI is disabled. */
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Turn off SPI unit prior changing mode */
|
||||
tmp = mpc8xxx_spi_read_reg(espi_mode);
|
||||
mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
|
||||
mpc8xxx_spi_write_reg(mode, cs->hw_mode);
|
||||
mpc8xxx_spi_write_reg(espi_mode, tmp);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
|
||||
{
|
||||
u32 data;
|
||||
u16 data_h;
|
||||
u16 data_l;
|
||||
const u32 *tx = mpc8xxx_spi->tx;
|
||||
|
||||
if (!tx)
|
||||
return 0;
|
||||
|
||||
data = *tx++ << mpc8xxx_spi->tx_shift;
|
||||
data_l = data & 0xffff;
|
||||
data_h = (data >> 16) & 0xffff;
|
||||
swab16s(&data_l);
|
||||
swab16s(&data_h);
|
||||
data = data_h | data_l;
|
||||
|
||||
mpc8xxx_spi->tx = tx;
|
||||
return data;
|
||||
}
|
||||
|
||||
static int fsl_espi_setup_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
int bits_per_word = 0;
|
||||
u8 pm;
|
||||
u32 hz = 0;
|
||||
struct spi_mpc8xxx_cs *cs = spi->controller_state;
|
||||
|
||||
if (t) {
|
||||
bits_per_word = t->bits_per_word;
|
||||
hz = t->speed_hz;
|
||||
}
|
||||
|
||||
/* spi_transfer level calls that work per-word */
|
||||
if (!bits_per_word)
|
||||
bits_per_word = spi->bits_per_word;
|
||||
|
||||
/* Make sure its a bit width we support [4..16] */
|
||||
if ((bits_per_word < 4) || (bits_per_word > 16))
|
||||
return -EINVAL;
|
||||
|
||||
if (!hz)
|
||||
hz = spi->max_speed_hz;
|
||||
|
||||
cs->rx_shift = 0;
|
||||
cs->tx_shift = 0;
|
||||
cs->get_rx = mpc8xxx_spi_rx_buf_u32;
|
||||
cs->get_tx = mpc8xxx_spi_tx_buf_u32;
|
||||
if (bits_per_word <= 8) {
|
||||
cs->rx_shift = 8 - bits_per_word;
|
||||
} else if (bits_per_word <= 16) {
|
||||
cs->rx_shift = 16 - bits_per_word;
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
cs->get_tx = fsl_espi_tx_buf_lsb;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mpc8xxx_spi->rx_shift = cs->rx_shift;
|
||||
mpc8xxx_spi->tx_shift = cs->tx_shift;
|
||||
mpc8xxx_spi->get_rx = cs->get_rx;
|
||||
mpc8xxx_spi->get_tx = cs->get_tx;
|
||||
|
||||
bits_per_word = bits_per_word - 1;
|
||||
|
||||
/* mask out bits we are going to set */
|
||||
cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF));
|
||||
|
||||
cs->hw_mode |= CSMODE_LEN(bits_per_word);
|
||||
|
||||
if ((mpc8xxx_spi->spibrg / hz) > 64) {
|
||||
cs->hw_mode |= CSMODE_DIV16;
|
||||
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
|
||||
|
||||
WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
|
||||
"Will use %d Hz instead.\n", dev_name(&spi->dev),
|
||||
hz, mpc8xxx_spi->spibrg / 1024);
|
||||
if (pm > 16)
|
||||
pm = 16;
|
||||
} else {
|
||||
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
|
||||
}
|
||||
if (pm)
|
||||
pm--;
|
||||
|
||||
cs->hw_mode |= CSMODE_PM(pm);
|
||||
|
||||
fsl_espi_change_mode(spi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
|
||||
unsigned int len)
|
||||
{
|
||||
u32 word;
|
||||
struct fsl_espi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
mspi->count = len;
|
||||
|
||||
/* enable rx ints */
|
||||
mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE);
|
||||
|
||||
/* transmit word */
|
||||
word = mspi->get_tx(mspi);
|
||||
mpc8xxx_spi_write_reg(®_base->transmit, word);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
unsigned int len = t->len;
|
||||
u8 bits_per_word;
|
||||
int ret;
|
||||
|
||||
bits_per_word = spi->bits_per_word;
|
||||
if (t->bits_per_word)
|
||||
bits_per_word = t->bits_per_word;
|
||||
|
||||
mpc8xxx_spi->len = t->len;
|
||||
len = roundup(len, 4) / 4;
|
||||
|
||||
mpc8xxx_spi->tx = t->tx_buf;
|
||||
mpc8xxx_spi->rx = t->rx_buf;
|
||||
|
||||
INIT_COMPLETION(mpc8xxx_spi->done);
|
||||
|
||||
/* Set SPCOM[CS] and SPCOM[TRANLEN] field */
|
||||
if ((t->len - 1) > SPCOM_TRANLEN_MAX) {
|
||||
dev_err(mpc8xxx_spi->dev, "Transaction length (%d)"
|
||||
" beyond the SPCOM[TRANLEN] field\n", t->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
mpc8xxx_spi_write_reg(®_base->command,
|
||||
(SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1)));
|
||||
|
||||
ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wait_for_completion(&mpc8xxx_spi->done);
|
||||
|
||||
/* disable rx ints */
|
||||
mpc8xxx_spi_write_reg(®_base->mask, 0);
|
||||
|
||||
return mpc8xxx_spi->count;
|
||||
}
|
||||
|
||||
static void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd)
|
||||
{
|
||||
if (cmd[1] && cmd[2] && cmd[3]) {
|
||||
cmd[1] = (u8)(addr >> 16);
|
||||
cmd[2] = (u8)(addr >> 8);
|
||||
cmd[3] = (u8)(addr >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int fsl_espi_cmd2addr(u8 *cmd)
|
||||
{
|
||||
if (cmd[1] && cmd[2] && cmd[3])
|
||||
return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_espi_do_trans(struct spi_message *m,
|
||||
struct fsl_espi_transfer *tr)
|
||||
{
|
||||
struct spi_device *spi = m->spi;
|
||||
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_espi_transfer *espi_trans = tr;
|
||||
struct spi_message message;
|
||||
struct spi_transfer *t, *first, trans;
|
||||
int status = 0;
|
||||
|
||||
spi_message_init(&message);
|
||||
memset(&trans, 0, sizeof(trans));
|
||||
|
||||
first = list_first_entry(&m->transfers, struct spi_transfer,
|
||||
transfer_list);
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if ((first->bits_per_word != t->bits_per_word) ||
|
||||
(first->speed_hz != t->speed_hz)) {
|
||||
espi_trans->status = -EINVAL;
|
||||
dev_err(mspi->dev, "bits_per_word/speed_hz should be"
|
||||
" same for the same SPI transfer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trans.speed_hz = t->speed_hz;
|
||||
trans.bits_per_word = t->bits_per_word;
|
||||
trans.delay_usecs = max(first->delay_usecs, t->delay_usecs);
|
||||
}
|
||||
|
||||
trans.len = espi_trans->len;
|
||||
trans.tx_buf = espi_trans->tx_buf;
|
||||
trans.rx_buf = espi_trans->rx_buf;
|
||||
spi_message_add_tail(&trans, &message);
|
||||
|
||||
list_for_each_entry(t, &message.transfers, transfer_list) {
|
||||
if (t->bits_per_word || t->speed_hz) {
|
||||
status = -EINVAL;
|
||||
|
||||
status = fsl_espi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->len)
|
||||
status = fsl_espi_bufs(spi, t);
|
||||
|
||||
if (status) {
|
||||
status = -EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
}
|
||||
|
||||
espi_trans->status = status;
|
||||
fsl_espi_setup_transfer(spi, NULL);
|
||||
}
|
||||
|
||||
static void fsl_espi_cmd_trans(struct spi_message *m,
|
||||
struct fsl_espi_transfer *trans, u8 *rx_buff)
|
||||
{
|
||||
struct spi_transfer *t;
|
||||
u8 *local_buf;
|
||||
int i = 0;
|
||||
struct fsl_espi_transfer *espi_trans = trans;
|
||||
|
||||
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
|
||||
if (!local_buf) {
|
||||
espi_trans->status = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf) {
|
||||
memcpy(local_buf + i, t->tx_buf, t->len);
|
||||
i += t->len;
|
||||
}
|
||||
}
|
||||
|
||||
espi_trans->tx_buf = local_buf;
|
||||
espi_trans->rx_buf = local_buf + espi_trans->n_tx;
|
||||
fsl_espi_do_trans(m, espi_trans);
|
||||
|
||||
espi_trans->actual_length = espi_trans->len;
|
||||
kfree(local_buf);
|
||||
}
|
||||
|
||||
static void fsl_espi_rw_trans(struct spi_message *m,
|
||||
struct fsl_espi_transfer *trans, u8 *rx_buff)
|
||||
{
|
||||
struct fsl_espi_transfer *espi_trans = trans;
|
||||
unsigned int n_tx = espi_trans->n_tx;
|
||||
unsigned int n_rx = espi_trans->n_rx;
|
||||
struct spi_transfer *t;
|
||||
u8 *local_buf;
|
||||
u8 *rx_buf = rx_buff;
|
||||
unsigned int trans_len;
|
||||
unsigned int addr;
|
||||
int i, pos, loop;
|
||||
|
||||
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
|
||||
if (!local_buf) {
|
||||
espi_trans->status = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) {
|
||||
trans_len = n_rx - pos;
|
||||
if (trans_len > SPCOM_TRANLEN_MAX - n_tx)
|
||||
trans_len = SPCOM_TRANLEN_MAX - n_tx;
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf) {
|
||||
memcpy(local_buf + i, t->tx_buf, t->len);
|
||||
i += t->len;
|
||||
}
|
||||
}
|
||||
|
||||
addr = fsl_espi_cmd2addr(local_buf);
|
||||
addr += pos;
|
||||
fsl_espi_addr2cmd(addr, local_buf);
|
||||
|
||||
espi_trans->n_tx = n_tx;
|
||||
espi_trans->n_rx = trans_len;
|
||||
espi_trans->len = trans_len + n_tx;
|
||||
espi_trans->tx_buf = local_buf;
|
||||
espi_trans->rx_buf = local_buf + n_tx;
|
||||
fsl_espi_do_trans(m, espi_trans);
|
||||
|
||||
memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
|
||||
|
||||
if (loop > 0)
|
||||
espi_trans->actual_length += espi_trans->len - n_tx;
|
||||
else
|
||||
espi_trans->actual_length += espi_trans->len;
|
||||
}
|
||||
|
||||
kfree(local_buf);
|
||||
}
|
||||
|
||||
static void fsl_espi_do_one_msg(struct spi_message *m)
|
||||
{
|
||||
struct spi_transfer *t;
|
||||
u8 *rx_buf = NULL;
|
||||
unsigned int n_tx = 0;
|
||||
unsigned int n_rx = 0;
|
||||
struct fsl_espi_transfer espi_trans;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf)
|
||||
n_tx += t->len;
|
||||
if (t->rx_buf) {
|
||||
n_rx += t->len;
|
||||
rx_buf = t->rx_buf;
|
||||
}
|
||||
}
|
||||
|
||||
espi_trans.n_tx = n_tx;
|
||||
espi_trans.n_rx = n_rx;
|
||||
espi_trans.len = n_tx + n_rx;
|
||||
espi_trans.actual_length = 0;
|
||||
espi_trans.status = 0;
|
||||
|
||||
if (!rx_buf)
|
||||
fsl_espi_cmd_trans(m, &espi_trans, NULL);
|
||||
else
|
||||
fsl_espi_rw_trans(m, &espi_trans, rx_buf);
|
||||
|
||||
m->actual_length = espi_trans.actual_length;
|
||||
m->status = espi_trans.status;
|
||||
m->complete(m->context);
|
||||
}
|
||||
|
||||
static int fsl_espi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
int retval;
|
||||
u32 hw_mode;
|
||||
u32 loop_mode;
|
||||
struct spi_mpc8xxx_cs *cs = spi->controller_state;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
|
||||
if (!cs) {
|
||||
cs = kzalloc(sizeof *cs, GFP_KERNEL);
|
||||
if (!cs)
|
||||
return -ENOMEM;
|
||||
spi->controller_state = cs;
|
||||
}
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
hw_mode = cs->hw_mode; /* Save orginal settings */
|
||||
cs->hw_mode = mpc8xxx_spi_read_reg(
|
||||
®_base->csmode[spi->chip_select]);
|
||||
/* mask out bits we are going to set */
|
||||
cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH
|
||||
| CSMODE_REV);
|
||||
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cs->hw_mode |= CSMODE_CP_BEGIN_EDGECLK;
|
||||
if (spi->mode & SPI_CPOL)
|
||||
cs->hw_mode |= CSMODE_CI_INACTIVEHIGH;
|
||||
if (!(spi->mode & SPI_LSB_FIRST))
|
||||
cs->hw_mode |= CSMODE_REV;
|
||||
|
||||
/* Handle the loop mode */
|
||||
loop_mode = mpc8xxx_spi_read_reg(®_base->mode);
|
||||
loop_mode &= ~SPMODE_LOOP;
|
||||
if (spi->mode & SPI_LOOP)
|
||||
loop_mode |= SPMODE_LOOP;
|
||||
mpc8xxx_spi_write_reg(®_base->mode, loop_mode);
|
||||
|
||||
retval = fsl_espi_setup_transfer(spi, NULL);
|
||||
if (retval < 0) {
|
||||
cs->hw_mode = hw_mode; /* Restore settings */
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
|
||||
{
|
||||
struct fsl_espi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
/* We need handle RX first */
|
||||
if (events & SPIE_NE) {
|
||||
u32 rx_data;
|
||||
|
||||
/* Spin until RX is done */
|
||||
while (SPIE_RXCNT(events) < min(4, mspi->len)) {
|
||||
cpu_relax();
|
||||
events = mpc8xxx_spi_read_reg(®_base->event);
|
||||
}
|
||||
mspi->len -= 4;
|
||||
|
||||
rx_data = mpc8xxx_spi_read_reg(®_base->receive);
|
||||
|
||||
if (mspi->rx)
|
||||
mspi->get_rx(rx_data, mspi);
|
||||
}
|
||||
|
||||
if (!(events & SPIE_NF)) {
|
||||
int ret;
|
||||
|
||||
/* spin until TX is done */
|
||||
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
|
||||
®_base->event)) & SPIE_NF) == 0, 1000, 0);
|
||||
if (!ret) {
|
||||
dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the events */
|
||||
mpc8xxx_spi_write_reg(®_base->event, events);
|
||||
|
||||
mspi->count -= 1;
|
||||
if (mspi->count) {
|
||||
u32 word = mspi->get_tx(mspi);
|
||||
|
||||
mpc8xxx_spi_write_reg(®_base->transmit, word);
|
||||
} else {
|
||||
complete(&mspi->done);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
|
||||
{
|
||||
struct mpc8xxx_spi *mspi = context_data;
|
||||
struct fsl_espi_reg *reg_base = mspi->reg_base;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 events;
|
||||
|
||||
/* Get interrupt events(tx/rx) */
|
||||
events = mpc8xxx_spi_read_reg(®_base->event);
|
||||
if (events)
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events);
|
||||
|
||||
fsl_espi_cpu_irq(mspi, events);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
iounmap(mspi->reg_base);
|
||||
}
|
||||
|
||||
static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
|
||||
struct resource *mem, unsigned int irq)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
u32 regval;
|
||||
int i, ret = 0;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
|
||||
if (!master) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, master);
|
||||
|
||||
ret = mpc8xxx_spi_probe(dev, mem, irq);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
|
||||
master->setup = fsl_espi_setup;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
|
||||
mpc8xxx_spi->spi_remove = fsl_espi_remove;
|
||||
|
||||
mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
|
||||
if (!mpc8xxx_spi->reg_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
reg_base = mpc8xxx_spi->reg_base;
|
||||
|
||||
/* Register for SPI Interrupt */
|
||||
ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
|
||||
0, "fsl_espi", mpc8xxx_spi);
|
||||
if (ret)
|
||||
goto free_irq;
|
||||
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
|
||||
mpc8xxx_spi->rx_shift = 16;
|
||||
mpc8xxx_spi->tx_shift = 24;
|
||||
}
|
||||
|
||||
/* SPI controller initializations */
|
||||
mpc8xxx_spi_write_reg(®_base->mode, 0);
|
||||
mpc8xxx_spi_write_reg(®_base->mask, 0);
|
||||
mpc8xxx_spi_write_reg(®_base->command, 0);
|
||||
mpc8xxx_spi_write_reg(®_base->event, 0xffffffff);
|
||||
|
||||
/* Init eSPI CS mode register */
|
||||
for (i = 0; i < pdata->max_chipselect; i++)
|
||||
mpc8xxx_spi_write_reg(®_base->csmode[i], CSMODE_INIT_VAL);
|
||||
|
||||
/* Enable SPI interface */
|
||||
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
|
||||
|
||||
mpc8xxx_spi_write_reg(®_base->mode, regval);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0)
|
||||
goto unreg_master;
|
||||
|
||||
dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq);
|
||||
|
||||
return master;
|
||||
|
||||
unreg_master:
|
||||
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
|
||||
free_irq:
|
||||
iounmap(mpc8xxx_spi->reg_base);
|
||||
err_probe:
|
||||
spi_master_put(master);
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int of_fsl_espi_get_chipselects(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
const u32 *prop;
|
||||
int len;
|
||||
|
||||
prop = of_get_property(np, "fsl,espi-num-chipselects", &len);
|
||||
if (!prop || len < sizeof(*prop)) {
|
||||
dev_err(dev, "No 'fsl,espi-num-chipselects' property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->max_chipselect = *prop;
|
||||
pdata->cs_control = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit of_fsl_espi_probe(struct platform_device *ofdev,
|
||||
const struct of_device_id *ofid)
|
||||
{
|
||||
struct device *dev = &ofdev->dev;
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct spi_master *master;
|
||||
struct resource mem;
|
||||
struct resource irq;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
ret = of_mpc8xxx_spi_probe(ofdev, ofid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_fsl_espi_get_chipselects(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_address_to_resource(np, 0, &mem);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_irq_to_resource(np, 0, &irq);
|
||||
if (!ret) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
master = fsl_espi_probe(dev, &mem, irq.start);
|
||||
if (IS_ERR(master)) {
|
||||
ret = PTR_ERR(master);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit of_fsl_espi_remove(struct platform_device *dev)
|
||||
{
|
||||
return mpc8xxx_spi_remove(&dev->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_fsl_espi_match[] = {
|
||||
{ .compatible = "fsl,mpc8536-espi" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_fsl_espi_match);
|
||||
|
||||
static struct of_platform_driver fsl_espi_driver = {
|
||||
.driver = {
|
||||
.name = "fsl_espi",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_fsl_espi_match,
|
||||
},
|
||||
.probe = of_fsl_espi_probe,
|
||||
.remove = __devexit_p(of_fsl_espi_remove),
|
||||
};
|
||||
|
||||
static int __init fsl_espi_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&fsl_espi_driver);
|
||||
}
|
||||
module_init(fsl_espi_init);
|
||||
|
||||
static void __exit fsl_espi_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&fsl_espi_driver);
|
||||
}
|
||||
module_exit(fsl_espi_exit);
|
||||
|
||||
MODULE_AUTHOR("Mingkai Hu");
|
||||
MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");
|
||||
MODULE_LICENSE("GPL");
|
237
drivers/spi/spi_fsl_lib.c
Normal file
237
drivers/spi/spi_fsl_lib.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Freescale SPI/eSPI controller driver library.
|
||||
*
|
||||
* Maintainer: Kumar Gala
|
||||
*
|
||||
* Copyright (C) 2006 Polycom, Inc.
|
||||
*
|
||||
* CPM SPI and QE buffer descriptors mode support:
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_spi.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
|
||||
#include "spi_fsl_lib.h"
|
||||
|
||||
#define MPC8XXX_SPI_RX_BUF(type) \
|
||||
void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
|
||||
{ \
|
||||
type *rx = mpc8xxx_spi->rx; \
|
||||
*rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
|
||||
mpc8xxx_spi->rx = rx; \
|
||||
}
|
||||
|
||||
#define MPC8XXX_SPI_TX_BUF(type) \
|
||||
u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
|
||||
{ \
|
||||
u32 data; \
|
||||
const type *tx = mpc8xxx_spi->tx; \
|
||||
if (!tx) \
|
||||
return 0; \
|
||||
data = *tx++ << mpc8xxx_spi->tx_shift; \
|
||||
mpc8xxx_spi->tx = tx; \
|
||||
return data; \
|
||||
}
|
||||
|
||||
MPC8XXX_SPI_RX_BUF(u8)
|
||||
MPC8XXX_SPI_RX_BUF(u16)
|
||||
MPC8XXX_SPI_RX_BUF(u32)
|
||||
MPC8XXX_SPI_TX_BUF(u8)
|
||||
MPC8XXX_SPI_TX_BUF(u16)
|
||||
MPC8XXX_SPI_TX_BUF(u32)
|
||||
|
||||
struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata)
|
||||
{
|
||||
return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
|
||||
}
|
||||
|
||||
void mpc8xxx_spi_work(struct work_struct *work)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
|
||||
work);
|
||||
|
||||
spin_lock_irq(&mpc8xxx_spi->lock);
|
||||
while (!list_empty(&mpc8xxx_spi->queue)) {
|
||||
struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
|
||||
struct spi_message, queue);
|
||||
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock_irq(&mpc8xxx_spi->lock);
|
||||
|
||||
if (mpc8xxx_spi->spi_do_one_msg)
|
||||
mpc8xxx_spi->spi_do_one_msg(m);
|
||||
|
||||
spin_lock_irq(&mpc8xxx_spi->lock);
|
||||
}
|
||||
spin_unlock_irq(&mpc8xxx_spi->lock);
|
||||
}
|
||||
|
||||
int mpc8xxx_spi_transfer(struct spi_device *spi,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
unsigned long flags;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = -EINPROGRESS;
|
||||
|
||||
spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
|
||||
list_add_tail(&m->queue, &mpc8xxx_spi->queue);
|
||||
queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
|
||||
spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mpc8xxx_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
kfree(spi->controller_state);
|
||||
}
|
||||
|
||||
const char *mpc8xxx_spi_strmode(unsigned int flags)
|
||||
{
|
||||
if (flags & SPI_QE_CPU_MODE) {
|
||||
return "QE CPU";
|
||||
} else if (flags & SPI_CPM_MODE) {
|
||||
if (flags & SPI_QE)
|
||||
return "QE";
|
||||
else if (flags & SPI_CPM2)
|
||||
return "CPM2";
|
||||
else
|
||||
return "CPM1";
|
||||
}
|
||||
return "CPU";
|
||||
}
|
||||
|
||||
int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
int ret = 0;
|
||||
|
||||
master = dev_get_drvdata(dev);
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
|
||||
| SPI_LSB_FIRST | SPI_LOOP;
|
||||
|
||||
master->transfer = mpc8xxx_spi_transfer;
|
||||
master->cleanup = mpc8xxx_spi_cleanup;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->dev = dev;
|
||||
mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
|
||||
mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
|
||||
mpc8xxx_spi->flags = pdata->flags;
|
||||
mpc8xxx_spi->spibrg = pdata->sysclk;
|
||||
mpc8xxx_spi->irq = irq;
|
||||
|
||||
mpc8xxx_spi->rx_shift = 0;
|
||||
mpc8xxx_spi->tx_shift = 0;
|
||||
|
||||
init_completion(&mpc8xxx_spi->done);
|
||||
|
||||
master->bus_num = pdata->bus_num;
|
||||
master->num_chipselect = pdata->max_chipselect;
|
||||
|
||||
spin_lock_init(&mpc8xxx_spi->lock);
|
||||
init_completion(&mpc8xxx_spi->done);
|
||||
INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
|
||||
INIT_LIST_HEAD(&mpc8xxx_spi->queue);
|
||||
|
||||
mpc8xxx_spi->workqueue = create_singlethread_workqueue(
|
||||
dev_name(master->dev.parent));
|
||||
if (mpc8xxx_spi->workqueue == NULL) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __devexit mpc8xxx_spi_remove(struct device *dev)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct spi_master *master;
|
||||
|
||||
master = dev_get_drvdata(dev);
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
|
||||
flush_workqueue(mpc8xxx_spi->workqueue);
|
||||
destroy_workqueue(mpc8xxx_spi->workqueue);
|
||||
spi_unregister_master(master);
|
||||
|
||||
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
|
||||
|
||||
if (mpc8xxx_spi->spi_remove)
|
||||
mpc8xxx_spi->spi_remove(mpc8xxx_spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
|
||||
const struct of_device_id *ofid)
|
||||
{
|
||||
struct device *dev = &ofdev->dev;
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct mpc8xxx_spi_probe_info *pinfo;
|
||||
struct fsl_spi_platform_data *pdata;
|
||||
const void *prop;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = &pinfo->pdata;
|
||||
dev->platform_data = pdata;
|
||||
|
||||
/* Allocate bus num dynamically. */
|
||||
pdata->bus_num = -1;
|
||||
|
||||
/* SPI controller is either clocked from QE or SoC clock. */
|
||||
pdata->sysclk = get_brgfreq();
|
||||
if (pdata->sysclk == -1) {
|
||||
pdata->sysclk = fsl_get_sys_freq();
|
||||
if (pdata->sysclk == -1) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
prop = of_get_property(np, "mode", NULL);
|
||||
if (prop && !strcmp(prop, "cpu-qe"))
|
||||
pdata->flags = SPI_QE_CPU_MODE;
|
||||
else if (prop && !strcmp(prop, "qe"))
|
||||
pdata->flags = SPI_CPM_MODE | SPI_QE;
|
||||
else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
|
||||
pdata->flags = SPI_CPM_MODE | SPI_CPM2;
|
||||
else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
|
||||
pdata->flags = SPI_CPM_MODE | SPI_CPM1;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(pinfo);
|
||||
return ret;
|
||||
}
|
124
drivers/spi/spi_fsl_lib.h
Normal file
124
drivers/spi/spi_fsl_lib.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Freescale SPI/eSPI controller driver library.
|
||||
*
|
||||
* Maintainer: Kumar Gala
|
||||
*
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2006 Polycom, Inc.
|
||||
*
|
||||
* CPM SPI and QE buffer descriptors mode support:
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#ifndef __SPI_FSL_LIB_H__
|
||||
#define __SPI_FSL_LIB_H__
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/* SPI/eSPI Controller driver's private data. */
|
||||
struct mpc8xxx_spi {
|
||||
struct device *dev;
|
||||
void *reg_base;
|
||||
|
||||
/* rx & tx bufs from the spi_transfer */
|
||||
const void *tx;
|
||||
void *rx;
|
||||
#ifdef CONFIG_SPI_FSL_ESPI
|
||||
int len;
|
||||
#endif
|
||||
|
||||
int subblock;
|
||||
struct spi_pram __iomem *pram;
|
||||
struct cpm_buf_desc __iomem *tx_bd;
|
||||
struct cpm_buf_desc __iomem *rx_bd;
|
||||
|
||||
struct spi_transfer *xfer_in_progress;
|
||||
|
||||
/* dma addresses for CPM transfers */
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t rx_dma;
|
||||
bool map_tx_dma;
|
||||
bool map_rx_dma;
|
||||
|
||||
dma_addr_t dma_dummy_tx;
|
||||
dma_addr_t dma_dummy_rx;
|
||||
|
||||
/* functions to deal with different sized buffers */
|
||||
void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
|
||||
u32(*get_tx) (struct mpc8xxx_spi *);
|
||||
|
||||
/* hooks for different controller driver */
|
||||
void (*spi_do_one_msg) (struct spi_message *m);
|
||||
void (*spi_remove) (struct mpc8xxx_spi *mspi);
|
||||
|
||||
unsigned int count;
|
||||
unsigned int irq;
|
||||
|
||||
unsigned nsecs; /* (clock cycle time)/2 */
|
||||
|
||||
u32 spibrg; /* SPIBRG input clock */
|
||||
u32 rx_shift; /* RX data reg shift when in qe mode */
|
||||
u32 tx_shift; /* TX data reg shift when in qe mode */
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct work;
|
||||
|
||||
struct list_head queue;
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
struct spi_mpc8xxx_cs {
|
||||
/* functions to deal with different sized buffers */
|
||||
void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
|
||||
u32 (*get_tx) (struct mpc8xxx_spi *);
|
||||
u32 rx_shift; /* RX data reg shift when in qe mode */
|
||||
u32 tx_shift; /* TX data reg shift when in qe mode */
|
||||
u32 hw_mode; /* Holds HW mode register settings */
|
||||
};
|
||||
|
||||
static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
|
||||
{
|
||||
out_be32(reg, val);
|
||||
}
|
||||
|
||||
static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
|
||||
{
|
||||
return in_be32(reg);
|
||||
}
|
||||
|
||||
struct mpc8xxx_spi_probe_info {
|
||||
struct fsl_spi_platform_data pdata;
|
||||
int *gpios;
|
||||
bool *alow_flags;
|
||||
};
|
||||
|
||||
extern u32 mpc8xxx_spi_tx_buf_u8(struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
extern u32 mpc8xxx_spi_tx_buf_u16(struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
extern u32 mpc8xxx_spi_tx_buf_u32(struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
extern void mpc8xxx_spi_rx_buf_u8(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
extern void mpc8xxx_spi_rx_buf_u16(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
extern void mpc8xxx_spi_rx_buf_u32(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
|
||||
|
||||
extern struct mpc8xxx_spi_probe_info *to_of_pinfo(
|
||||
struct fsl_spi_platform_data *pdata);
|
||||
extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t, unsigned int len);
|
||||
extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m);
|
||||
extern void mpc8xxx_spi_cleanup(struct spi_device *spi);
|
||||
extern const char *mpc8xxx_spi_strmode(unsigned int flags);
|
||||
extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
|
||||
unsigned int irq);
|
||||
extern int mpc8xxx_spi_remove(struct device *dev);
|
||||
extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev,
|
||||
const struct of_device_id *ofid);
|
||||
|
||||
#endif /* __SPI_FSL_LIB_H__ */
|
File diff suppressed because it is too large
Load Diff
@ -261,15 +261,25 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
||||
if (dma_mode) {
|
||||
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
||||
s3c2410_dma_config(sdd->tx_dmach, 1);
|
||||
s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8);
|
||||
s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
|
||||
xfer->tx_dma, xfer->len);
|
||||
s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
|
||||
} else {
|
||||
unsigned char *buf = (unsigned char *) xfer->tx_buf;
|
||||
int i = 0;
|
||||
while (i < xfer->len)
|
||||
writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);
|
||||
switch (sdd->cur_bpw) {
|
||||
case 32:
|
||||
iowrite32_rep(regs + S3C64XX_SPI_TX_DATA,
|
||||
xfer->tx_buf, xfer->len / 4);
|
||||
break;
|
||||
case 16:
|
||||
iowrite16_rep(regs + S3C64XX_SPI_TX_DATA,
|
||||
xfer->tx_buf, xfer->len / 2);
|
||||
break;
|
||||
default:
|
||||
iowrite8_rep(regs + S3C64XX_SPI_TX_DATA,
|
||||
xfer->tx_buf, xfer->len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +296,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
||||
| S3C64XX_SPI_PACKET_CNT_EN,
|
||||
regs + S3C64XX_SPI_PACKET_CNT);
|
||||
s3c2410_dma_config(sdd->rx_dmach, 1);
|
||||
s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8);
|
||||
s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
|
||||
xfer->rx_dma, xfer->len);
|
||||
s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
|
||||
@ -366,20 +376,26 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
unsigned char *buf;
|
||||
int i;
|
||||
|
||||
/* If it was only Tx */
|
||||
if (xfer->rx_buf == NULL) {
|
||||
sdd->state &= ~TXBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
buf = xfer->rx_buf;
|
||||
while (i < xfer->len)
|
||||
buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA);
|
||||
|
||||
switch (sdd->cur_bpw) {
|
||||
case 32:
|
||||
ioread32_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||
xfer->rx_buf, xfer->len / 4);
|
||||
break;
|
||||
case 16:
|
||||
ioread16_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||
xfer->rx_buf, xfer->len / 2);
|
||||
break;
|
||||
default:
|
||||
ioread8_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||
xfer->rx_buf, xfer->len);
|
||||
break;
|
||||
}
|
||||
sdd->state &= ~RXBUSY;
|
||||
}
|
||||
|
||||
@ -399,13 +415,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
|
||||
|
||||
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||
void __iomem *regs = sdd->regs;
|
||||
u32 val;
|
||||
|
||||
/* Disable Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
if (sci->clk_from_cmu) {
|
||||
clk_disable(sdd->src_clk);
|
||||
} else {
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
}
|
||||
|
||||
/* Set Polarity and Phase */
|
||||
val = readl(regs + S3C64XX_SPI_CH_CFG);
|
||||
@ -429,29 +450,39 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||
switch (sdd->cur_bpw) {
|
||||
case 32:
|
||||
val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD;
|
||||
val |= S3C64XX_SPI_MODE_CH_TSZ_WORD;
|
||||
break;
|
||||
case 16:
|
||||
val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD;
|
||||
val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD;
|
||||
break;
|
||||
default:
|
||||
val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE;
|
||||
val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE;
|
||||
break;
|
||||
}
|
||||
val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */
|
||||
|
||||
writel(val, regs + S3C64XX_SPI_MODE_CFG);
|
||||
|
||||
/* Configure Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val &= ~S3C64XX_SPI_PSR_MASK;
|
||||
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
|
||||
& S3C64XX_SPI_PSR_MASK);
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
if (sci->clk_from_cmu) {
|
||||
/* Configure Clock */
|
||||
/* There is half-multiplier before the SPI */
|
||||
clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
|
||||
/* Enable Clock */
|
||||
clk_enable(sdd->src_clk);
|
||||
} else {
|
||||
/* Configure Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val &= ~S3C64XX_SPI_PSR_MASK;
|
||||
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
|
||||
& S3C64XX_SPI_PSR_MASK);
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
|
||||
/* Enable Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val |= S3C64XX_SPI_ENCLK_ENABLE;
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
/* Enable Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val |= S3C64XX_SPI_ENCLK_ENABLE;
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
}
|
||||
}
|
||||
|
||||
static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
|
||||
@ -499,6 +530,7 @@ static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
|
||||
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||
struct device *dev = &sdd->pdev->dev;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
@ -514,6 +546,9 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
|
||||
/* Map until end or first fail */
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
|
||||
if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
|
||||
continue;
|
||||
|
||||
if (xfer->tx_buf != NULL) {
|
||||
xfer->tx_dma = dma_map_single(dev,
|
||||
(void *)xfer->tx_buf, xfer->len,
|
||||
@ -545,6 +580,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
|
||||
static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||
struct device *dev = &sdd->pdev->dev;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
@ -553,6 +589,9 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
|
||||
if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
|
||||
continue;
|
||||
|
||||
if (xfer->rx_buf != NULL
|
||||
&& xfer->rx_dma != XFER_DMAADDR_INVALID)
|
||||
dma_unmap_single(dev, xfer->rx_dma,
|
||||
@ -608,6 +647,14 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
|
||||
bpw = xfer->bits_per_word ? : spi->bits_per_word;
|
||||
speed = xfer->speed_hz ? : spi->max_speed_hz;
|
||||
|
||||
if (xfer->len % (bpw / 8)) {
|
||||
dev_err(&spi->dev,
|
||||
"Xfer length(%u) not a multiple of word size(%u)\n",
|
||||
xfer->len, bpw / 8);
|
||||
status = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
|
||||
sdd->cur_bpw = bpw;
|
||||
sdd->cur_speed = speed;
|
||||
@ -798,7 +845,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct s3c64xx_spi_info *sci;
|
||||
struct spi_message *msg;
|
||||
u32 psr, speed;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
@ -841,31 +887,36 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
}
|
||||
|
||||
/* Check if we can provide the requested rate */
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */
|
||||
if (!sci->clk_from_cmu) {
|
||||
u32 psr, speed;
|
||||
|
||||
if (spi->max_speed_hz > speed)
|
||||
spi->max_speed_hz = speed;
|
||||
/* Max possible */
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
|
||||
|
||||
psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
|
||||
psr &= S3C64XX_SPI_PSR_MASK;
|
||||
if (psr == S3C64XX_SPI_PSR_MASK)
|
||||
psr--;
|
||||
if (spi->max_speed_hz > speed)
|
||||
spi->max_speed_hz = speed;
|
||||
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
if (spi->max_speed_hz < speed) {
|
||||
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
|
||||
psr++;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto setup_exit;
|
||||
psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
|
||||
psr &= S3C64XX_SPI_PSR_MASK;
|
||||
if (psr == S3C64XX_SPI_PSR_MASK)
|
||||
psr--;
|
||||
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
if (spi->max_speed_hz < speed) {
|
||||
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
|
||||
psr++;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto setup_exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
if (spi->max_speed_hz >= speed)
|
||||
spi->max_speed_hz = speed;
|
||||
else
|
||||
err = -EINVAL;
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
if (spi->max_speed_hz >= speed)
|
||||
spi->max_speed_hz = speed;
|
||||
else
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
setup_exit:
|
||||
|
||||
@ -888,7 +939,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
|
||||
/* Disable Interrupts - we use Polling if not DMA mode */
|
||||
writel(0, regs + S3C64XX_SPI_INT_EN);
|
||||
|
||||
writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
|
||||
if (!sci->clk_from_cmu)
|
||||
writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
|
||||
regs + S3C64XX_SPI_CLK_CFG);
|
||||
writel(0, regs + S3C64XX_SPI_MODE_CFG);
|
||||
writel(0, regs + S3C64XX_SPI_PACKET_CNT);
|
||||
|
1303
drivers/spi/spi_topcliff_pch.c
Normal file
1303
drivers/spi/spi_topcliff_pch.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -228,6 +228,7 @@ enum ssp_chip_select {
|
||||
};
|
||||
|
||||
|
||||
struct dma_chan;
|
||||
/**
|
||||
* struct pl022_ssp_master - device.platform_data for SPI controller devices.
|
||||
* @num_chipselect: chipselects are used to distinguish individual
|
||||
@ -235,11 +236,16 @@ enum ssp_chip_select {
|
||||
* each slave has a chipselect signal, but it's common that not
|
||||
* every chipselect is connected to a slave.
|
||||
* @enable_dma: if true enables DMA driven transfers.
|
||||
* @dma_rx_param: parameter to locate an RX DMA channel.
|
||||
* @dma_tx_param: parameter to locate a TX DMA channel.
|
||||
*/
|
||||
struct pl022_ssp_controller {
|
||||
u16 bus_id;
|
||||
u8 num_chipselect;
|
||||
u8 enable_dma:1;
|
||||
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
|
||||
void *dma_rx_param;
|
||||
void *dma_tx_param;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -270,20 +276,13 @@ struct pl022_ssp_controller {
|
||||
* @dma_config: DMA configuration for SSP controller and peripheral
|
||||
*/
|
||||
struct pl022_config_chip {
|
||||
struct device *dev;
|
||||
enum ssp_loopback lbm;
|
||||
enum ssp_interface iface;
|
||||
enum ssp_hierarchy hierarchy;
|
||||
bool slave_tx_disable;
|
||||
struct ssp_clock_params clk_freq;
|
||||
enum ssp_rx_endian endian_rx;
|
||||
enum ssp_tx_endian endian_tx;
|
||||
enum ssp_data_size data_size;
|
||||
enum ssp_mode com_mode;
|
||||
enum ssp_rx_level_trig rx_lev_trig;
|
||||
enum ssp_tx_level_trig tx_lev_trig;
|
||||
enum ssp_spi_clk_phase clk_phase;
|
||||
enum ssp_spi_clk_pol clk_pol;
|
||||
enum ssp_microwire_ctrl_len ctrl_len;
|
||||
enum ssp_microwire_wait_state wait_state;
|
||||
enum ssp_duplex duplex;
|
||||
|
Loading…
Reference in New Issue
Block a user