From 08142ec6f4f19a5e882c5650676c6d1c441abd92 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:34:26 +0200 Subject: misc: Add i2s driver Signed-off-by: Robert Marklund Conflicts: drivers/misc/Makefile Conflicts: drivers/misc/Kconfig drivers/misc/Makefile --- Documentation/DocBook/i2s.tmpl | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Documentation/DocBook/i2s.tmpl (limited to 'Documentation') diff --git a/Documentation/DocBook/i2s.tmpl b/Documentation/DocBook/i2s.tmpl new file mode 100644 index 00000000000..6b6c50572e2 --- /dev/null +++ b/Documentation/DocBook/i2s.tmpl @@ -0,0 +1,97 @@ + + + + + + I2S + + + + Sandeep + Kaushik + +
+ sandeep.kaushik@st.com +
+
+
+
+ + + 2008-2009 + STMicroelectronics Pvt Ltd + + + + + Linux standard functions + + + + + + + + This documentation 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. + + + + 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 + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + Introduction + + This Documentation describes the APIs provided by the I2S Bus Driver. I2S bus supports different + protocols like I2S, PCM, SPI etc. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + This Section lists the functions exported by the I2S bus driver. These functions cater to all the protocols + supported namely: I2S, PCM, SPI. + +!Edrivers/misc/i2s/i2s.c + +
-- cgit v1.2.3 From 0b7be4cf79c48e5617c878cf4c4197ff0325ae3a Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Fri, 18 Mar 2011 14:57:19 +0100 Subject: spi: add stm msp driver Signed-off-by: Robert Marklund --- Documentation/DocBook/msp.tmpl | 104 ++ arch/arm/mach-ux500/include/mach/msp.h | 970 ++++++++++++++++ drivers/spi/Kconfig | 14 + drivers/spi/Makefile | 1 + drivers/spi/stm_msp.c | 1929 ++++++++++++++++++++++++++++++++ include/linux/spi/stm_msp.h | 126 +++ 6 files changed, 3144 insertions(+) create mode 100644 Documentation/DocBook/msp.tmpl create mode 100644 arch/arm/mach-ux500/include/mach/msp.h create mode 100644 drivers/spi/stm_msp.c create mode 100644 include/linux/spi/stm_msp.h (limited to 'Documentation') diff --git a/Documentation/DocBook/msp.tmpl b/Documentation/DocBook/msp.tmpl new file mode 100644 index 00000000000..55cec352a76 --- /dev/null +++ b/Documentation/DocBook/msp.tmpl @@ -0,0 +1,104 @@ + + + + + + MSP + + + + Sandeep + Kaushik + +
+ sandeep.kaushik@st.com +
+
+
+
+ + + 2008-2009 + STMicroelectronics Pvt Ltd + + + + + Linux standard functions + + + + + + + + This documentation 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. + + + + 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 + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + Introduction + + This Documentation describes the API's provided by the MSP controller Driver. + MSP controller supports different protocols like I2S, PCM, SPI etc. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + Not Applicable. + + + + + Private Functions + + This Section lists the functions used by the MSP controller driver. + These functions cater to all the protocols supported namely: I2S, PCM, SPI. + +!Idrivers/misc/i2s/msp_i2s.c + +
diff --git a/arch/arm/mach-ux500/include/mach/msp.h b/arch/arm/mach-ux500/include/mach/msp.h new file mode 100644 index 00000000000..aecca3a59a3 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/msp.h @@ -0,0 +1,970 @@ +/* + * Copyright (c) 2009 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#ifndef _STM_MSP_HEADER +#define _STM_MSP_HEADER +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Generic config struct. Use the actual values defined below for global + * control register + */ + +enum msp_state { + MSP_STATE_IDLE = 0, + MSP_STATE_CONFIGURED = 1, + MSP_STATE_RUN = 2, +}; + +enum msp_rx_comparison_enable_mode { + MSP_COMPARISON_DISABLED = 0, + MSP_COMPARISON_NONEQUAL_ENABLED = 2, + MSP_COMPARISON_EQUAL_ENABLED = 3 +}; + +#define RMCEN_BIT 0 +#define RMCSF_BIT 1 +#define RCMPM_BIT 3 +#define TMCEN_BIT 5 +#define TNCSF_BIT 6 + +struct msp_multichannel_config { + bool rx_multichannel_enable; + bool tx_multichannel_enable; + enum msp_rx_comparison_enable_mode rx_comparison_enable_mode; + u8 padding; + u32 comparison_value; + u32 comparison_mask; + u32 rx_channel_0_enable; + u32 rx_channel_1_enable; + u32 rx_channel_2_enable; + u32 rx_channel_3_enable; + u32 tx_channel_0_enable; + u32 tx_channel_1_enable; + u32 tx_channel_2_enable; + u32 tx_channel_3_enable; +}; + +/** + * struct msp_protocol_desc- MSP Protocol desc structure per MSP. + * @rx_phase_mode: rx_phase_mode whether single or dual. + * @tx_phase_mode: tx_phase_mode whether single or dual. + * @rx_phase2_start_mode: rx_phase2_start_mode whether imediate or after + * some delay. + * @tx_phase2_start_mode: tx_phase2_start_mode whether imediate or after + * some delay. + * @rx_bit_transfer_format: MSP or LSB. + * @tx_bit_transfer_format: MSP or LSB. + * @rx_frame_length_1: Frame1 length 1,2,3.. + * @rx_frame_length_2: Frame2 length 1,2,3.. + * @tx_frame_length_1: Frame1 length 1,2,3.. + * @tx_frame_length_2: Frame2 length 1,2,3.. + * @rx_element_length_1: Element1 length 1,2,... + * @rx_element_length_2: Element2 length 1,2,... + * @tx_element_length_1: Element1 length 1,2,... + * @tx_element_length_2: Element2 length 1,2,... + * @rx_data_delay: Delay in clk cycle after frame sync + * @tx_data_delay: Delay in clk cycle after frame sync + * @rx_clock_pol: Rxpol whether rising or falling.It indicates pol of bit clock. + * @tx_clock_pol: Txpol whether rising or falling.It indicates pol of bit clock. + * @rx_frame_sync_pol: Frame sync pol whether rising or Falling. + * @tx_frame_sync_pol: Frame sync pol whether rising or Falling. + * @rx_half_word_swap: Word swap half word, full word. + * @tx_half_word_swap: Word swap half word, full word. + * @compression_mode: Compression mode whether Alaw or Ulaw or disabled. + * @expansion_mode: Compression mode whether Alaw or Ulaw or disabled. + * @spi_clk_mode: Spi clock mode to be enabled or not. + * @spi_burst_mode: Spi burst mode to be enabled or not. + * @frame_sync_ignore: Frame sync to be ignored or not. Ignore in case of Audio + * codec acting as Master. + * @frame_period: Frame period (clk cycles) after which new frame sync occurs. + * @frame_width: Frame width (clk cycles) after which frame sycn changes state. + * @total_clocks_for_one_frame: No. of clk cycles per frame. + * + * Main Msp protocol descriptor data structure to be used to store various info + * in transmit or recevie configuration registers of an MSP. + */ + +struct msp_protocol_desc { + u32 rx_phase_mode; + u32 tx_phase_mode; + u32 rx_phase2_start_mode; + u32 tx_phase2_start_mode; + u32 rx_bit_transfer_format; + u32 tx_bit_transfer_format; + u32 rx_frame_length_1; + u32 rx_frame_length_2; + u32 tx_frame_length_1; + u32 tx_frame_length_2; + u32 rx_element_length_1; + u32 rx_element_length_2; + u32 tx_element_length_1; + u32 tx_element_length_2; + u32 rx_data_delay; + u32 tx_data_delay; + u32 rx_clock_pol; + u32 tx_clock_pol; + u32 rx_frame_sync_pol; + u32 tx_frame_sync_pol; + u32 rx_half_word_swap; + u32 tx_half_word_swap; + u32 compression_mode; + u32 expansion_mode; + u32 spi_clk_mode; + u32 spi_burst_mode; + u32 frame_sync_ignore; + u32 frame_period; + u32 frame_width; + u32 total_clocks_for_one_frame; +}; + +/** + * struct trans_data - MSP transfer data structure used during xfer. + * @message: i2s message. + * @msp: msp structure. + * @tx_handler: callback handler for transmit path. + * @rx_handler: callback handler for receive path. + * @tx_callback_data: callback data for transmit. + * @rx_callback_data: callback data for receive. + * + */ +struct trans_data { + struct i2s_message message; + struct msp *msp; + void (*tx_handler) (void *data); + void (*rx_handler) (void *data); + void *tx_callback_data; + void *rx_callback_data; +}; + +/** + * struct msp_config- MSP configuration structure used by i2s client. + * @input_clock_freq: Input clock frequency default is 48MHz. + * @rx_clock_sel: Receive clock selection (Provided by Sample Gen or external + * source). + * @tx_clock_sel: Transmit clock selection (Provided by Sample Gen or external. + * source). + * @srg_clock_sel: APB clock or clock dervied from Slave (Audio codec). + * @rx_frame_sync_pol: Receive frame sync polarity. + * @tx_frame_sync_pol: Transmit frame sync polarity. + * @rx_frame_sync_sel: Rx frame sync signal is provided by which source. + * External source or by frame generator logic. + * @tx_frame_sync_sel: Tx frame sync signal is provided by which source. + * External source or by frame generator logic. + * @rx_fifo_config: Receive fifo enable or not. + * @tx_fifo_config: Transmit fifo enable or not. + * @spi_clk_mode: In case of SPI protocol spi modes: Normal, Zero delay or + * half cycle delay. + * @spi_burst_mode: Spi burst mode is enabled or not. + * @loopback_enable: Loopback mode. + * @tx_data_enable: Transmit extra delay enable. + * @default_protocol_desc: Flag to indicate client defined protocol desc or + * statically defined in msp.h. + * @protocol_desc: Protocol desc structure filled by i2s client driver. + * In case client defined default_prtocol_desc as 0. + * @multichannel_configured: multichannel configuration structure. + * @multichannel_config: multichannel is enabled or not. + * @direction: Transmit, Receive or Both. + * @work_mode: Dma, Polling or Interrupt. + * @protocol: I2S, PCM, etc. + * @frame_freq: Sampling freq at which data is sampled. + * @frame_size: size of element. + * @data_size: data size which defines the format in which data is written on + * transmit or receive fifo. Only three modes 8,16,32 are supported. + * @def_elem_len: Flag to indicate whether default element length is to be used + * or should be changed acc to data size defined by user at run time. + * @iodelay: value for the MSP_IODLY register + * @handler: callback handler in case of interrupt or dma. + * @tx_callback_data: Callback data for transmit. + * @rx_callback_data: Callback data for receive. + * + * Main Msp configuration data structure used by i2s client driver to fill + * various info like data size, frequency etc. + */ +struct msp_config { + unsigned int input_clock_freq; + unsigned int rx_clock_sel; + unsigned int tx_clock_sel; + unsigned int srg_clock_sel; + unsigned int rx_frame_sync_pol; + unsigned int tx_frame_sync_pol; + unsigned int rx_frame_sync_sel; + unsigned int tx_frame_sync_sel; + unsigned int rx_fifo_config; + unsigned int tx_fifo_config; + unsigned int spi_clk_mode; + unsigned int spi_burst_mode; + unsigned int loopback_enable; + unsigned int tx_data_enable; + unsigned int default_protocol_desc; + struct msp_protocol_desc protocol_desc; + int multichannel_configured; + struct msp_multichannel_config multichannel_config; + unsigned int direction; + unsigned int work_mode; + unsigned int protocol; + unsigned int frame_freq; + unsigned int frame_size; + enum msp_data_size data_size; + unsigned int def_elem_len; + unsigned int iodelay; + void (*handler) (void *data); + void *tx_callback_data; + void *rx_callback_data; + +}; + +/*** Protocols ***/ +enum msp_protocol { + MSP_I2S_PROTOCOL, + MSP_PCM_PROTOCOL, + MSP_PCM_COMPAND_PROTOCOL, + MSP_AC97_PROTOCOL, + MSP_MASTER_SPI_PROTOCOL, + MSP_SLAVE_SPI_PROTOCOL, + MSP_INVALID_PROTOCOL +}; + +/*** Sample Frequencies ***/ +/* These are no longer required, frequencies in Hz can be used directly */ +enum msp_sample_freq { + MSP_SAMPLE_FREQ_NOT_SUPPORTED = -1, + MSP_SAMPLE_FREQ_8KHZ = 8000, + MSP_SAMPLE_FREQ_12KHZ = 12000, + MSP_SAMPLE_FREQ_16KHZ = 16000, + MSP_SAMPLE_FREQ_24KHZ = 24000, + MSP_SAMPLE_FREQ_32KHZ = 32000, + MSP_SAMPLE_FREQ_44KHZ = 44000, + MSP_SAMPLE_FREQ_48KHZ = 48000, + MSP_SAMPLE_FREQ_64KHZ = 64000, + MSP_SAMPLE_FREQ_88KHZ = 88000, + MSP_SAMPLE_FREQ_96KHZ = 96000, + MSP_SAMPLE_FREQ_22KHZ = 22000, + MSP_SAMPLE_FREQ_11KHZ = 11000 +}; + +/*** Input Frequencies ***/ +/* These are no longer required, frequencies in Hz can be used directly */ +enum msp_in_clock_freq { + MSP_INPUT_FREQ_1MHZ = 1000, + MSP_INPUT_FREQ_2MHZ = 2000, + MSP_INPUT_FREQ_3MHZ = 3000, + MSP_INPUT_FREQ_4MHZ = 4000, + MSP_INPUT_FREQ_5MHZ = 5000, + MSP_INPUT_FREQ_6MHZ = 6000, + MSP_INPUT_FREQ_8MHZ = 8000, + MSP_INPUT_FREQ_11MHZ = 11000, + MSP_INPUT_FREQ_12MHZ = 12000, + MSP_INPUT_FREQ_16MHZ = 16000, + MSP_INPUT_FREQ_22MHZ = 22000, + MSP_INPUT_FREQ_24MHZ = 24000, + MSP_INPUT_FREQ_48MHZ = 48000 +}; + +#define MSP_INPUT_FREQ_APB 48000000 + +/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), + * 32 bits accesses (stereo). + ***/ +enum msp_stereo_mode { + MSP_MONO, + MSP_STEREO +}; + +/* Direction (Transmit/Receive mode) */ +enum msp_direction { + MSP_TRANSMIT_MODE, + MSP_RECEIVE_MODE, + MSP_BOTH_T_R_MODE +}; + +/* Dma mode should be used for large transfers, + * polling mode should be used for transfers of a few bytes + */ +enum msp_xfer_mode { + MSP_DMA_MODE, + MSP_POLLING_MODE, + MSP_INTERRUPT_MODE +}; + +/* User client for the MSP */ +enum msp_user { + MSP_NO_USER = 0, + MSP_USER_SPI, + MSP_USER_ALSA, + MSP_USER_SAA, +}; + +/*Flag structure for MSPx*/ +struct msp_flag { + struct semaphore lock; + enum msp_user user; +}; + +/* User client for the MSP */ +enum msp_mode { + MSP_NO_MODE = 0, + MSP_MODE_SPI, + MSP_MODE_NON_SPI, +}; + +/* Transmit and receive configuration register */ +#define MSP_BIG_ENDIAN 0x00000000 +#define MSP_LITTLE_ENDIAN 0x00001000 +#define MSP_UNEXPECTED_FS_ABORT 0x00000000 +#define MSP_UNEXPECTED_FS_IGNORE 0x00008000 +#define MSP_NON_MODE_BIT_MASK 0x00009000 + +/* Global configuration register */ +#define RX_ENABLE 0x00000001 +#define RX_FIFO_ENABLE 0x00000002 +#define RX_SYNC_SRG 0x00000010 +#define RX_CLK_POL_RISING 0x00000020 +#define RX_CLK_SEL_SRG 0x00000040 +#define TX_ENABLE 0x00000100 +#define TX_FIFO_ENABLE 0x00000200 +#define TX_SYNC_SRG_PROG 0x00001800 +#define TX_SYNC_SRG_AUTO 0x00001000 +#define TX_CLK_POL_RISING 0x00002000 +#define TX_CLK_SEL_SRG 0x00004000 +#define TX_EXTRA_DELAY_ENABLE 0x00008000 +#define SRG_ENABLE 0x00010000 +#define FRAME_GEN_ENABLE 0x00100000 +#define SRG_CLK_SEL_APB 0x00000000 +#define RX_FIFO_SYNC_HI 0x00000000 +#define TX_FIFO_SYNC_HI 0x00000000 +#define SPI_CLK_MODE_NORMAL 0x00000000 + +/* SPI Clock Modes enumertion + * SPI clock modes of MSP provides compatibility with + * the SPI protocol.MSP supports 2 SPI transfer formats. + * MSP_ZERO_DELAY_SPI_MODE:MSP transmits data over Tx/Rx + * Lines immediately after MSPTCK/MSPRCK rising/falling edge. + * MSP_HALF_CYCLE_DELY_SPI_MODE:MSP transmits data one-half cycle + * ahead of the rising/falling edge of the MSPTCK + */ + +#define MSP_FRAME_SIZE_AUTO -1 + + +#define MSP_DR 0x00 +#define MSP_GCR 0x04 +#define MSP_TCF 0x08 +#define MSP_RCF 0x0c +#define MSP_SRG 0x10 +#define MSP_FLR 0x14 +#define MSP_DMACR 0x18 + +#define MSP_IMSC 0x20 +#define MSP_RIS 0x24 +#define MSP_MIS 0x28 +#define MSP_ICR 0x2c +#define MSP_MCR 0x30 +#define MSP_RCV 0x34 +#define MSP_RCM 0x38 + +#define MSP_TCE0 0x40 +#define MSP_TCE1 0x44 +#define MSP_TCE2 0x48 +#define MSP_TCE3 0x4c + +#define MSP_RCE0 0x60 +#define MSP_RCE1 0x64 +#define MSP_RCE2 0x68 +#define MSP_RCE3 0x6c +#define MSP_IODLY 0x70 + +#define MSP_ITCR 0x80 +#define MSP_ITIP 0x84 +#define MSP_ITOP 0x88 +#define MSP_TSTDR 0x8c + +#define MSP_PID0 0xfe0 +#define MSP_PID1 0xfe4 +#define MSP_PID2 0xfe8 +#define MSP_PID3 0xfec + +#define MSP_CID0 0xff0 +#define MSP_CID1 0xff4 +#define MSP_CID2 0xff8 +#define MSP_CID3 0xffc + +/* Single or dual phase mode */ +enum msp_phase_mode { + MSP_SINGLE_PHASE, + MSP_DUAL_PHASE +}; + +/* Frame length */ +enum msp_frame_length { + MSP_FRAME_LENGTH_1 = 0, + MSP_FRAME_LENGTH_2 = 1, + MSP_FRAME_LENGTH_4 = 3, + MSP_FRAME_LENGTH_8 = 7, + MSP_FRAME_LENGTH_12 = 11, + MSP_FRAME_LENGTH_16 = 15, + MSP_FRAME_LENGTH_20 = 19, + MSP_FRAME_LENGTH_32 = 31, + MSP_FRAME_LENGTH_48 = 47, + MSP_FRAME_LENGTH_64 = 63 +}; + +/* Element length */ +enum msp_elem_length { + MSP_ELEM_LENGTH_8 = 0, + MSP_ELEM_LENGTH_10 = 1, + MSP_ELEM_LENGTH_12 = 2, + MSP_ELEM_LENGTH_14 = 3, + MSP_ELEM_LENGTH_16 = 4, + MSP_ELEM_LENGTH_20 = 5, + MSP_ELEM_LENGTH_24 = 6, + MSP_ELEM_LENGTH_32 = 7 +}; + +enum msp_data_xfer_width { + MSP_DATA_TRANSFER_WIDTH_BYTE, + MSP_DATA_TRANSFER_WIDTH_HALFWORD, + MSP_DATA_TRANSFER_WIDTH_WORD +}; + +enum msp_frame_sync { + MSP_FRAME_SYNC_UNIGNORE = 0, + MSP_FRAME_SYNC_IGNORE = 1, + +}; + +enum msp_phase2_start_mode { + MSP_PHASE2_START_MODE_IMEDIATE, + MSP_PHASE2_START_MODE_FRAME_SYNC +}; + +enum msp_btf { + MSP_BTF_MS_BIT_FIRST = 0, + MSP_BTF_LS_BIT_FIRST = 1 +}; + +enum msp_frame_sync_pol { + MSP_FRAME_SYNC_POL_ACTIVE_HIGH = 0, + MSP_FRAME_SYNC_POL_ACTIVE_LOW = 1 +}; + +/* Data delay (in bit clock cycles) */ +enum msp_delay { + MSP_DELAY_0 = 0, + MSP_DELAY_1 = 1, + MSP_DELAY_2 = 2, + MSP_DELAY_3 = 3 +}; + +/* Configurations of clocks (transmit, receive or sample rate generator) */ +enum msp_edge { + MSP_FALLING_EDGE = 0, + MSP_RISING_EDGE = 1, +}; + +enum msp_hws { + MSP_HWS_NO_SWAP = 0, + MSP_HWS_BYTE_SWAP_IN_WORD = 1, + MSP_HWS_BYTE_SWAP_IN_EACH_HALF_WORD = 2, + MSP_HWS_HALF_WORD_SWAP_IN_WORD = 3 +}; + +enum msp_compress_mode { + MSP_COMPRESS_MODE_LINEAR = 0, + MSP_COMPRESS_MODE_MU_LAW = 2, + MSP_COMPRESS_MODE_A_LAW = 3 +}; + +enum msp_spi_clock_mode { + MSP_SPI_CLOCK_MODE_NON_SPI = 0, + MSP_SPI_CLOCK_MODE_ZERO_DELAY = 2, + MSP_SPI_CLOCK_MODE_HALF_CYCLE_DELAY = 3 +}; + +enum msp_spi_burst_mode { + MSP_SPI_BURST_MODE_DISABLE = 0, + MSP_SPI_BURST_MODE_ENABLE = 1 +}; + +enum msp_expand_mode { + MSP_EXPAND_MODE_LINEAR = 0, + MSP_EXPAND_MODE_LINEAR_SIGNED = 1, + MSP_EXPAND_MODE_MU_LAW = 2, + MSP_EXPAND_MODE_A_LAW = 3 +}; + +/* Protocol dependant parameters list */ +#define RX_ENABLE_MASK BIT(0) +#define RX_FIFO_ENABLE_MASK BIT(1) +#define RX_FRAME_SYNC_MASK BIT(2) +#define DIRECT_COMPANDING_MASK BIT(3) +#define RX_SYNC_SEL_MASK BIT(4) +#define RX_CLK_POL_MASK BIT(5) +#define RX_CLK_SEL_MASK BIT(6) +#define LOOPBACK_MASK BIT(7) +#define TX_ENABLE_MASK BIT(8) +#define TX_FIFO_ENABLE_MASK BIT(9) +#define TX_FRAME_SYNC_MASK BIT(10) +#define TX_MSP_TDR_TSR BIT(11) +#define TX_SYNC_SEL_MASK (BIT(12) | BIT(11)) +#define TX_CLK_POL_MASK BIT(13) +#define TX_CLK_SEL_MASK BIT(14) +#define TX_EXTRA_DELAY_MASK BIT(15) +#define SRG_ENABLE_MASK BIT(16) +#define SRG_CLK_POL_MASK BIT(17) +#define SRG_CLK_SEL_MASK (BIT(19) | BIT(18)) +#define FRAME_GEN_EN_MASK BIT(20) +#define SPI_CLK_MODE_MASK (BIT(22) | BIT(21)) +#define SPI_BURST_MODE_MASK BIT(23) + +#define RXEN_SHIFT 0 +#define RFFEN_SHIFT 1 +#define RFSPOL_SHIFT 2 +#define DCM_SHIFT 3 +#define RFSSEL_SHIFT 4 +#define RCKPOL_SHIFT 5 +#define RCKSEL_SHIFT 6 +#define LBM_SHIFT 7 +#define TXEN_SHIFT 8 +#define TFFEN_SHIFT 9 +#define TFSPOL_SHIFT 10 +#define TFSSEL_SHIFT 11 +#define TCKPOL_SHIFT 13 +#define TCKSEL_SHIFT 14 +#define TXDDL_SHIFT 15 +#define SGEN_SHIFT 16 +#define SCKPOL_SHIFT 17 +#define SCKSEL_SHIFT 18 +#define FGEN_SHIFT 20 +#define SPICKM_SHIFT 21 +#define TBSWAP_SHIFT 28 + +#define RCKPOL_MASK BIT(0) +#define TCKPOL_MASK BIT(0) +#define SPICKM_MASK (BIT(1) | BIT(0)) +#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) +#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) +#define MSP_SPI_CLK_MODE_BITS(n) ((n & SPICKM_MASK) << SPICKM_SHIFT) + + + +/* Use this to clear the clock mode bits to non-spi */ +#define MSP_NON_SPI_CLK_MASK (BIT(22) | BIT(21)) + +#define P1ELEN_SHIFT 0 +#define P1FLEN_SHIFT 3 +#define DTYP_SHIFT 10 +#define ENDN_SHIFT 12 +#define DDLY_SHIFT 13 +#define FSIG_SHIFT 15 +#define P2ELEN_SHIFT 16 +#define P2FLEN_SHIFT 19 +#define P2SM_SHIFT 26 +#define P2EN_SHIFT 27 +#define FRAME_SYNC_SHIFT 15 + + +#define P1ELEN_MASK 0x00000007 +#define P2ELEN_MASK 0x00070000 +#define P1FLEN_MASK 0x00000378 +#define P2FLEN_MASK 0x03780000 +#define DDLY_MASK 0x00003000 +#define DTYP_MASK 0x00000600 +#define P2SM_MASK 0x04000000 +#define P2EN_MASK 0x08000000 +#define ENDN_MASK 0x00001000 +#define TFSPOL_MASK 0x00000400 +#define TBSWAP_MASK 0x30000000 +#define COMPANDING_MODE_MASK 0x00000c00 +#define FRAME_SYNC_MASK 0x00008000 + +#define MSP_P1_ELEM_LEN_BITS(n) (n & P1ELEN_MASK) +#define MSP_P2_ELEM_LEN_BITS(n) (((n) << P2ELEN_SHIFT) & P2ELEN_MASK) +#define MSP_P1_FRAME_LEN_BITS(n) (((n) << P1FLEN_SHIFT) & P1FLEN_MASK) +#define MSP_P2_FRAME_LEN_BITS(n) (((n) << P2FLEN_SHIFT) & P2FLEN_MASK) +#define MSP_DATA_DELAY_BITS(n) (((n) << DDLY_SHIFT) & DDLY_MASK) +#define MSP_DATA_TYPE_BITS(n) (((n) << DTYP_SHIFT) & DTYP_MASK) +#define MSP_P2_START_MODE_BIT(n) ((n << P2SM_SHIFT) & P2SM_MASK) +#define MSP_P2_ENABLE_BIT(n) ((n << P2EN_SHIFT) & P2EN_MASK) +#define MSP_SET_ENDIANNES_BIT(n) ((n << ENDN_SHIFT) & ENDN_MASK) +#define MSP_FRAME_SYNC_POL(n) ((n << TFSPOL_SHIFT) & TFSPOL_MASK) +#define MSP_DATA_WORD_SWAP(n) ((n << TBSWAP_SHIFT) & TBSWAP_MASK) +#define MSP_SET_COMPANDING_MODE(n) ((n << DTYP_SHIFT) & COMPANDING_MODE_MASK) +#define MSP_SET_FRAME_SYNC_IGNORE(n) ((n << FRAME_SYNC_SHIFT) & \ + FRAME_SYNC_MASK) + +/* Flag register */ +#define RX_BUSY BIT(0) +#define RX_FIFO_EMPTY BIT(1) +#define RX_FIFO_FULL BIT(2) +#define TX_BUSY BIT(3) +#define TX_FIFO_EMPTY BIT(4) +#define TX_FIFO_FULL BIT(5) + +#define RBUSY_SHIFT 0 +#define RFE_SHIFT 1 +#define RFU_SHIFT 2 +#define TBUSY_SHIFT 3 +#define TFE_SHIFT 4 +#define TFU_SHIFT 5 + +/* Multichannel control register */ +#define RMCEN_SHIFT 0 +#define RMCSF_SHIFT 1 +#define RCMPM_SHIFT 3 +#define TMCEN_SHIFT 5 +#define TNCSF_SHIFT 6 + +/* Sample rate generator register */ +#define SCKDIV_SHIFT 0 +#define FRWID_SHIFT 10 +#define FRPER_SHIFT 16 + +#define SCK_DIV_MASK 0x0000003FF +#define FRAME_WIDTH_BITS(n) (((n) << FRWID_SHIFT) & 0x0000FC00) +#define FRAME_PERIOD_BITS(n) (((n) << FRPER_SHIFT) & 0x1FFF0000) + +/* DMA controller register */ +#define RX_DMA_ENABLE BIT(0) +#define TX_DMA_ENABLE BIT(1) + +#define RDMAE_SHIFT 0 +#define TDMAE_SHIFT 1 + +/* Interrupt Register */ +#define RECEIVE_SERVICE_INT BIT(0) +#define RECEIVE_OVERRUN_ERROR_INT BIT(1) +#define RECEIVE_FRAME_SYNC_ERR_INT BIT(2) +#define RECEIVE_FRAME_SYNC_INT BIT(3) +#define TRANSMIT_SERVICE_INT BIT(4) +#define TRANSMIT_UNDERRUN_ERR_INT BIT(5) +#define TRANSMIT_FRAME_SYNC_ERR_INT BIT(6) +#define TRANSMIT_FRAME_SYNC_INT BIT(7) +#define ALL_INT 0x000000ff + +/* + * Protocol configuration values I2S: + * Single phase, 16 bits, 2 words per frame + */ +#define I2S_PROTOCOL_DESC \ +{ \ + MSP_SINGLE_PHASE, \ + MSP_SINGLE_PHASE, \ + MSP_PHASE2_START_MODE_IMEDIATE, \ + MSP_PHASE2_START_MODE_IMEDIATE, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_ELEM_LENGTH_32, \ + MSP_ELEM_LENGTH_32, \ + MSP_ELEM_LENGTH_32, \ + MSP_ELEM_LENGTH_32, \ + MSP_DELAY_1, \ + MSP_DELAY_1, \ + MSP_RISING_EDGE, \ + MSP_RISING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 31, \ + 15, \ + 32, \ +} + +#define PCM_PROTOCOL_DESC \ +{ \ + MSP_DUAL_PHASE, \ + MSP_DUAL_PHASE, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_ELEM_LENGTH_16, \ + MSP_ELEM_LENGTH_16, \ + MSP_ELEM_LENGTH_16, \ + MSP_ELEM_LENGTH_16, \ + MSP_DELAY_0, \ + MSP_DELAY_0, \ + MSP_FALLING_EDGE, \ + MSP_FALLING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 255, \ + 0, \ + 256, \ +} + +/* Companded PCM: Single phase, 8 bits, 1 word per frame */ +#define PCM_COMPAND_PROTOCOL_DESC \ +{ \ + MSP_SINGLE_PHASE, \ + MSP_SINGLE_PHASE, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_DELAY_0, \ + MSP_DELAY_0, \ + MSP_FALLING_EDGE, \ + MSP_RISING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 255, \ + 0, \ + 256, \ +} + +/* + * AC97: Double phase, 1 element of 16 bits during first phase, + * 12 elements of 20 bits in second phase. + */ +#define AC97_PROTOCOL_DESC \ +{ \ + MSP_DUAL_PHASE, \ + MSP_DUAL_PHASE, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_12, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_12, \ + MSP_ELEM_LENGTH_16, \ + MSP_ELEM_LENGTH_20, \ + MSP_ELEM_LENGTH_16, \ + MSP_ELEM_LENGTH_20, \ + MSP_DELAY_1, \ + MSP_DELAY_1, \ + MSP_FALLING_EDGE, \ + MSP_RISING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 255, \ + 0, \ + 256, \ +} + +#define SPI_MASTER_PROTOCOL_DESC \ +{ \ + MSP_SINGLE_PHASE, \ + MSP_SINGLE_PHASE, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_DELAY_1, \ + MSP_DELAY_1, \ + MSP_RISING_EDGE, \ + MSP_FALLING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 255, \ + 0, \ + 256, \ +} + +#define SPI_SLAVE_PROTOCOL_DESC \ +{ \ + MSP_SINGLE_PHASE, \ + MSP_SINGLE_PHASE, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_PHASE2_START_MODE_FRAME_SYNC, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_BTF_MS_BIT_FIRST, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_FRAME_LENGTH_1, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_ELEM_LENGTH_8, \ + MSP_DELAY_1, \ + MSP_DELAY_1, \ + MSP_RISING_EDGE, \ + MSP_FALLING_EDGE, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_FRAME_SYNC_POL_ACTIVE_HIGH, \ + MSP_HWS_NO_SWAP, \ + MSP_HWS_NO_SWAP, \ + MSP_COMPRESS_MODE_LINEAR, \ + MSP_EXPAND_MODE_LINEAR, \ + MSP_SPI_CLOCK_MODE_NON_SPI, \ + MSP_SPI_BURST_MODE_DISABLE, \ + MSP_FRAME_SYNC_IGNORE, \ + 255, \ + 0, \ + 256, \ +} + +#define MSP_FRAME_PERIOD_IN_MONO_MODE 256 +#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32 +#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16 + +/* + * No of registers to backup during + * suspend resume + */ +#define MAX_MSP_BACKUP_REGS 36 + +enum enum_i2s_controller { + MSP_0_I2S_CONTROLLER = 1, + MSP_1_I2S_CONTROLLER, + MSP_2_I2S_CONTROLLER, + MSP_3_I2S_CONTROLLER, +}; + +/** + * struct msp - Main msp controller data structure per MSP. + * @work_mode: Mode i.e dma, polling or interrupt. + * @id: Controller id like MSP1 or MSP2 etc. + * @msp_io_error: To indicate error while transferring. + * @registers: MSP's register base address. + * @actual_data_size: Data size in which data needs to send or receive. + * @irq: MSP's irq number. + * @i2s_cont: MSP's Controller's structure pointer created per MSP. + * @lock: semaphore lock acquired while configuring msp. + * @dma_cfg_tx: TX DMA configuration + * @dma_cfg_rx: RX DMA configuration + * @tx_pipeid: TX DMA channel + * @rx_pipeid: RX DMA channel + * @msp_state: Current state of msp. + * @read: Function pointer for read, u8_msp_read,u16_msp_read,u32_msp_read. + * @write: Function pointer for write, u8_msp_write,u16_msp_write,u32_msp_write. + * @transfer: Function pointer for type of transfer i.e dma,polling or interrupt + * @xfer_data: MSP's transfer data structure. Contains info about current xfer. + * @plat_init: MSP's initialization function. + * @plat_exit: MSP's Exit function. + * @notify_timer: Timer used in Polling mode to prevent hang. + * @polling_flag: Flag used in error handling while polling. + * @def_elem_len: Flag indicates whether default elem len to be used in + * protocol_desc or not. + * @vape_opp_constraint: 1 if constraint is applied to have vape at 100OPP; 0 otherwise + * @infinite: true if an infinite transfer has been configured + * + * Main Msp private data structure to be used to store various info of a + * particular MSP.Longer description + */ +struct msp { + int work_mode; + enum enum_i2s_controller id; + int msp_io_error; + void __iomem *registers; + enum msp_data_size actual_data_size; + int irq; + struct i2s_controller *i2s_cont; + struct semaphore lock; + struct stedma40_chan_cfg *dma_cfg_rx; + struct stedma40_chan_cfg *dma_cfg_tx; + struct dma_chan *tx_pipeid; + struct dma_chan *rx_pipeid; + enum msp_state msp_state; + void (*read) (struct trans_data *xfer_data); + void (*write) (struct trans_data *xfer_data); + int (*transfer) (struct msp *msp, struct i2s_message *message); + struct trans_data xfer_data; + int (*plat_init) (void); + int (*plat_exit) (void); + struct timer_list notify_timer; + int polling_flag; + int def_elem_len; + struct clk *clk; + unsigned int direction; + int users; + int loopback_enable; + u32 backup_regs[MAX_MSP_BACKUP_REGS]; + int vape_opp_constraint; + bool infinite; +}; + +/** + * struct msp_i2s_platform_data - Main msp controller platform data structure. + * @id: Controller id like MSP1 or MSP2 etc. + * @msp_i2s_dma_rx: RX DMA channel config + * @msp_i2s_dma_tx: RX DMA channel config + * @msp_i2s_init: MSP's initialization function. + * @msp_i2s_exit: MSP's Exit function. + * @backup_regs: used for backup registers during suspend resume. + * + * Platform data structure passed by devices.c file. + */ +struct msp_i2s_platform_data { + enum enum_i2s_controller id; + struct stedma40_chan_cfg *msp_i2s_dma_rx; + struct stedma40_chan_cfg *msp_i2s_dma_tx; + int (*msp_i2s_init) (void); + int (*msp_i2s_exit) (void); +}; + +#endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 00c024039c9..b8aa82fa2a6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -113,6 +113,20 @@ config SPI_BITBANG need it. You only need to select this explicitly to support driver modules that aren't part of this kernel tree. +config STM_MSP_SPI + tristate "STM MSP CONTROLLER (SPI master)" + default y + help + This enables using the STM MSP controller in master + mode. + +config SPI_WORKQUEUE + bool "SPI_WORKQUEUE" + depends on STM_MSP_SPI + default n + help + This feature allow SPI works to be deferred in MSP driver. + config SPI_BUTTERFLY tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)" depends on PARPORT diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d75d2198ff..6c10ed18023 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -62,4 +62,5 @@ obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o +obj-$(CONFIG_STM_MSP_SPI) += stm_msp.o diff --git a/drivers/spi/stm_msp.c b/drivers/spi/stm_msp.c new file mode 100644 index 00000000000..6b9d7bb75e5 --- /dev/null +++ b/drivers/spi/stm_msp.c @@ -0,0 +1,1929 @@ +/* + * drivers/spi/stm_msp.c + * + * Copyright (C) 2010 STMicroelectronics Pvt. Ltd. + * + * Author: Sachin Verma + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * MSP Controller Register Offsets + */ +#define MSP_DR(r) (r + 0x000) +#define MSP_GCR(r) (r + 0x004) +#define MSP_TCF(r) (r + 0x008) +#define MSP_RCF(r) (r + 0x00C) +#define MSP_SRG(r) (r + 0x010) +#define MSP_FLR(r) (r + 0x014) +#define MSP_DMACR(r) (r + 0x018) +#define MSP_IMSC(r) (r + 0x020) +#define MSP_RIS(r) (r + 0x024) +#define MSP_MIS(r) (r + 0x028) +#define MSP_ICR(r) (r + 0x02C) +#define MSP_MCR(r) (r + 0x030) +#define MSP_RCV(r) (r + 0x034) +#define MSP_RCM(r) (r + 0x038) +#define MSP_TCE0(r) (r + 0x040) +#define MSP_TCE1(r) (r + 0x044) +#define MSP_TCE2(r) (r + 0x048) +#define MSP_TCE3(r) (r + 0x04C) +#define MSP_RCE0(r) (r + 0x060) +#define MSP_RCE1(r) (r + 0x064) +#define MSP_RCE2(r) (r + 0x068) +#define MSP_RCE3(r) (r + 0x06C) +#define MSP_PID0(r) (r + 0xFE0) +#define MSP_PID1(r) (r + 0xFE4) +#define MSP_PID2(r) (r + 0xFE8) +#define MSP_PID3(r) (r + 0xFEC) + +/** + * MSP Global Configuration Register - MSP_GCR + */ +#define MSP_GCR_MASK_RXEN ((u32)(0x1UL << 0)) +#define MSP_GCR_MASK_RFFEN ((u32)(0x1UL << 1)) +#define MSP_GCR_MASK_RFSPOL ((u32)(0x1UL << 2)) +#define MSP_GCR_MASK_DCM ((u32)(0x1UL << 3)) +#define MSP_GCR_MASK_RFSSEL ((u32)(0x1UL << 4)) +#define MSP_GCR_MASK_RCKPOL ((u32)(0x1UL << 5)) +#define MSP_GCR_MASK_RCKSEL ((u32)(0x1UL << 6)) +#define MSP_GCR_MASK_LBM ((u32)(0x1UL << 7)) +#define MSP_GCR_MASK_TXEN ((u32)(0x1UL << 8)) +#define MSP_GCR_MASK_TFFEN ((u32)(0x1UL << 9)) +#define MSP_GCR_MASK_TFSPOL ((u32)(0x1UL << 10)) +#define MSP_GCR_MASK_TFSSEL ((u32)(0x3UL << 11)) +#define MSP_GCR_MASK_TCKPOL ((u32)(0x1UL << 13)) +#define MSP_GCR_MASK_TCKSEL ((u32)(0x1UL << 14)) +#define MSP_GCR_MASK_TXDDL ((u32)(0x1UL << 15)) +#define MSP_GCR_MASK_SGEN ((u32)(0x1UL << 16)) +#define MSP_GCR_MASK_SCKPOL ((u32)(0x1UL << 17)) +#define MSP_GCR_MASK_SCKSEL ((u32)(0x3UL << 18)) +#define MSP_GCR_MASK_FGEN ((u32)(0x1UL << 20)) +#define MSP_GCR_MASK_SPICKM ((u32)(0x3UL << 21)) +#define MSP_GCR_MASK_SPIBME ((u32)(0x1UL << 23)) + +/** + * MSP Transmit Configuration Register - MSP_TCF + */ +#define MSP_TCF_MASK_TP1ELEN ((u32)(0x7UL << 0)) +#define MSP_TCF_MASK_TP1FLEN ((u32)(0x7FUL << 3)) +#define MSP_TCF_MASK_TDTYP ((u32)(0x3UL << 10)) +#define MSP_TCF_MASK_TENDN ((u32)(0x1UL << 12)) +#define MSP_TCF_MASK_TDDLY ((u32)(0x3UL << 13)) +#define MSP_TCF_MASK_TFSIG ((u32)(0x1UL << 15)) +#define MSP_TCF_MASK_TP2ELEN ((u32)(0x7UL << 16)) +#define MSP_TCF_MASK_TP2FLEN ((u32)(0x7FUL << 19)) +#define MSP_TCF_MASK_TP2SM ((u32)(0x1UL << 26)) +#define MSP_TCF_MASK_TP2EN ((u32)(0x1UL << 27)) +#define MSP_TCF_MASK_TBSWAP ((u32)(0x3UL << 28)) + +/** + * MSP Receive Configuration Register - MSP_RCF + */ +#define MSP_RCF_MASK_RP1ELEN ((u32)(0x7UL << 0)) +#define MSP_RCF_MASK_RP1FLEN ((u32)(0x7FUL << 3)) +#define MSP_RCF_MASK_RDTYP ((u32)(0x3UL << 10)) +#define MSP_RCF_MASK_RENDN ((u32)(0x1UL << 12)) +#define MSP_RCF_MASK_RDDLY ((u32)(0x3UL << 13)) +#define MSP_RCF_MASK_RFSIG ((u32)(0x1UL << 15)) +#define MSP_RCF_MASK_RP2ELEN ((u32)(0x7UL << 16)) +#define MSP_RCF_MASK_RP2FLEN ((u32)(0x7FUL << 19)) +#define MSP_RCF_MASK_RP2SM ((u32)(0x1UL << 26)) +#define MSP_RCF_MASK_RP2EN ((u32)(0x1UL << 27)) +#define MSP_RCF_MASK_RBSWAP ((u32)(0x3UL << 28)) + +/** + * MSP Sample Rate Generator Register - MSP_SRG + */ +#define MSP_SRG_MASK_SCKDIV ((u32)(0x3FFUL << 0)) +#define MSP_SRG_MASK_FRWID ((u32)(0x3FUL << 10)) +#define MSP_SRG_MASK_FRPER ((u32)(0x1FFFUL << 16)) + +/** + * MSP Flag Register - MSP_FLR + */ +#define MSP_FLR_MASK_RBUSY ((u32)(0x1UL << 0)) +#define MSP_FLR_MASK_RFE ((u32)(0x1UL << 1)) +#define MSP_FLR_MASK_RFU ((u32)(0x1UL << 2)) +#define MSP_FLR_MASK_TBUSY ((u32)(0x1UL << 3)) +#define MSP_FLR_MASK_TFE ((u32)(0x1UL << 4)) +#define MSP_FLR_MASK_TFU ((u32)(0x1UL << 5)) + +/** + * MSP DMA Control Register - MSP_DMACR + */ +#define MSP_DMACR_MASK_RDMAE ((u32)(0x1UL << 0)) +#define MSP_DMACR_MASK_TDMAE ((u32)(0x1UL << 1)) + +/** + * MSP Interrupt Mask Set/Clear Register - MSP_IMSC + */ +#define MSP_IMSC_MASK_RXIM ((u32)(0x1UL << 0)) +#define MSP_IMSC_MASK_ROEIM ((u32)(0x1UL << 1)) +#define MSP_IMSC_MASK_RSEIM ((u32)(0x1UL << 2)) +#define MSP_IMSC_MASK_RFSIM ((u32)(0x1UL << 3)) +#define MSP_IMSC_MASK_TXIM ((u32)(0x1UL << 4)) +#define MSP_IMSC_MASK_TUEIM ((u32)(0x1UL << 5)) +#define MSP_IMSC_MASK_TSEIM ((u32)(0x1UL << 6)) +#define MSP_IMSC_MASK_TFSIM ((u32)(0x1UL << 7)) +#define MSP_IMSC_MASK_RFOIM ((u32)(0x1UL << 8)) +#define MSP_IMSC_MASK_TFOIM ((u32)(0x1UL << 9)) + +/** + * MSP Raw Interrupt status Register - MSP_RIS + */ +#define MSP_RIS_MASK_RXRIS ((u32)(0x1UL << 0)) +#define MSP_RIS_MASK_ROERIS ((u32)(0x1UL << 1)) +#define MSP_RIS_MASK_RSERIS ((u32)(0x1UL << 2)) +#define MSP_RIS_MASK_RFSRIS ((u32)(0x1UL << 3)) +#define MSP_RIS_MASK_TXRIS ((u32)(0x1UL << 4)) +#define MSP_RIS_MASK_TUERIS ((u32)(0x1UL << 5)) +#define MSP_RIS_MASK_TSERIS ((u32)(0x1UL << 6)) +#define MSP_RIS_MASK_TFSRIS ((u32)(0x1UL << 7)) +#define MSP_RIS_MASK_RFORIS ((u32)(0x1UL << 8)) +#define MSP_RIS_MASK_TFORIS ((u32)(0x1UL << 9)) + +/** + * MSP Masked Interrupt status Register - MSP_MIS + */ +#define MSP_MIS_MASK_RXMIS ((u32)(0x1UL << 0)) +#define MSP_MIS_MASK_ROEMIS ((u32)(0x1UL << 1)) +#define MSP_MIS_MASK_RSEMIS ((u32)(0x1UL << 2)) +#define MSP_MIS_MASK_RFSMIS ((u32)(0x1UL << 3)) +#define MSP_MIS_MASK_TXMIS ((u32)(0x1UL << 4)) +#define MSP_MIS_MASK_TUEMIS ((u32)(0x1UL << 5)) +#define MSP_MIS_MASK_TSEMIS ((u32)(0x1UL << 6)) +#define MSP_MIS_MASK_TFSMIS ((u32)(0x1UL << 7)) +#define MSP_MIS_MASK_RFOMIS ((u32)(0x1UL << 8)) +#define MSP_MIS_MASK_TFOMIS ((u32)(0x1UL << 9)) + +/** + * MSP Interrupt Clear Register - MSP_ICR + */ +#define MSP_ICR_MASK_ROEIC ((u32)(0x1UL << 1)) +#define MSP_ICR_MASK_RSEIC ((u32)(0x1UL << 2)) +#define MSP_ICR_MASK_RFSIC ((u32)(0x1UL << 3)) +#define MSP_ICR_MASK_TUEIC ((u32)(0x1UL << 5)) +#define MSP_ICR_MASK_TSEIC ((u32)(0x1UL << 6)) +#define MSP_ICR_MASK_TFSIC ((u32)(0x1UL << 7)) + +#define GEN_MASK_BITS(val, mask, sb) ((u32)((((u32)val) << (sb)) & (mask))) +#define MSP_WBITS(reg, val, mask, sb) ((reg) = (((reg) & ~(mask)) |\ + (((val) << (sb)) & (mask)))) +#define DEFAULT_MSP_REG_DMACR 0x00000000 +#define DEFAULT_MSP_REG_SRG 0x1FFF0000 + +#define DEFAULT_MSP_REG_GCR ( \ + GEN_MASK_BITS(MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0) |\ + GEN_MASK_BITS(MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1) |\ + GEN_MASK_BITS(MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7) |\ + GEN_MASK_BITS(MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8) |\ + GEN_MASK_BITS(MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9) |\ + GEN_MASK_BITS(MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10)|\ + GEN_MASK_BITS(MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11) |\ + GEN_MASK_BITS(MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13) |\ + GEN_MASK_BITS(MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14) |\ + GEN_MASK_BITS(MSP_TRANSMIT_DATA_WITHOUT_DELAY, MSP_GCR_MASK_TXDDL, 15)|\ + GEN_MASK_BITS(MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16)|\ + GEN_MASK_BITS(MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18) |\ + GEN_MASK_BITS(MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20) |\ + GEN_MASK_BITS(MSP_SPI_PHASE_ZERO_CYCLE_DELAY, MSP_GCR_MASK_SPICKM, 21)|\ + GEN_MASK_BITS(SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23)\ + ) +#define DEFAULT_MSP_REG_RCF ( \ + GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_RCF_MASK_RP1ELEN, 0) | \ + GEN_MASK_BITS(MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15) |\ + GEN_MASK_BITS(MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13) | \ + GEN_MASK_BITS(MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12) \ + ) + +#define DEFAULT_MSP_REG_TCF ( \ + GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_TCF_MASK_TP1ELEN, 0) | \ + GEN_MASK_BITS(MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15) |\ + GEN_MASK_BITS(MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13) | \ + GEN_MASK_BITS(MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12) \ + ) + +/** + * MSP Receiver/Transmitter states (enabled or disabled) + */ +#define MSP_RECEIVER_DISABLED 0 +#define MSP_RECEIVER_ENABLED 1 +#define MSP_TRANSMITTER_DISABLED 0 +#define MSP_TRANSMITTER_ENABLED 1 + +/** + * MSP Receiver/Transmitter FIFO constants + */ +#define MSP_LOOPBACK_DISABLED 0 +#define MSP_LOOPBACK_ENABLED 1 + +#define MSP_TX_FIFO_DISABLED 0 +#define MSP_TX_FIFO_ENABLED 1 +#define MSP_TX_ENDIANESS_MSB 0 +#define MSP_TX_ENDIANESS_LSB 1 + +#define MSP_RX_FIFO_DISABLED 0 +#define MSP_RX_FIFO_ENABLED 1 +#define MSP_RX_ENDIANESS_MSB 0 +#define MSP_RX_ENDIANESS_LSB 1 + +#define MSP_TX_FRAME_SYNC_EXT 0x0 +#define MSP_TX_FRAME_SYNC_INT 0x2 +#define MSP_TX_FRAME_SYNC_INT_CFG 0x3 + +#define MSP_TX_FRAME_SYNC_POL_HIGH 0 +#define MSP_TX_FRAME_SYNC_POL_LOW 1 + +#define MSP_HANDLE_RX_FRAME_SYNC_PULSE 0 +#define MSP_IGNORE_RX_FRAME_SYNC_PULSE 1 + +#define MSP_RX_NO_DATA_DELAY 0x0 +#define MSP_RX_1BIT_DATA_DELAY 0x1 +#define MSP_RX_2BIT_DATA_DELAY 0x2 +#define MSP_RX_3BIT_DATA_DELAY 0x3 + +#define MSP_HANDLE_TX_FRAME_SYNC_PULSE 0 +#define MSP_IGNORE_TX_FRAME_SYNC_PULSE 1 + +#define MSP_TX_NO_DATA_DELAY 0x0 +#define MSP_TX_1BIT_DATA_DELAY 0x1 +#define MSP_TX_2BIT_DATA_DELAY 0x2 +#define MSP_TX_3BIT_DATA_DELAY 0x3 + +#define MSP_TX_CLOCK_POL_LOW 0 +#define MSP_TX_CLOCK_POL_HIGH 1 + +#define MSP_SPI_PHASE_ZERO_CYCLE_DELAY 0x2 +#define MSP_SPI_PHASE_HALF_CYCLE_DELAY 0x3 + +#define MSP_IS_SPI_SLAVE 0 +#define MSP_IS_SPI_MASTER 1 + +#define MSP_FRAME_GEN_DISABLE 0 +#define MSP_FRAME_GEN_ENABLE 1 + +#define MSP_SAMPLE_RATE_GEN_DISABLE 0 +#define MSP_SAMPLE_RATE_GEN_ENABLE 1 + +#define SPI_BURST_MODE_DISABLE 0 +#define SPI_BURST_MODE_ENABLE 1 + +#define MSP_TRANSMIT_DATA_WITHOUT_DELAY 0 +#define MSP_TRANSMIT_DATA_WITH_DELAY 1 + +#define MSP_CLOCK_INTERNAL 0x0 /* 48 MHz */ + +/* SRG is derived from MSPSCK pin but is resynchronized on MSPRFS + * (Receive Frame Sync signal) */ +#define MSP_CLOCK_EXTERNAL 0x2 +#define MSP_CLOCK_EXTERNAL_RESYNC 0x3 + +#define DISABLE_ALL_MSP_INTERRUPTS (0x0) +#define ENABLE_ALL_MSP_INTERRUPTS (0x333) +#define CLEAR_ALL_MSP_INTERRUPTS (0xEE) +#define DEFAULT_MSP_CLK (48000000) +#define MAX_SCKDIV (1023) + +#define MSP_FIFO_DEPTH 8 + +/** + * Queue State + */ +#define QUEUE_RUNNING (0) +#define QUEUE_STOPPED (1) + +#define START_STATE ((void *)0) +#define RUNNING_STATE ((void *)1) +#define DONE_STATE ((void *)2) +#define ERROR_STATE ((void *)-1) + +/* Default values */ +#define SPI_DEFAULT_MAX_SPEED_HZ 48000 +#define SPI_TRANSFER_TIMEOUT_MS 5000 + +/* CONTROLLER COMMANDS */ +enum cntlr_commands { + DISABLE_CONTROLLER = 0, + ENABLE_CONTROLLER , + DISABLE_ALL_INTERRUPT , + ENABLE_ALL_INTERRUPT , + FLUSH_FIFO , + RESTORE_STATE , + LOAD_DEFAULT_CONFIG , + CLEAR_ALL_INTERRUPT, +}; + +struct stm_msp { + struct amba_device *adev; + struct spi_master *master; + struct stm_msp_controller *master_info; + void __iomem *regs; + struct clk *clk; +#ifdef CONFIG_SPI_WORKQUEUE + struct workqueue_struct *workqueue; +#endif + struct work_struct spi_work; + spinlock_t lock; + struct list_head queue; + int busy; + int run; + struct tasklet_struct pump_transfers; + struct timer_list spi_notify_timer; + int spi_io_error; + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + void (*write)(struct stm_msp *stm_msp); + void (*read)(struct stm_msp *stm_msp); + void (*delay)(struct stm_msp *stm_msp); +}; + +/** + * struct chip_data - To maintain runtime state of SPICntlr for each client chip + * @ctr_regs: void pointer which is assigned a struct having regs of the cntlr. + * @chip_id: Chip Id assigned to this client to identify it. + * @n_bytes: how many bytes(power of 2) reqd for a given data width of client + * @write: function to be used to write when doing xfer for this chip + * @null_write: function to be used for dummy write for receiving data. + * @read: function to be used to read when doing xfer for this chip + * @null_read: function to be used to for dummy read while writting data. + * @cs_control: chip select callback provided by chip + * @xfer_type: polling/interrupt + * + * Runtime state of the SPI controller, maintained per chip, + * This would be set according to the current message that would be served + */ +struct chip_data { + void *ctr_regs; + u32 chip_id; + u8 n_bytes; + void (*write) (struct stm_msp *stm_msp); + void (*null_write) (struct stm_msp *stm_msp); + void (*read) (struct stm_msp *stm_msp); + void (*null_read) (struct stm_msp *stm_msp); + void (*delay) (struct stm_msp *stm_msp); + void (*cs_control) (u32 command); + int xfer_type; +}; + +/** + * struct msp_regs - Used to store MSP controller registry values + * used by the driver. + * @gcr: global configuration register + * @tcf: transmit configuration register + * @rcf: receive configuration register + * @srg: sample rate generator register + * @dmacr: DMA configuration register + */ +struct msp_regs { + u32 gcr; + u32 tcf; + u32 rcf; + u32 srg; + u32 dmacr; +}; + +/** + * stm_msp_controller_cmd - To execute controller commands for MSP + * @stm_msp: SPI driver private data structure + * @cmd: Command which is to be executed on the controller + */ +static int stm_msp_controller_cmd(struct stm_msp *stm_msp, int cmd) +{ + int retval = 0; + struct msp_regs *msp_regs = NULL; + + switch (cmd) { + case DISABLE_CONTROLLER: { + dev_dbg(&stm_msp->adev->dev, + "Disabling MSP controller...\n"); + writel((readl(MSP_GCR(stm_msp->regs)) & + (~(MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN))), + MSP_GCR(stm_msp->regs)); + break; + } + case ENABLE_CONTROLLER: { + dev_dbg(&stm_msp->adev->dev, + "Enabling MSP controller...\n"); + writel((readl(MSP_GCR(stm_msp->regs)) | + (MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN)), + MSP_GCR(stm_msp->regs)); + break; + } + case DISABLE_ALL_INTERRUPT: { + dev_dbg(&stm_msp->adev->dev, + "Disabling all MSP interrupts...\n"); + writel(DISABLE_ALL_MSP_INTERRUPTS, + MSP_IMSC(stm_msp->regs)); + break; + } + case ENABLE_ALL_INTERRUPT: { + dev_dbg(&stm_msp->adev->dev, + "Enabling all MSP interrupts...\n"); + writel(ENABLE_ALL_MSP_INTERRUPTS, + MSP_IMSC(stm_msp->regs)); + break; + } + case CLEAR_ALL_INTERRUPT: { + dev_dbg(&stm_msp->adev->dev, + "Clearing all MSP interrupts...\n"); + writel(CLEAR_ALL_MSP_INTERRUPTS, + MSP_ICR(stm_msp->regs)); + break; + } + case FLUSH_FIFO: { + unsigned long limit = loops_per_jiffy << 1; + + dev_dbg(&stm_msp->adev->dev, "MSP FIFO flushed\n"); + + do { + while (!(readl(MSP_FLR(stm_msp->regs)) & + MSP_FLR_MASK_RFE)) { + readl(MSP_DR(stm_msp->regs)); + } + } while ((readl(MSP_FLR(stm_msp->regs)) & + (MSP_FLR_MASK_TBUSY | MSP_FLR_MASK_RBUSY)) && + limit--); + + retval = limit; + break; + } + case RESTORE_STATE: { + msp_regs = + (struct msp_regs *)stm_msp->cur_chip->ctr_regs; + + dev_dbg(&stm_msp->adev->dev, + "Restoring MSP state...\n"); + + writel(msp_regs->gcr, MSP_GCR(stm_msp->regs)); + writel(msp_regs->tcf, MSP_TCF(stm_msp->regs)); + writel(msp_regs->rcf, MSP_RCF(stm_msp->regs)); + writel(msp_regs->srg, MSP_SRG(stm_msp->regs)); + writel(msp_regs->dmacr, MSP_DMACR(stm_msp->regs)); + writel(DISABLE_ALL_MSP_INTERRUPTS, + MSP_IMSC(stm_msp->regs)); + writel(CLEAR_ALL_MSP_INTERRUPTS, + MSP_ICR(stm_msp->regs)); + break; + } + case LOAD_DEFAULT_CONFIG: { + dev_dbg(&stm_msp->adev->dev, + "Loading default MSP config...\n"); + + writel(DEFAULT_MSP_REG_GCR, MSP_GCR(stm_msp->regs)); + writel(DEFAULT_MSP_REG_TCF, MSP_TCF(stm_msp->regs)); + writel(DEFAULT_MSP_REG_RCF, MSP_RCF(stm_msp->regs)); + writel(DEFAULT_MSP_REG_SRG, MSP_SRG(stm_msp->regs)); + writel(DEFAULT_MSP_REG_DMACR, MSP_DMACR(stm_msp->regs)); + writel(DISABLE_ALL_MSP_INTERRUPTS, + MSP_IMSC(stm_msp->regs)); + writel(CLEAR_ALL_MSP_INTERRUPTS, + MSP_ICR(stm_msp->regs)); + break; + } + default: + dev_dbg(&stm_msp->adev->dev, "Unknown command\n"); + retval = -1; + break; + } + + return retval; +} + +/** + * giveback - current spi_message is over, schedule next spi_message + * @message: current SPI message + * @stm_msp: spi driver private data structure + * + * current spi_message is over, schedule next spi_message and call + * callback of this msg. + */ +static void giveback(struct spi_message *message, struct stm_msp *stm_msp) +{ + struct spi_transfer *last_transfer; + unsigned long flags; + struct spi_message *msg; + void (*curr_cs_control)(u32 command); + + spin_lock_irqsave(&stm_msp->lock, flags); + msg = stm_msp->cur_msg; + + curr_cs_control = stm_msp->cur_chip->cs_control; + + stm_msp->cur_msg = NULL; + stm_msp->cur_transfer = NULL; + stm_msp->cur_chip = NULL; +#ifdef CONFIG_SPI_WORKQUEUE + queue_work(stm_msp->workqueue, &stm_msp->spi_work); +#else + schedule_work(&stm_msp->spi_work); +#endif + spin_unlock_irqrestore(&stm_msp->lock, flags); + + last_transfer = list_entry(msg->transfers.prev, + struct spi_transfer, transfer_list); + + if (!last_transfer->cs_change) + curr_cs_control(SPI_CHIP_DESELECT); + + msg->state = NULL; + + if (msg->complete) + msg->complete(msg->context); + + stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER); + clk_disable(stm_msp->clk); +} + +/** + * spi_notify - Handles Polling hang issue over spi bus. + * @data: main driver data + * Context: Process. + * + * This is used to handle error condition in transfer and receive function used + * in polling mode. + * Sometimes due to passing wrong protocol desc , polling transfer may hang. + * To prevent this, timer is added. + * + * Returns void. + */ +static void spi_notify(unsigned long data) +{ + struct stm_msp *stm_msp = (struct stm_msp *)data; + stm_msp->spi_io_error = 1; + + dev_err(&stm_msp->adev->dev, + "Polling is taking time, maybe device not responding\n"); + + del_timer(&stm_msp->spi_notify_timer); +} + +/** + * stm_msp_transfer - transfer function registered to SPI master framework + * @spi: spi device which is requesting transfer + * @msg: spi message which is to handled is queued to driver queue + * + * This function is registered to the SPI framework for this SPI master + * controller. It will queue the spi_message in the queue of driver if + * the queue is not stopped and return. + */ +static int stm_msp_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct stm_msp *stm_msp = spi_master_get_devdata(spi->master); + unsigned long flags; + + spin_lock_irqsave(&stm_msp->lock, flags); + + if (stm_msp->run == QUEUE_STOPPED) { + spin_unlock_irqrestore(&stm_msp->lock, flags); + return -ESHUTDOWN; + } + dev_err(&spi->dev, "Regular request (No infinite DMA ongoing)\n"); + + msg->actual_length = 0; + msg->status = -EINPROGRESS; + msg->state = START_STATE; + + list_add_tail(&msg->queue, &stm_msp->queue); + + if ((stm_msp->run == QUEUE_RUNNING) && (!stm_msp->busy)) +#ifdef CONFIG_SPI_WORKQUEUE + queue_work(stm_msp->workqueue, &stm_msp->spi_work); +#else + schedule_work(&stm_msp->spi_work); +#endif + spin_unlock_irqrestore(&stm_msp->lock, flags); + return 0; +} + +/** + * next_transfer - Move to the Next transfer in the current spi message + * @stm_msp: spi driver private data structure + * + * This function moves though the linked list of spi transfers in the + * current spi message and returns with the state of current spi + * message i.e whether its last transfer is done(DONE_STATE) or + * Next transfer is ready(RUNNING_STATE) + */ +static void *next_transfer(struct stm_msp *stm_msp) +{ + struct spi_message *msg = stm_msp->cur_msg; + struct spi_transfer *trans = stm_msp->cur_transfer; + + /* Move to next transfer */ + if (trans->transfer_list.next != &msg->transfers) { + stm_msp->cur_transfer = list_entry(trans->transfer_list.next, + struct spi_transfer, + transfer_list); + return RUNNING_STATE; + } + return DONE_STATE; +} + +static void do_interrupt_transfer(void *data) +{ + struct stm_msp *stm_msp = (struct stm_msp *)data; + + stm_msp->tx = (void *)stm_msp->cur_transfer->tx_buf; + stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len; + + stm_msp->rx = (void *)stm_msp->cur_transfer->rx_buf; + stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len; + + stm_msp->write = stm_msp->tx ? + stm_msp->cur_chip->write : stm_msp->cur_chip->null_write; + stm_msp->read = stm_msp->rx ? + stm_msp->cur_chip->read : stm_msp->cur_chip->null_read; + + stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT); + + stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT); + stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER); +} + +static void do_polling_transfer(void *data) +{ + struct stm_msp *stm_msp = (struct stm_msp *)data; + struct spi_message *message = NULL; + struct spi_transfer *transfer = NULL; + struct spi_transfer *previous = NULL; + struct chip_data *chip; + unsigned long limit = 0; + u32 timer_expire = 0; + + chip = stm_msp->cur_chip; + message = stm_msp->cur_msg; + + while (message->state != DONE_STATE) { + /* Handle for abort */ + if (message->state == ERROR_STATE) + break; + + transfer = stm_msp->cur_transfer; + + /* 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); + + if (previous->cs_change) + stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT); + } else { + /* START_STATE */ + message->state = RUNNING_STATE; + stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT); + } + + /* Configuration Changing Per Transfer */ + stm_msp->tx = (void *)transfer->tx_buf; + stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len; + stm_msp->rx = (void *)transfer->rx_buf; + stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len; + + stm_msp->write = stm_msp->tx ? + stm_msp->cur_chip->write : + stm_msp->cur_chip->null_write; + stm_msp->read = stm_msp->rx ? + stm_msp->cur_chip->read : + stm_msp->cur_chip->null_read; + stm_msp->delay = stm_msp->cur_chip->delay; + + stm_msp_controller_cmd(stm_msp, FLUSH_FIFO); + stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER); + + timer_expire = stm_msp->cur_transfer->len / 1024; + + if (!timer_expire) + timer_expire = SPI_TRANSFER_TIMEOUT_MS; + else + timer_expire = + (stm_msp->cur_transfer->len / 1024) * + SPI_TRANSFER_TIMEOUT_MS; + + stm_msp->spi_notify_timer.expires = + jiffies + msecs_to_jiffies(timer_expire); + + add_timer(&stm_msp->spi_notify_timer); + + dev_dbg(&stm_msp->adev->dev, "Polling transfer ongoing...\n"); + + while (stm_msp->tx < stm_msp->tx_end) { + + stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER); + stm_msp->read(stm_msp); + stm_msp->write(stm_msp); + + stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER); + + if (stm_msp->delay) + stm_msp->delay(stm_msp); + + if (stm_msp->spi_io_error == 1) + break; + } + + del_timer(&stm_msp->spi_notify_timer); + + if (stm_msp->spi_io_error == 1) + goto out; + + limit = loops_per_jiffy << 1; + + while ((stm_msp->rx < stm_msp->rx_end) && (limit--)) + stm_msp->read(stm_msp); + + /* Update total byte transfered */ + message->actual_length += stm_msp->cur_transfer->len; + + if (stm_msp->cur_transfer->cs_change) + stm_msp->cur_chip->cs_control(SPI_CHIP_DESELECT); + + stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER); + + /* Move to next transfer */ + message->state = next_transfer(stm_msp); + } +out: + /* Handle end of message */ + if (message->state == DONE_STATE) + message->status = 0; + else + message->status = -EIO; + + giveback(message, stm_msp); + + stm_msp->spi_io_error = 0; /* Reset state for further transfers */ + + return; +} + +/** + * pump_messages - Workqueue function which processes spi message queue + * @work: pointer to work + * + * This function checks if there is any spi message in the queue that + * needs processing and delegate control to appropriate function + * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer() + * based on the kind of the transfer + * + */ +static void pump_messages(struct work_struct *work) +{ + struct stm_msp *stm_msp = container_of(work, struct stm_msp, spi_work); + unsigned long flags; + + /* Lock queue and check for queue work */ + spin_lock_irqsave(&stm_msp->lock, flags); + + if (list_empty(&stm_msp->queue) || stm_msp->run == QUEUE_STOPPED) { + dev_dbg(&stm_msp->adev->dev, "work_queue: Queue Empty\n"); + stm_msp->busy = 0; + spin_unlock_irqrestore(&stm_msp->lock, flags); + return; + } + /* Make sure we are not already running a message */ + if (stm_msp->cur_msg) { + spin_unlock_irqrestore(&stm_msp->lock, flags); + return; + } + + clk_enable(stm_msp->clk); + + /* Extract head of queue */ + stm_msp->cur_msg = list_entry(stm_msp->queue.next, + struct spi_message, + queue); + + list_del_init(&stm_msp->cur_msg->queue); + stm_msp->busy = 1; + spin_unlock_irqrestore(&stm_msp->lock, flags); + + /* Initial message state */ + stm_msp->cur_msg->state = START_STATE; + stm_msp->cur_transfer = list_entry(stm_msp->cur_msg->transfers.next, + struct spi_transfer, + transfer_list); + + /* Setup the SPI using the per chip configuration */ + stm_msp->cur_chip = spi_get_ctldata(stm_msp->cur_msg->spi); + stm_msp_controller_cmd(stm_msp, RESTORE_STATE); + stm_msp_controller_cmd(stm_msp, FLUSH_FIFO); + + if (stm_msp->cur_chip->xfer_type == SPI_POLLING_TRANSFER) + do_polling_transfer(stm_msp); + else if (stm_msp->cur_chip->xfer_type == SPI_INTERRUPT_TRANSFER) + do_interrupt_transfer(stm_msp); +} + +/** + * pump_transfers - Tasklet function which schedules next interrupt xfer + * @data: spi driver private data structure + */ +static void pump_transfers(unsigned long data) +{ + struct stm_msp *stm_msp = (struct stm_msp *)data; + struct spi_message *message = NULL; + struct spi_transfer *transfer = NULL; + struct spi_transfer *previous = NULL; + + message = stm_msp->cur_msg; + + /* Handle for abort */ + if (message->state == ERROR_STATE) { + message->status = -EIO; + giveback(message, stm_msp); + return; + } + + /* Handle end of message */ + if (message->state == DONE_STATE) { + message->status = 0; + giveback(message, stm_msp); + return; + } + transfer = stm_msp->cur_transfer; + + /* 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); + if (previous->cs_change) + stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT); + } else { + /* START_STATE */ + message->state = RUNNING_STATE; + } + stm_msp->tx = (void *)transfer->tx_buf; + stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len; + stm_msp->rx = (void *)transfer->rx_buf; + stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len; + + stm_msp->write = stm_msp->tx ? + stm_msp->cur_chip->write : stm_msp->cur_chip->null_write; + stm_msp->read = stm_msp->rx ? + stm_msp->cur_chip->read : stm_msp->cur_chip->null_read; + + stm_msp_controller_cmd(stm_msp, FLUSH_FIFO); + stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT); +} + +static int init_queue(struct stm_msp *stm_msp) +{ + INIT_LIST_HEAD(&stm_msp->queue); + spin_lock_init(&stm_msp->lock); + + stm_msp->run = QUEUE_STOPPED; + stm_msp->busy = 0; + + tasklet_init(&stm_msp->pump_transfers, pump_transfers, + (unsigned long)stm_msp); + INIT_WORK(&stm_msp->spi_work, pump_messages); + +#ifdef CONFIG_SPI_WORKQUEUE + stm_msp->workqueue = create_singlethread_workqueue( + dev_name(&stm_msp->master->dev)); + + if (stm_msp->workqueue == NULL) + return -EBUSY; +#endif /* CONFIG_SPI_WORKQUEUE */ + + init_timer(&stm_msp->spi_notify_timer); + + stm_msp->spi_notify_timer.expires = jiffies + msecs_to_jiffies(1000); + stm_msp->spi_notify_timer.function = spi_notify; + stm_msp->spi_notify_timer.data = (unsigned long)stm_msp; + + return 0; +} + +static int start_queue(struct stm_msp *stm_msp) +{ + unsigned long flags; + + spin_lock_irqsave(&stm_msp->lock, flags); + + if (stm_msp->run == QUEUE_RUNNING || stm_msp->busy) { + spin_unlock_irqrestore(&stm_msp->lock, flags); + return -EBUSY; + } + + stm_msp->run = QUEUE_RUNNING; + stm_msp->cur_msg = NULL; + stm_msp->cur_transfer = NULL; + stm_msp->cur_chip = NULL; + spin_unlock_irqrestore(&stm_msp->lock, flags); + return 0; +} + +static int stop_queue(struct stm_msp *stm_msp) +{ + unsigned long flags; + unsigned limit = 500; + int status = 0; + + spin_lock_irqsave(&stm_msp->lock, flags); + + /* This is a bit lame, but is optimized for the common execution path. + * A wait_queue on the stm_msp->busy could be used, but then the common + * execution path (pump_messages) would be required to call wake_up or + * friends on every SPI message. Do this instead */ + + stm_msp->run = QUEUE_STOPPED; + + while (!list_empty(&stm_msp->queue) && stm_msp->busy && limit--) { + spin_unlock_irqrestore(&stm_msp->lock, flags); + msleep(10); + spin_lock_irqsave(&stm_msp->lock, flags); + } + + if (!list_empty(&stm_msp->queue) || stm_msp->busy) + status = -EBUSY; + + spin_unlock_irqrestore(&stm_msp->lock, flags); + + return status; +} + +static int destroy_queue(struct stm_msp *stm_msp) +{ + int status; + + status = stop_queue(stm_msp); + + if (status != 0) + return status; +#ifdef CONFIG_SPI_WORKQUEUE + destroy_workqueue(stm_msp->workqueue); +#endif + del_timer_sync(&stm_msp->spi_notify_timer); + + return 0; +} + +/** + * stm_msp_null_writer - To Write Dummy Data in Data register + * @stm_msp: spi driver private data structure + * + * This function is set as a write function for transfer which have + * Tx transfer buffer as NULL. It simply writes '0' in the Data + * register + */ +static void stm_msp_null_writer(struct stm_msp *stm_msp) +{ + u32 cur_write = 0; + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_TFU) || + (stm_msp->tx >= stm_msp->tx_end)) + return; + + writel(0x0, MSP_DR(stm_msp->regs)); + stm_msp->tx += (stm_msp->cur_chip->n_bytes); + cur_write++; + + if (cur_write == 8) + return; + } +} + +/** + * stm_msp_null_reader - To read data from Data register and discard it + * @stm_msp: spi driver private data structure + * + * This function is set as a reader function for transfer which have + * Rx Transfer buffer as null. Read Data is rejected + */ +static void stm_msp_null_reader(struct stm_msp *stm_msp) +{ + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_RFE) || + (stm_msp->rx >= stm_msp->rx_end)) + return; + + readl(MSP_DR(stm_msp->regs)); + stm_msp->rx += (stm_msp->cur_chip->n_bytes); + } +} + +/** + * stm_msp_u8_writer - Write FIFO data in Data register as a 8 Bit Data + * @stm_msp: spi driver private data structure + * + * This function writes data in Tx FIFO till it is not full + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary write ptr tx in stm_msp which maintains + * current write position in transfer buffer. we do not write data more than + * FIFO depth + */ +void stm_msp_u8_writer(struct stm_msp *stm_msp) +{ + u32 cur_write = 0; + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_TFU) || + (stm_msp->tx >= stm_msp->tx_end)) + return; + + writel((u32)(*(u8 *)(stm_msp->tx)), MSP_DR(stm_msp->regs)); + stm_msp->tx += (stm_msp->cur_chip->n_bytes); + cur_write++; + + if (cur_write == MSP_FIFO_DEPTH) + return; + } +} + +/** + * stm_msp_u8_reader - Read FIFO data in Data register as a 8 Bit Data + * @stm_msp: spi driver private data structure + * + * This function reads data in Rx FIFO till it is not empty + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary Read ptr rx in stm_msp which maintains + * current read position in transfer buffer + */ +void stm_msp_u8_reader(struct stm_msp *stm_msp) +{ + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_RFE) || + (stm_msp->rx >= stm_msp->rx_end)) + return; + + *(u8 *)(stm_msp->rx) = (u8)readl(MSP_DR(stm_msp->regs)); + stm_msp->rx += (stm_msp->cur_chip->n_bytes); + } +} + +/** + * stm_msp_u16_writer - Write FIFO data in Data register as a 16 Bit Data + * @stm_msp: spi driver private data structure + * + * This function writes data in Tx FIFO till it is not full + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary write ptr tx in stm_msp which maintains + * current write position in transfer buffer. we do not write data more than + * FIFO depth + */ +void stm_msp_u16_writer(struct stm_msp *stm_msp) +{ + u32 cur_write = 0; + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_TFU) || + (stm_msp->tx >= stm_msp->tx_end)) + return; + + writel((u32)(*(u16 *)(stm_msp->tx)), MSP_DR(stm_msp->regs)); + stm_msp->tx += (stm_msp->cur_chip->n_bytes); + cur_write++; + + if (cur_write == MSP_FIFO_DEPTH) + return; + } +} + +/** + * stm_msp_u16_reader - Read FIFO data in Data register as a 16 Bit Data + * @stm_msp: spi driver private data structure + * + * This function reads data in Rx FIFO till it is not empty + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary Read ptr rx in stm_msp which maintains + * current read position in transfer buffer + */ +void stm_msp_u16_reader(struct stm_msp *stm_msp) +{ + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_RFE) || + (stm_msp->rx >= stm_msp->rx_end)) + return; + + *(u16 *)(stm_msp->rx) = (u16)readl(MSP_DR(stm_msp->regs)); + stm_msp->rx += (stm_msp->cur_chip->n_bytes); + } +} + +/** + * stm_msp_u32_writer - Write FIFO data in Data register as a 32 Bit Data + * @stm_msp: spi driver private data structure + * + * This function writes data in Tx FIFO till it is not full + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary write ptr tx in stm_msp which maintains + * current write position in transfer buffer. we do not write data more than + * FIFO depth + */ +void stm_msp_u32_writer(struct stm_msp *stm_msp) +{ + u32 cur_write = 0; + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_TFU) || + (stm_msp->tx >= stm_msp->tx_end)) + return; + + /* Write Data to Data Register */ + writel(*(u32 *)(stm_msp->tx), MSP_DR(stm_msp->regs)); + stm_msp->tx += (stm_msp->cur_chip->n_bytes); + cur_write++; + + if (cur_write == MSP_FIFO_DEPTH) + return; + } +} + +/** + * stm_msp_u32_reader - Read FIFO data in Data register as a 32 Bit Data + * @stm_msp: spi driver private data structure + * + * This function reads data in Rx FIFO till it is not empty + * which is indicated by the status register or our transfer is complete. + * It also updates the temporary Read ptr rx in stm_msp which maintains + * current read position in transfer buffer + */ +void stm_msp_u32_reader(struct stm_msp *stm_msp) +{ + u32 status; + + while (1) { + status = readl(MSP_FLR(stm_msp->regs)); + + if ((status & MSP_FLR_MASK_RFE) || + (stm_msp->rx >= stm_msp->rx_end)) + return; + + *(u32 *)(stm_msp->rx) = readl(MSP_DR(stm_msp->regs)); + stm_msp->rx += (stm_msp->cur_chip->n_bytes); + } +} + +/** + * stm_msp_interrupt_handler - Interrupt hanlder function + */ +static irqreturn_t stm_msp_interrupt_handler(int irq, void *dev_id) +{ + struct stm_msp *stm_msp = (struct stm_msp *)dev_id; + struct spi_message *msg = stm_msp->cur_msg; + u32 irq_status = 0; + u32 flag = 0; + + if (!msg) { + dev_err(&stm_msp->adev->dev, + "Bad message state in interrupt handler"); + /* Never fail */ + return IRQ_HANDLED; + } + + /* Read the Interrupt Status Register */ + irq_status = readl(MSP_MIS(stm_msp->regs)); + + if (irq_status) { + if (irq_status & MSP_MIS_MASK_ROEMIS) { /* Overrun interrupt */ + /* Bail out our Data has been corrupted */ + dev_dbg(&stm_msp->adev->dev, + "Received ROR interrupt\n"); + + stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT); + stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT); + stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER); + msg->state = ERROR_STATE; + tasklet_schedule(&stm_msp->pump_transfers); + return IRQ_HANDLED; + } + + stm_msp->read(stm_msp); + stm_msp->write(stm_msp); + + if ((stm_msp->tx == stm_msp->tx_end) && (flag == 0)) { + flag = 1; + /* Disable Transmit interrupt */ + writel(readl(MSP_IMSC(stm_msp->regs)) & + (~MSP_IMSC_MASK_TXIM) & (~MSP_IMSC_MASK_TFOIM), + (stm_msp->regs + 0x14)); + } + + /* Clearing any Xmit underrun error. Overrun already handled */ + stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT); + + if (stm_msp->rx == stm_msp->rx_end) { + stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT); + stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT); + + dev_dbg(&stm_msp->adev->dev, + "Interrupt transfer completed.\n"); + + /* Update total bytes transfered */ + msg->actual_length += stm_msp->cur_transfer->len; + + if (stm_msp->cur_transfer->cs_change) + stm_msp->cur_chip->cs_control( + SPI_CHIP_DESELECT); + + /* Move to next transfer */ + msg->state = next_transfer(stm_msp); + tasklet_schedule(&stm_msp->pump_transfers); + return IRQ_HANDLED; + } + } + return IRQ_HANDLED; +} + +/** + * stm_msp_cleanup - cleanup function registered to SPI master framework + * @spi: spi device which is requesting cleanup + * + * This function is registered to the SPI framework for this SPI master + * controller. It will free the runtime state of chip. + */ +static void stm_msp_cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + struct stm_msp *stm_msp = spi_master_get_devdata(spi->master); + struct spi_master *master; + master = stm_msp->master; + + if (chip) { + kfree(chip->ctr_regs); + kfree(chip); + spi_set_ctldata(spi, NULL); + } +} + +/** + * null_cs_control - Dummy chip select function + * @command: select/delect the chip + * + * If no chip select function is provided by client this is used as dummy + * chip select + */ +static void null_cs_control(u32 command) +{ + /* Nothing to do */ + (void)command; +} + +static int verify_msp_controller_parameters(struct stm_msp_config_chip + *chip_info) +{ + + /* FIXME: check clock params */ + if ((chip_info->lbm != SPI_LOOPBACK_ENABLED) && + (chip_info->lbm != SPI_LOOPBACK_DISABLED)) { + dev_dbg(chip_info->dev, + "Loopback Mode is configured incorrectly\n"); + return -1; + } + if ((chip_info->hierarchy != SPI_MASTER) && + (chip_info->hierarchy != SPI_SLAVE)) { + dev_dbg(chip_info->dev, + "hierarchy is configured incorrectly\n"); + return -1; + } + if ((chip_info->endian_rx != SPI_FIFO_MSB) && + (chip_info->endian_rx != SPI_FIFO_LSB)) { + dev_dbg(chip_info->dev, + "Rx FIFO endianess is configured incorrectly\n"); + return -1; + } + if ((chip_info->endian_tx != SPI_FIFO_MSB) && + (chip_info->endian_tx != SPI_FIFO_LSB)) { + dev_dbg(chip_info->dev, + "Tx FIFO endianess is configured incorrectly\n"); + return -1; + } + if ((chip_info->data_size < MSP_DATA_BITS_8) || + (chip_info->data_size > MSP_DATA_BITS_32)) { + dev_dbg(chip_info->dev, + "MSP DATA Size is configured incorrectly\n"); + return -1; + } + if ((chip_info->com_mode != SPI_INTERRUPT_TRANSFER) && + (chip_info->com_mode != SPI_POLLING_TRANSFER)) { + dev_dbg(chip_info->dev, + "Communication mode is configured incorrectly\n"); + return -1; + } + if (((chip_info->proto_params).clk_phase != + SPI_CLK_ZERO_CYCLE_DELAY) && + ((chip_info->proto_params).clk_phase != + SPI_CLK_HALF_CYCLE_DELAY)) { + dev_dbg(chip_info->dev, + "Clock Phase is configured incorrectly\n"); + return -1; + } + if (((chip_info->proto_params).clk_pol != + SPI_CLK_POL_IDLE_LOW) && + ((chip_info->proto_params).clk_pol != + SPI_CLK_POL_IDLE_HIGH)) { + dev_dbg(chip_info->dev, + "Clk Polarity configured incorrectly\n"); + return -1; + } + if (chip_info->cs_control == NULL) { + dev_dbg(chip_info->dev, + "Chip Select Function is NULL for this chip\n"); + chip_info->cs_control = null_cs_control; + } + return 0; +} + +static struct stm_msp_config_chip *allocate_default_msp_chip_cfg( + struct spi_device *spi) +{ + struct stm_msp_config_chip *chip_info; + + chip_info = kzalloc(sizeof(struct stm_msp_config_chip), GFP_KERNEL); + + if (!chip_info) { + dev_err(&spi->dev, "setup - cannot allocate controller data"); + return NULL; + } + dev_dbg(&spi->dev, "Allocated Memory for controller data\n"); + + chip_info->lbm = SPI_LOOPBACK_DISABLED; + chip_info->com_mode = SPI_POLLING_TRANSFER; + chip_info->hierarchy = SPI_MASTER; + chip_info->endian_tx = SPI_FIFO_LSB; + chip_info->endian_rx = SPI_FIFO_LSB; + chip_info->data_size = MSP_DATA_BITS_32; + + if (spi->max_speed_hz != 0) + chip_info->freq = spi->max_speed_hz; + else + chip_info->freq = SPI_DEFAULT_MAX_SPEED_HZ; + + chip_info->proto_params.clk_phase = SPI_CLK_HALF_CYCLE_DELAY; + chip_info->proto_params.clk_pol = SPI_CLK_POL_IDLE_LOW; + chip_info->cs_control = null_cs_control; + + return chip_info; +} + +static void stm_msp_delay(struct stm_msp *stm_msp) +{ + udelay(15); + + while (readl(MSP_FLR(stm_msp->regs)) & + (MSP_FLR_MASK_RBUSY | MSP_FLR_MASK_TBUSY)) + udelay(1); +} + +/** + * stm_msp_setup - setup function registered to SPI master framework + * @spi: spi device which is requesting setup + * + * This function is registered to the SPI framework for this SPI master + * controller. If it is the first time when setup is called by this device, + * this function will initialize the runtime state for this chip and save + * the same in the device structure. Else it will update the runtime info + * with the updated chip info. + */ +static int stm_msp_setup(struct spi_device *spi) +{ + struct stm_msp_config_chip *chip_info; + struct chip_data *curr_cfg; + struct spi_master *master; + int status = 0; + u16 sckdiv = 0; + s16 bus_num = 0; + struct stm_msp *stm_msp = spi_master_get_devdata(spi->master); + struct msp_regs *msp_regs; + master = stm_msp->master; + bus_num = master->bus_num - 1; + + /* Get controller data */ + chip_info = spi->controller_data; + /* Get controller_state */ + curr_cfg = spi_get_ctldata(spi); + + if (curr_cfg == NULL) { + curr_cfg = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + + if (!curr_cfg) { + dev_err(&stm_msp->adev->dev, + "setup - cannot allocate controller state"); + return -ENOMEM; + } + + curr_cfg->chip_id = spi->chip_select; + curr_cfg->ctr_regs = kzalloc(sizeof(struct msp_regs), + GFP_KERNEL); + + if (curr_cfg->ctr_regs == NULL) { + dev_err(&stm_msp->adev->dev, + "setup - cannot allocate mem for regs"); + goto err_first_setup; + } + + dev_err(&stm_msp->adev->dev, + "chip Id = %d\n", curr_cfg->chip_id); + + if (chip_info == NULL) { + chip_info = allocate_default_msp_chip_cfg(spi); + + if (!chip_info) { + dev_err(&stm_msp->adev->dev, + "setup - cannot allocate cntlr data"); + status = -ENOMEM; + goto err_first_setup; + } + + spi->controller_data = chip_info; + } + } + + /* Pointer back to the SPI device */ + chip_info->dev = &spi->dev; + + if (chip_info->freq == 0) { + /* Calculate Specific Freq. */ + if ((MSP_INTERNAL_CLK == chip_info->clk_freq.clk_src) || + (MSP_EXTERNAL_CLK == chip_info->clk_freq.clk_src)) { + sckdiv = chip_info->clk_freq.sckdiv; + } else { + status = -1; + dev_err(&stm_msp->adev->dev, + "setup - controller clock data is incorrect"); + goto err_config_params; + } + } else { + /* Calculate Effective Freq. */ + sckdiv = (DEFAULT_MSP_CLK / (chip_info->freq)) - 1; + + if (sckdiv > MAX_SCKDIV) { + dev_dbg(&stm_msp->adev->dev, + "SPI: Cannot set frequency less than 48Khz," + "setting lowest(48 Khz)\n"); + sckdiv = MAX_SCKDIV; + } + } + + status = verify_msp_controller_parameters(chip_info); + + if (status) { + dev_err(&stm_msp->adev->dev, + "setup - controller data is incorrect"); + goto err_config_params; + } + + /* Now set controller state based on controller data */ + curr_cfg->xfer_type = chip_info->com_mode; + curr_cfg->cs_control = chip_info->cs_control; + curr_cfg->delay = stm_msp_delay; + + curr_cfg->null_write = stm_msp_null_writer; + curr_cfg->null_read = stm_msp_null_reader; + + if (chip_info->data_size <= MSP_DATA_BITS_8) { + dev_dbg(&stm_msp->adev->dev, "Less than 8 bits per word...\n"); + + curr_cfg->n_bytes = 1; + curr_cfg->read = stm_msp_u8_reader; + curr_cfg->write = stm_msp_u8_writer; + } else if (chip_info->data_size <= MSP_DATA_BITS_16) { + dev_dbg(&stm_msp->adev->dev, "Less than 16 bits per word...\n"); + + curr_cfg->n_bytes = 2; + curr_cfg->read = stm_msp_u16_reader; + curr_cfg->write = stm_msp_u16_writer; + } else { + dev_dbg(&stm_msp->adev->dev, "Less than 32 bits per word...\n"); + + curr_cfg->n_bytes = 4; + curr_cfg->read = stm_msp_u32_reader; + curr_cfg->write = stm_msp_u32_writer; + } + + /* Now initialize all register settings reqd. for this chip */ + + msp_regs = (struct msp_regs *)(curr_cfg->ctr_regs); + msp_regs->gcr = 0x0; + msp_regs->tcf = 0x0; + msp_regs->rcf = 0x0; + msp_regs->srg = 0x0; + msp_regs->dmacr = 0x0; + + MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_RDMAE, 0); + MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_TDMAE, 1); + + /* GCR Reg Config */ + + MSP_WBITS(msp_regs->gcr, + MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0); + MSP_WBITS(msp_regs->gcr, + MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1); + MSP_WBITS(msp_regs->gcr, + MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8); + MSP_WBITS(msp_regs->gcr, + MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9); + MSP_WBITS(msp_regs->gcr, + MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10); + MSP_WBITS(msp_regs->gcr, + MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11); + MSP_WBITS(msp_regs->gcr, + MSP_TRANSMIT_DATA_WITH_DELAY, MSP_GCR_MASK_TXDDL, 15); + MSP_WBITS(msp_regs->gcr, + MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16); + MSP_WBITS(msp_regs->gcr, + MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18); + MSP_WBITS(msp_regs->gcr, + MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20); + MSP_WBITS(msp_regs->gcr, + SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23); + + if (chip_info->lbm == SPI_LOOPBACK_ENABLED) + MSP_WBITS(msp_regs->gcr, + MSP_LOOPBACK_ENABLED, MSP_GCR_MASK_LBM, 7); + else + MSP_WBITS(msp_regs->gcr, + MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7); + + if (chip_info->hierarchy == SPI_MASTER) + MSP_WBITS(msp_regs->gcr, + MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14); + else + MSP_WBITS(msp_regs->gcr, + MSP_IS_SPI_SLAVE, MSP_GCR_MASK_TCKSEL, 14); + + if (chip_info->proto_params.clk_phase == SPI_CLK_ZERO_CYCLE_DELAY) + MSP_WBITS(msp_regs->gcr, + MSP_SPI_PHASE_ZERO_CYCLE_DELAY, + MSP_GCR_MASK_SPICKM, 21); + else + MSP_WBITS(msp_regs->gcr, + MSP_SPI_PHASE_HALF_CYCLE_DELAY, + MSP_GCR_MASK_SPICKM, 21); + + if (chip_info->proto_params.clk_pol == SPI_CLK_POL_IDLE_HIGH) + MSP_WBITS(msp_regs->gcr, + MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13); + else + MSP_WBITS(msp_regs->gcr, + MSP_TX_CLOCK_POL_LOW, MSP_GCR_MASK_TCKPOL, 13); + + /* RCF Reg Config */ + MSP_WBITS(msp_regs->rcf, + MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15); + MSP_WBITS(msp_regs->rcf, + MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13); + + if (chip_info->endian_rx == SPI_FIFO_LSB) + MSP_WBITS(msp_regs->rcf, + MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12); + else + MSP_WBITS(msp_regs->rcf, + MSP_RX_ENDIANESS_MSB, MSP_RCF_MASK_RENDN, 12); + + MSP_WBITS(msp_regs->rcf, chip_info->data_size, MSP_RCF_MASK_RP1ELEN, 0); + + /* TCF Reg Config */ + + MSP_WBITS(msp_regs->tcf, + MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15); + MSP_WBITS(msp_regs->tcf, + MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13); + + if (chip_info->endian_rx == SPI_FIFO_LSB) + MSP_WBITS(msp_regs->tcf, + MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12); + else + MSP_WBITS(msp_regs->tcf, + MSP_TX_ENDIANESS_MSB, MSP_TCF_MASK_TENDN, 12); + MSP_WBITS(msp_regs->tcf, chip_info->data_size, MSP_TCF_MASK_TP1ELEN, 0); + + /* SRG Reg Config */ + + MSP_WBITS(msp_regs->srg, sckdiv, MSP_SRG_MASK_SCKDIV, 0); + + /* Save controller_state */ + spi_set_ctldata(spi, curr_cfg); + + return status; + +err_config_params: +err_first_setup: + + kfree(curr_cfg); + return status; +} + +static int __init stm_msp_probe(struct amba_device *adev, struct amba_id *id) +{ + struct device *dev = &adev->dev; + struct stm_msp_controller *platform_info = adev->dev.platform_data; + struct spi_master *master; + struct stm_msp *stm_msp = NULL; /* Data for this driver */ + int irq, status = 0; + + dev_info(dev, "STM MSP driver, device ID: 0x%08x\n", adev->periphid); + + if (platform_info == NULL) { + dev_err(dev, "probe - no platform data supplied\n"); + status = -ENODEV; + goto err_no_pdata; + } + + /* Allocate master with space for data */ + master = spi_alloc_master(dev, sizeof(struct stm_msp)); + + if (master == NULL) { + dev_err(dev, "probe - cannot alloc spi_master\n"); + status = -ENOMEM; + goto err_no_mem; + } + + stm_msp = spi_master_get_devdata(master); + stm_msp->master = master; + stm_msp->master_info = platform_info; + stm_msp->adev = adev; + + stm_msp->clk = clk_get(&adev->dev, NULL); + + if (IS_ERR(stm_msp->clk)) { + dev_err(dev, "probe - cannot find clock\n"); + status = PTR_ERR(stm_msp->clk); + goto free_master; + } + + /* Fetch the Resources, using platform data */ + status = amba_request_regions(adev, NULL); + + if (status) { + status = -ENODEV; + goto disable_clk; + } + + /* Get Hold of Device Register Area... */ + stm_msp->regs = ioremap(adev->res.start, resource_size(&adev->res)); + + if (stm_msp->regs == NULL) { + status = -ENODEV; + goto disable_clk; + } + + irq = adev->irq[0]; + + if (irq <= 0) { + status = -ENODEV; + goto err_no_iores; + } + + stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG); + + /* Required Info for an SPI controller */ + /* Bus Number Which Assigned to this SPI controller on this board */ + master->bus_num = (u16) platform_info->id; + master->num_chipselect = platform_info->num_chipselect; + master->setup = stm_msp_setup; + master->cleanup = (void *)stm_msp_cleanup; + master->transfer = stm_msp_transfer; + + dev_dbg(dev, "BUSNO: %d\n", master->bus_num); + + /* Initialize and start queue */ + status = init_queue(stm_msp); + + if (status != 0) { + dev_err(dev, "probe - problem initializing queue\n"); + goto err_init_queue; + } + + status = start_queue(stm_msp); + + if (status != 0) { + dev_err(dev, "probe - problem starting queue\n"); + goto err_start_queue; + } + + amba_set_drvdata(adev, stm_msp); + + dev_dbg(dev, "probe succeded\n"); + dev_dbg(dev, "Bus No = %d, IRQ Line = %d, Virtual Addr: %x\n", + master->bus_num, irq, (u32)(stm_msp->regs)); + + status = request_irq(stm_msp->adev->irq[0], + stm_msp_interrupt_handler, + 0, stm_msp->master_info->device_name, + stm_msp); + + if (status < 0) { + dev_err(dev, "probe - cannot get IRQ (%d)\n", status); + goto err_irq; + } + + /* Register with the SPI framework */ + status = spi_register_master(master); + + if (status != 0) { + dev_err(dev, "probe - problem registering spi master\n"); + goto err_spi_register; + } + + return 0; + +err_spi_register: + free_irq(stm_msp->adev->irq[0], stm_msp); +err_irq: +err_init_queue: +err_start_queue: + destroy_queue(stm_msp); +err_no_iores: + iounmap(stm_msp->regs); +disable_clk: + clk_put(stm_msp->clk); +free_master: + spi_master_put(master); +err_no_mem: +err_no_pdata: + return status; +} + +static int __exit stm_msp_remove(struct amba_device *adev) +{ + struct stm_msp *stm_msp = amba_get_drvdata(adev); + int status = 0; + + if (!stm_msp) + return 0; + + /* Remove the queue */ + status = destroy_queue(stm_msp); + + if (status != 0) { + dev_err(&adev->dev, "queue remove failed (%d)\n", status); + return status; + } + + stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG); + + /* Release map resources */ + iounmap(stm_msp->regs); + amba_release_regions(adev); + tasklet_disable(&stm_msp->pump_transfers); + free_irq(stm_msp->adev->irq[0], stm_msp); + + /* Disconnect from the SPI framework */ + spi_unregister_master(stm_msp->master); + + clk_put(stm_msp->clk); + + /* Prevent double remove */ + amba_set_drvdata(adev, NULL); + dev_dbg(&adev->dev, "remove succeded\n"); + return status; +} + +#ifdef CONFIG_PM + +/** + * stm_msp_suspend - MSP suspend function registered with PM framework. + * @dev: Reference to amba device structure of the device + * @state: power mgmt state. + * + * This function is invoked when the system is going into sleep, called + * by the power management framework of the linux kernel. + */ +static int stm_msp_suspend(struct amba_device *adev, pm_message_t state) +{ + struct stm_msp *stm_msp = amba_get_drvdata(adev); + int status = 0; + + status = stop_queue(stm_msp); + + if (status != 0) { + dev_warn(&adev->dev, "suspend cannot stop queue\n"); + return status; + } + + dev_dbg(&adev->dev, "suspended\n"); + return 0; +} + +/** + * stm_msp_resume - MSP Resume function registered with PM framework. + * @dev: Reference to amba device structure of the device + * + * This function is invoked when the system is coming out of sleep, called + * by the power management framework of the linux kernel. + */ +static int stm_msp_resume(struct amba_device *adev) +{ + struct stm_msp *stm_msp = amba_get_drvdata(adev); + int status = 0; + + /* Start the queue running */ + status = start_queue(stm_msp); + + if (status) + dev_err(&adev->dev, "problem starting queue (%d)\n", status); + else + dev_dbg(&adev->dev, "resumed\n"); + + return status; +} + +#else +#define stm_msp_suspend NULL +#define stm_msp_resume NULL +#endif /* CONFIG_PM */ + +static struct amba_id stm_msp_ids[] = { + { + .id = MSP_PER_ID, + .mask = MSP_PER_MASK, + }, + { + 0, + 0, + }, +}; + +static struct amba_driver stm_msp_driver = { + .drv = { + .name = "MSP", + }, + .id_table = stm_msp_ids, + .probe = stm_msp_probe, + .remove = __exit_p(stm_msp_remove), + .resume = stm_msp_resume, + .suspend = stm_msp_suspend, +}; + +static int __init stm_msp_init(void) +{ + return amba_driver_register(&stm_msp_driver); +} + +static void __exit stm_msp_exit(void) +{ + amba_driver_unregister(&stm_msp_driver); +} + +module_init(stm_msp_init); +module_exit(stm_msp_exit); + +MODULE_AUTHOR("Sachin Verma "); +MODULE_DESCRIPTION("STM MSP (SPI protocol) Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/stm_msp.h b/include/linux/spi/stm_msp.h new file mode 100644 index 00000000000..501023105cb --- /dev/null +++ b/include/linux/spi/stm_msp.h @@ -0,0 +1,126 @@ +/* + * include/linux/spi/stm_msp.h + * + * Copyright (C) 2010 STMicroelectronics Pvt. Ltd. + * + * Author: Sachin Verma + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _STM_MSP_H +#define _STM_MSP_H + +#include + +/* CHIP select/deselect commands */ +enum spi_chip_select { + SPI_CHIP_SELECT, + SPI_CHIP_DESELECT +}; + +/* Common configuration for different SPI controllers */ +enum spi_loopback { + SPI_LOOPBACK_DISABLED, + SPI_LOOPBACK_ENABLED +}; + +enum spi_hierarchy { + SPI_MASTER, + SPI_SLAVE +}; + +/* Endianess of FIFO Data */ +enum spi_fifo_endian { + SPI_FIFO_MSB, + SPI_FIFO_LSB +}; + +/* SPI mode of operation (Communication modes) */ +enum spi_mode { + SPI_INTERRUPT_TRANSFER, + SPI_POLLING_TRANSFER, +}; + +enum msp_data_size { + MSP_DATA_BITS_DEFAULT = -1, + MSP_DATA_BITS_8 = 0x00, + MSP_DATA_BITS_10, + MSP_DATA_BITS_12, + MSP_DATA_BITS_14, + MSP_DATA_BITS_16, + MSP_DATA_BITS_20, + MSP_DATA_BITS_24, + MSP_DATA_BITS_32, +}; + +enum msp_clk_src { + MSP_INTERNAL_CLK = 0x0, + MSP_EXTERNAL_CLK, +}; + +struct msp_clock_params { + enum msp_clk_src clk_src; + /* value from 0 to 1023 */ + u16 sckdiv; + /* Used only when MSPSCK clocks the sample rate + * generator (SCKSEL = 1Xb): + * 0b: The rising edge of MSPSCK clocks the sample rate generator + * 1b: The falling edge of MSPSCK clocks the sample rate generator */ + int sckpol; +}; + +/* Motorola SPI protocol specific definitions */ +enum spi_clk_phase { + SPI_CLK_ZERO_CYCLE_DELAY = 0x0, /* Receive data on rising edge. */ + SPI_CLK_HALF_CYCLE_DELAY /* Receive data on falling edge. */ +}; + +/* SPI Clock Polarity */ +enum spi_clk_pol { + SPI_CLK_POL_IDLE_LOW, /* Low inactive level */ + SPI_CLK_POL_IDLE_HIGH /* High inactive level */ +}; + +struct motorola_spi_proto_params { + enum spi_clk_phase clk_phase; + enum spi_clk_pol clk_pol; +}; + +struct stm_msp_config_chip { + struct device *dev; + enum spi_loopback lbm; + enum spi_hierarchy hierarchy; + enum spi_fifo_endian endian_rx; + enum spi_fifo_endian endian_tx; + enum spi_mode com_mode; + enum msp_data_size data_size; + struct msp_clock_params clk_freq; + int spi_burst_mode_enable; + struct motorola_spi_proto_params proto_params; + u32 freq; + void (*cs_control)(u32 control); +}; + +/** + * struct stm_msp_controller - device.platform_data for SPI controller devices. + * + * @num_chipselect: chipselects are used to distinguish individual + * SPI slaves, and are numbered from zero to num_chipselects - 1. + * each slave has a chipselect signal, but it's common that not + * every chipselect is connected to a slave. + */ +struct stm_msp_controller { + u8 num_chipselect; + u32 id; + u32 base_addr; + char *device_name; +}; +#endif /* _STM_MSP_H */ -- cgit v1.2.3