# HG changeset patch # User Lukasz Forynski # Date 1285032611 -3600 # Node ID e5fd00cbb70adb3104d5d2616b2e41231bd0c145 # Parent 29b14275133ade187efb857307f6d2174c3a3e38 Added IIC SPI implementation / tests (Master channel only) diff -r 29b14275133a -r e5fd00cbb70a omap3530/assp/bld.inf --- a/omap3530/assp/bld.inf Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/assp/bld.inf Tue Sep 21 02:30:11 2010 +0100 @@ -19,14 +19,15 @@ PRJ_EXPORTS -./inc/assp.mmh assp/omap3530_assp/ // -./inc/omap3530_assp_priv.h assp/omap3530_assp/ // -./inc/omap3530_irqmap.h assp/omap3530_assp/ // -./inc/omap3530_hardware_base.h assp/omap3530_assp/ // -./inc/omap3530_timer.h assp/omap3530_assp/ // -./inc/omap3530_ktrace.h assp/omap3530_assp/ // -./inc/omap3530_asspreg.h assp/omap3530_assp/ // -./inc/locks.h assp/omap3530_assp/ // +./inc/assp.mmh assp/omap3530_assp/ // +./inc/omap3530_assp_priv.h assp/omap3530_assp/ // +./inc/omap3530_irqmap.h assp/omap3530_assp/ // +./inc/omap3530_hardware_base.h assp/omap3530_assp/ // +./inc/omap3530_timer.h assp/omap3530_assp/ // +./inc/omap3530_ktrace.h assp/omap3530_assp/ // +./inc/omap3530_asspreg.h assp/omap3530_assp/ // +./inc/omap3530_scm.h assp/omap3530_assp/ // +./inc/locks.h assp/omap3530_assp/ // PRJ_MMPFILES diff -r 29b14275133a -r e5fd00cbb70a omap3530/assp/inc/omap3530_scm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/assp/inc/omap3530_scm.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,246 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// Description: +// omap3530/assp/inc/omap3530_scm.h +// +// Contains definitions of SCM pad control registers and +// helper functions to manipulate PAD configuration +// (i.e. to change pin functions / modes) +// + +#ifndef __OMAP3530_SCM_H__ +#define __OMAP3530_SCM_H__ + +#include +#include + +class SCM + { +public: + + enum TPadConfigFlags + { + EMode0 = 0x0, + EMode1 = 0x1, + EMode2 = 0x2, + EMode3 = 0x3, + EMode4 = 0x4, + EMode5 = 0x5, + EMode6 = 0x6, + ETestMode = 0x7, + EPullUdEnable = 0x8, + EPullTypeSelect = 0x10, + EInputEnable = 0x100, + EOffEnable = 0x200, + EOffOutEnable = 0x400, + EOffOutValue = 0x800, + EOffPullUdEnable = 0x1000, + EOffPullTypeSelect = 0x2000, + EWakeUpEnable = 0x4000, + EWakeUpEvent = 0x8000, + }; + + enum TLowerHigherWord + { + ELsw = 0, + EMsw + }; + + /** + Set pad configuration + @param aPadAddr - address of padconf register (one of defined below) + @param aAtMsw - one of TLowerHigherWords (flag) to specify which 16bit part + of the register should be updated (i.e. there is one register + for two pins) + @param aConfig - a bitmask of TPadConfigFlags (note to only useone of EModeXx values) + */ + inline static void SetPadConfig(TUint32 aPadAddr, TLowerHigherWord aAtMsw, TUint aConfig) + { + TUint clear_mask = aAtMsw ? (0xffffu ^ aConfig) << 16 : 0xffffu ^ aConfig; + TUint set_mask = aAtMsw ? (0xffffu & aConfig) << 16 : 0xffffu & aConfig; + AsspRegister::Modify32(aPadAddr, clear_mask, set_mask); + } + + /** + Get pad configuration + */ + inline static TUint GetPadConfig(TUint32 aPadAddr, TLowerHigherWord aAtMsw) + { + TUint val = AsspRegister::Read32(aPadAddr); + return aAtMsw ? val >> 16 : val & 0xffffu; + } + }; + +const TUint32 CONTROL_PADCONF_SDRC_D0 = Omap3530HwBase::TVirtual<0x48002030>::Value; // sdrc_d0 +const TUint32 CONTROL_PADCONF_SDRC_D2 = Omap3530HwBase::TVirtual<0x48002034>::Value; // sdrc_d2 +const TUint32 CONTROL_PADCONF_SDRC_D4 = Omap3530HwBase::TVirtual<0x48002038>::Value; // sdrc_d4 +const TUint32 CONTROL_PADCONF_SDRC_D6 = Omap3530HwBase::TVirtual<0x4800203C>::Value; // sdrc_d6 +const TUint32 CONTROL_PADCONF_SDRC_D8 = Omap3530HwBase::TVirtual<0x48002040>::Value; // sdrc_d8 +const TUint32 CONTROL_PADCONF_SDRC_D10 = Omap3530HwBase::TVirtual<0x48002044>::Value; // sdrc_d10 +const TUint32 CONTROL_PADCONF_SDRC_D12 = Omap3530HwBase::TVirtual<0x48002048>::Value; // sdrc_d12 +const TUint32 CONTROL_PADCONF_SDRC_D14 = Omap3530HwBase::TVirtual<0x4800204C>::Value; // sdrc_d14 +const TUint32 CONTROL_PADCONF_SDRC_D16 = Omap3530HwBase::TVirtual<0x48002050>::Value; // sdrc_d16 +const TUint32 CONTROL_PADCONF_SDRC_D18 = Omap3530HwBase::TVirtual<0x48002054>::Value; // sdrc_d18 +const TUint32 CONTROL_PADCONF_SDRC_D20 = Omap3530HwBase::TVirtual<0x48002058>::Value; // sdrc_d20 +const TUint32 CONTROL_PADCONF_SDRC_D22 = Omap3530HwBase::TVirtual<0x4800205C>::Value; // sdrc_d22 +const TUint32 CONTROL_PADCONF_SDRC_D24 = Omap3530HwBase::TVirtual<0x48002060>::Value; // sdrc_d24 +const TUint32 CONTROL_PADCONF_SDRC_D26 = Omap3530HwBase::TVirtual<0x48002064>::Value; // sdrc_d26 +const TUint32 CONTROL_PADCONF_SDRC_D28 = Omap3530HwBase::TVirtual<0x48002068>::Value; // sdrc_d28 +const TUint32 CONTROL_PADCONF_SDRC_D30 = Omap3530HwBase::TVirtual<0x4800206C>::Value; // sdrc_d30 +const TUint32 CONTROL_PADCONF_SDRC_CLK = Omap3530HwBase::TVirtual<0x48002070>::Value; // sdrc_clk +const TUint32 CONTROL_PADCONF_SDRC_CKE1 = Omap3530HwBase::TVirtual<0x48002264>::Value; // sdrc_cke1 safe_mode_ +const TUint32 CONTROL_PADCONF_SDRC_DQS1 = Omap3530HwBase::TVirtual<0x48002074>::Value; // sdrc_dqs1 +const TUint32 CONTROL_PADCONF_SDRC_DQS3 = Omap3530HwBase::TVirtual<0x48002078>::Value; // sdrc_dqs3 +const TUint32 CONTROL_PADCONF_GPMC_A2 = Omap3530HwBase::TVirtual<0x4800207C>::Value; // gpmc_a2 gpio_35 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_A4 = Omap3530HwBase::TVirtual<0x48002080>::Value; // gpmc_a4 gpio_37 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_A6 = Omap3530HwBase::TVirtual<0x48002084>::Value; // gpmc_a6 gpio_39 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_A8 = Omap3530HwBase::TVirtual<0x48002088>::Value; // gpmc_a8 gpio_41 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_A10 = Omap3530HwBase::TVirtual<0x4800208C>::Value; // gpmc_a10 sys_ndmareq3 gpio_43 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_D1 = Omap3530HwBase::TVirtual<0x48002090>::Value; // gpmc_d1 +const TUint32 CONTROL_PADCONF_GPMC_D3 = Omap3530HwBase::TVirtual<0x48002094>::Value; // gpmc_d3 +const TUint32 CONTROL_PADCONF_GPMC_D5 = Omap3530HwBase::TVirtual<0x48002098>::Value; // gpmc_d5 +const TUint32 CONTROL_PADCONF_GPMC_D7 = Omap3530HwBase::TVirtual<0x4800209C>::Value; // gpmc_d7 +const TUint32 CONTROL_PADCONF_GPMC_D9 = Omap3530HwBase::TVirtual<0x480020A0>::Value; // gpmc_d9 gpio_45 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_D11 = Omap3530HwBase::TVirtual<0x480020A4>::Value; // gpmc_d11 gpio_47 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_D13 = Omap3530HwBase::TVirtual<0x480020A8>::Value; // gpmc_d13 gpio_49 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_D15 = Omap3530HwBase::TVirtual<0x480020AC>::Value; // gpmc_d15 gpio_51 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_NCS1 = Omap3530HwBase::TVirtual<0x480020B0>::Value; // gpmc_ncs1 gpio_52 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_NCS3 = Omap3530HwBase::TVirtual<0x480020B4>::Value; // gpmc_ncs3 sys_ndmareq0 gpio_54 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_NCS5 = Omap3530HwBase::TVirtual<0x480020B8>::Value; // gpmc_ncs5 sys_ndmareq2 mcbsp4_dr gpt10_pwm_evt gpio_56 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_NCS7 = Omap3530HwBase::TVirtual<0x480020BC>::Value; // gpmc_ncs7 gpmc_io_dir mcbsp4_fsx gpt8_pwm_evt gpio_58 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_NADV_ALE = Omap3530HwBase::TVirtual<0x480020C0>::Value; // gpmc_nadv_ale +const TUint32 CONTROL_PADCONF_GPMC_NWE = Omap3530HwBase::TVirtual<0x480020C4>::Value; // gpmc_nwe +const TUint32 CONTROL_PADCONF_GPMC_NBE1 = Omap3530HwBase::TVirtual<0x480020C8>::Value; // gpmc_nbe1 gpio_61 safe_mode +const TUint32 CONTROL_PADCONF_GPMC_WAIT0 = Omap3530HwBase::TVirtual<0x480020CC>::Value; // gpmc_wait0 +const TUint32 CONTROL_PADCONF_GPMC_WAIT2 = Omap3530HwBase::TVirtual<0x480020D0>::Value; // gpmc_wait2 gpio_64 safe_mode +const TUint32 CONTROL_PADCONF_DSS_PCLK = Omap3530HwBase::TVirtual<0x480020D4>::Value; // dss_pclk gpio_66 hw_dbg12 safe_mode +const TUint32 CONTROL_PADCONF_DSS_VSYNC = Omap3530HwBase::TVirtual<0x480020D8>::Value; // dss_vsync gpio_68 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA0 = Omap3530HwBase::TVirtual<0x480020DC>::Value; // dss_data0 dsi_dx0 uart1_cts dssvenc656_data gpio_70 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA2 = Omap3530HwBase::TVirtual<0x480020E0>::Value; // dss_data2 dsi_dx1 dssvenc656_data gpio_72 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA4 = Omap3530HwBase::TVirtual<0x480020E4>::Value; // dss_data4 dsi_dx2 uart3_rx_irrx dssvenc656_data gpio_74 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA6 = Omap3530HwBase::TVirtual<0x480020E8>::Value; // dss_data6 uart1_tx dssvenc656_data gpio_76 hw_dbg14 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA8 = Omap3530HwBase::TVirtual<0x480020EC>::Value; // dss_data8 gpio_78 hw_dbg16 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA10 = Omap3530HwBase::TVirtual<0x480020F0>::Value; // dss_data10 sdi_dat1n gpio_80 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA12 = Omap3530HwBase::TVirtual<0x480020F4>::Value; // dss_data12 sdi_dat2n gpio_82 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA14 = Omap3530HwBase::TVirtual<0x480020F8>::Value; // dss_data14 sdi_dat3n gpio_84 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA16 = Omap3530HwBase::TVirtual<0x480020FC>::Value; // dss_data16 gpio_86 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA18 = Omap3530HwBase::TVirtual<0x48002100>::Value; // dss_data18 sdi_vsync mcspi3_clk dss_data0 gpio_88 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA20 = Omap3530HwBase::TVirtual<0x48002104>::Value; // dss_data20 sdi_den mcspi3_somi dss_data2 gpio_90 safe_mode +const TUint32 CONTROL_PADCONF_DSS_DATA22 = Omap3530HwBase::TVirtual<0x48002108>::Value; // dss_data22 sdi_clkp mcspi3_cs1 dss_data4 gpio_92 safe_mode +const TUint32 CONTROL_PADCONF_CAM_HS = Omap3530HwBase::TVirtual<0x4800210C>::Value; // cam_hs gpio_94 hw_dbg0 safe_mode +const TUint32 CONTROL_PADCONF_CAM_XCLKA = Omap3530HwBase::TVirtual<0x48002110>::Value; // cam_xclka gpio_96 safe_mode +const TUint32 CONTROL_PADCONF_CAM_FLD = Omap3530HwBase::TVirtual<0x48002114>::Value; // cam_fld cam_global_rese gpio_98 hw_dbg3 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D1 = Omap3530HwBase::TVirtual<0x48002118>::Value; // cam_d1 csi2_dy2 gpio_100 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D3 = Omap3530HwBase::TVirtual<0x4800211C>::Value; // cam_d3 gpio_102 hw_dbg5 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D5 = Omap3530HwBase::TVirtual<0x48002120>::Value; // cam_d5 gpio_104 hw_dbg7 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D7 = Omap3530HwBase::TVirtual<0x48002124>::Value; // cam_d7 gpio_106 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D9 = Omap3530HwBase::TVirtual<0x48002128>::Value; // cam_d9 gpio_108 safe_mode +const TUint32 CONTROL_PADCONF_CAM_D11 = Omap3530HwBase::TVirtual<0x4800212C>::Value; // cam_d11 gpio_110 hw_dbg9 safe_mode +const TUint32 CONTROL_PADCONF_CAM_WEN = Omap3530HwBase::TVirtual<0x48002130>::Value; // cam_wen cam_shutter gpio_167 hw_dbg10 safe_mode +const TUint32 CONTROL_PADCONF_CSI2_DX0 = Omap3530HwBase::TVirtual<0x48002134>::Value; // csi2_dx0 gpio_112 safe_mode +const TUint32 CONTROL_PADCONF_CSI2_DX1 = Omap3530HwBase::TVirtual<0x48002138>::Value; // csi2_dx1 gpio_114 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP2_FSX = Omap3530HwBase::TVirtual<0x4800213C>::Value; // mcbsp2_fsx gpio_116 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP2_DR = Omap3530HwBase::TVirtual<0x48002140>::Value; // mcbsp2_dr gpio_118 safe_mode +const TUint32 CONTROL_PADCONF_MMC1_CLK = Omap3530HwBase::TVirtual<0x48002144>::Value; // mmc1_clk ms_clk gpio_120 safe_mode +const TUint32 CONTROL_PADCONF_MMC1_DAT0 = Omap3530HwBase::TVirtual<0x48002148>::Value; // mmc1_dat0 ms_dat0 gpio_122 safe_mode +const TUint32 CONTROL_PADCONF_MMC1_DAT2 = Omap3530HwBase::TVirtual<0x4800214C>::Value; // mmc1_dat2 ms_dat2 gpio_124 safe_mode +const TUint32 CONTROL_PADCONF_MMC1_DAT4 = Omap3530HwBase::TVirtual<0x48002150>::Value; // mmc1_dat4 sim_io gpio_126 safe_mode +const TUint32 CONTROL_PADCONF_MMC1_DAT6 = Omap3530HwBase::TVirtual<0x48002154>::Value; // mmc1_dat6 sim_pwrctrl gpio_128 safe_mode +const TUint32 CONTROL_PADCONF_MMC2_CLK = Omap3530HwBase::TVirtual<0x48002158>::Value; // mmc2_clk mcspi3_clk gpio_130 safe_mode +const TUint32 CONTROL_PADCONF_MMC2_DAT0 = Omap3530HwBase::TVirtual<0x4800215C>::Value; // mmc2_dat0 mcspi3_somi gpio_132 safe_mode +const TUint32 CONTROL_PADCONF_MMC2_DAT2 = Omap3530HwBase::TVirtual<0x48002160>::Value; // mmc2_dat2 mcspi3_cs1 gpio_134 safe_mode +const TUint32 CONTROL_PADCONF_MMC2_DAT4 = Omap3530HwBase::TVirtual<0x48002164>::Value; // mmc2_dat4 mmc2_dir_dat0 mmc3_dat0 gpio_136 safe_mode +const TUint32 CONTROL_PADCONF_MMC2_DAT6 = Omap3530HwBase::TVirtual<0x48002168>::Value; // mmc2_dat6 mmc2_dir_cmd cam_shutter mmc3_dat2 gpio_138 hsusb3_tll_dir safe_mode +const TUint32 CONTROL_PADCONF_MCBSP3_DX = Omap3530HwBase::TVirtual<0x4800216C>::Value; // mcbsp3_dx uart2_cts gpio_140 hsusb3_tll_data4 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP3_CLKX = Omap3530HwBase::TVirtual<0x48002170>::Value; // mcbsp3_clkx uart2_tx gpio_142 hsusb3_tll_data6 safe_mode +const TUint32 CONTROL_PADCONF_UART2_CTS = Omap3530HwBase::TVirtual<0x48002174>::Value; // uart2_cts mcbsp3_dx gpt9_pwm_evt gpio_144 safe_mode +const TUint32 CONTROL_PADCONF_UART2_TX = Omap3530HwBase::TVirtual<0x48002178>::Value; // uart2_tx mcbsp3_clkx gpt11_pwm_evt gpio_146 safe_mode +const TUint32 CONTROL_PADCONF_UART1_TX = Omap3530HwBase::TVirtual<0x4800217C>::Value; // uart1_tx gpio_148 safe_mode +const TUint32 CONTROL_PADCONF_UART1_CTS = Omap3530HwBase::TVirtual<0x48002180>::Value; // uart1_cts gpio_150 hsusb3_tll_clk safe_mode +const TUint32 CONTROL_PADCONF_MCBSP4_CLKX = Omap3530HwBase::TVirtual<0x48002184>::Value; // mcbsp4_clkx gpio_152 hsusb3_tll_data1 mm3_txse0 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP4_DX = Omap3530HwBase::TVirtual<0x48002188>::Value; // mcbsp4_dx gpio_154 hsusb3_tll_data2 mm3_txdat safe_mode +const TUint32 CONTROL_PADCONF_MCBSP1_CLKR = Omap3530HwBase::TVirtual<0x4800218C>::Value; // mcbsp1_clkr mcspi4_clk sim_cd gpio_156 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP1_DX = Omap3530HwBase::TVirtual<0x48002190>::Value; // mcbsp1_dx mcspi4_simo mcbsp3_dx gpio_158 safe_mode +const TUint32 CONTROL_PADCONF_MCBSP_CLKS = Omap3530HwBase::TVirtual<0x48002194>::Value; // mcbsp_clks cam_shutter gpio_160 uart1_cts safe_mode +const TUint32 CONTROL_PADCONF_MCBSP1_CLKX = Omap3530HwBase::TVirtual<0x48002198>::Value; // mcbsp1_clkx mcbsp3_clkx gpio_162 safe_mode +const TUint32 CONTROL_PADCONF_UART3_RTS_SD = Omap3530HwBase::TVirtual<0x4800219C>::Value; // uart3_rts_sd gpio_164 safe_mode +const TUint32 CONTROL_PADCONF_UART3_TX_IRTX = Omap3530HwBase::TVirtual<0x480021A0>::Value; // uart3_tx_irtx gpio_166 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_STP = Omap3530HwBase::TVirtual<0x480021A4>::Value; // hsusb0_stp gpio_121 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_NXT = Omap3530HwBase::TVirtual<0x480021A8>::Value; // hsusb0_nxt gpio_124 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_DATA1 = Omap3530HwBase::TVirtual<0x480021AC>::Value; // hsusb0_data1 uart3_rx_irrx gpio_130 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_DATA3 = Omap3530HwBase::TVirtual<0x480021B0>::Value; // hsusb0_data3 uart3_cts_rctx gpio_169 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_DATA5 = Omap3530HwBase::TVirtual<0x480021B4>::Value; // hsusb0_data5 gpio_189 safe_mode +const TUint32 CONTROL_PADCONF_HSUSB0_DATA7 = Omap3530HwBase::TVirtual<0x480021B8>::Value; // hsusb0_data7 gpio_191 safe_mode +const TUint32 CONTROL_PADCONF_I2C1_SDA = Omap3530HwBase::TVirtual<0x480021BC>::Value; // i2c1_sda +const TUint32 CONTROL_PADCONF_I2C2_SDA = Omap3530HwBase::TVirtual<0x480021C0>::Value; // i2c2_sda gpio_183 safe_mode +const TUint32 CONTROL_PADCONF_I2C3_SDA = Omap3530HwBase::TVirtual<0x480021C4>::Value; // i2c3_sda gpio_185 safe_mode +const TUint32 CONTROL_PADCONF_MCSPI1_CLK = Omap3530HwBase::TVirtual<0x480021C8>::Value; // mcspi1_clk mmc2_dat4 gpio_171 safe_mode +const TUint32 CONTROL_PADCONF_MCSPI1_SOMI = Omap3530HwBase::TVirtual<0x480021CC>::Value; // mcspi1_somi mmc2_dat6 gpio_173 safe_mode +const TUint32 CONTROL_PADCONF_MCSPI1_CS1 = Omap3530HwBase::TVirtual<0x480021D0>::Value; // mcspi1_cs1 adpllv2d_dither mmc3_cmd gpio_175 safe_mode +const TUint32 CONTROL_PADCONF_MCSPI1_CS3 = Omap3530HwBase::TVirtual<0x480021D4>::Value; // mcspi1_cs3 hsusb2_tll_data2 hsusb2_data2 gpio_177 mm2_txdat safe_mode +const TUint32 CONTROL_PADCONF_MCSPI2_SIMO = Omap3530HwBase::TVirtual<0x480021D8>::Value; // mcspi2_simo gpt9_pwm_evt hsusb2_tll_data4 hsusb2_data4 gpio_179 safe_mode +const TUint32 CONTROL_PADCONF_MCSPI2_CS0 = Omap3530HwBase::TVirtual<0x480021DC>::Value; // mcspi2_cs0 gpt11_pwm_evt hsusb2_tll_data6 hsusb2_data6 gpio_181 safe_mode +const TUint32 CONTROL_PADCONF_SYS_NIRQ = Omap3530HwBase::TVirtual<0x480021E0>::Value; // sys_nirq gpio_0 safe_mode +const TUint32 CONTROL_PADCONF_ETK_CLK = Omap3530HwBase::TVirtual<0x480025D8>::Value; // etk_clk mcbsp5_clkx mmc3_clk hsusb1_stp gpio_12 mm1_rxdp hsusb1_tll_stp hw_dbg0 +const TUint32 CONTROL_PADCONF_ETK_D0 = Omap3530HwBase::TVirtual<0x480025DC>::Value; // etk_d0 mcspi3_simo mmc3_dat4 hsusb1_data0 gpio_14 mm1_rxrcv hsusb1_tll_data0 hw_dbg2 +const TUint32 CONTROL_PADCONF_ETK_D2 = Omap3530HwBase::TVirtual<0x480025E0>::Value; // etk_d2 mcspi3_cs0 hsusb1_data2 gpio_16 mm1_txdat hsusb1_tll_data2 hw_dbg4 +const TUint32 CONTROL_PADCONF_ETK_D4 = Omap3530HwBase::TVirtual<0x480025E4>::Value; // etk_d4 mcbsp5_dr mmc3_dat0 hsusb1_data4 gpio_18 hsusb1_tll_data4 hw_dbg6 +const TUint32 CONTROL_PADCONF_ETK_D6 = Omap3530HwBase::TVirtual<0x480025E8>::Value; // etk_d6 mcbsp5_dx mmc3_dat2 hsusb1_data6 gpio_20 hsusb1_tll_data6 hw_dbg8 +const TUint32 CONTROL_PADCONF_ETK_D8 = Omap3530HwBase::TVirtual<0x480025EC>::Value; // etk_d8 Reserved for mmc3_dat6 hsusb1_dir gpio_22 hsusb1_tll_dir hw_dbg10 +const TUint32 CONTROL_PADCONF_ETK_D10 = Omap3530HwBase::TVirtual<0x480025F0>::Value; // etk_d10 uart1_rx hsusb2_clk gpio_24 hsusb2_tll_clk hw_dbg12 +const TUint32 CONTROL_PADCONF_ETK_D12 = Omap3530HwBase::TVirtual<0x480025F4>::Value; // etk_d12 hsusb2_dir gpio_26 hsusb2_tll_dir hw_dbg14 +const TUint32 CONTROL_PADCONF_ETK_D14 = Omap3530HwBase::TVirtual<0x480025F8>::Value; // etk_d14 hsusb2_data0 gpio_28 mm2_rxrcv hsusb2_tll_data0 hw_dbg16 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD0 = Omap3530HwBase::TVirtual<0x480021E4>::Value; // sad2d_mcad0 mad2d_mcad0 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD2 = Omap3530HwBase::TVirtual<0x480021E8>::Value; // sad2d_mcad2 mad2d_mcad2 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD4 = Omap3530HwBase::TVirtual<0x480021EC>::Value; // sad2d_mcad4 mad2d_mcad4 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD6 = Omap3530HwBase::TVirtual<0x480021F0>::Value; // sad2d_mcad6 mad2d_mcad6 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD8 = Omap3530HwBase::TVirtual<0x480021F4>::Value; // sad2d_mcad8 mad2d_mcad8 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD10 = Omap3530HwBase::TVirtual<0x480021F8>::Value; // sad2d_mcad10 mad2d_mcad10 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD12 = Omap3530HwBase::TVirtual<0x480021FC>::Value; // sad2d_mcad12 mad2d_mcad12 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD14 = Omap3530HwBase::TVirtual<0x48002200>::Value; // sad2d_mcad14 mad2d_mcad14 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD16 = Omap3530HwBase::TVirtual<0x48002204>::Value; // sad2d_mcad16 mad2d_mcad16 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD18 = Omap3530HwBase::TVirtual<0x48002208>::Value; // sad2d_mcad18 mad2d_mcad18 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD20 = Omap3530HwBase::TVirtual<0x4800220C>::Value; // sad2d_mcad20 mad2d_mcad20 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD22 = Omap3530HwBase::TVirtual<0x48002210>::Value; // sad2d_mcad22 mad2d_mcad22 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD24 = Omap3530HwBase::TVirtual<0x48002214>::Value; // sad2d_mcad24 mad2d_mcad24 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD26 = Omap3530HwBase::TVirtual<0x48002218>::Value; // sad2d_mcad26 mad2d_mcad26 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD28 = Omap3530HwBase::TVirtual<0x4800221C>::Value; // sad2d_mcad28 mad2d_mcad28 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD30 = Omap3530HwBase::TVirtual<0x48002220>::Value; // sad2d_mcad30 mad2d_mcad30 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD32 = Omap3530HwBase::TVirtual<0x48002224>::Value; // sad2d_mcad32 mad2d_mcad32 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD34 = Omap3530HwBase::TVirtual<0x48002228>::Value; // sad2d_mcad34 mad2d_mcad34 +const TUint32 CONTROL_PADCONF_SAD2D_MCAD36 = Omap3530HwBase::TVirtual<0x4800222C>::Value; // sad2d_mcad36 mad2d_mcad36 +const TUint32 CONTROL_PADCONF_SAD2D_NRESPWRON = Omap3530HwBase::TVirtual<0x48002230>::Value; // sad2d_nrespwron +const TUint32 CONTROL_PADCONF_SAD2D_ARMNIRQ = Omap3530HwBase::TVirtual<0x48002234>::Value; // sad2d_armnirq +const TUint32 CONTROL_PADCONF_SAD2D_SPINT = Omap3530HwBase::TVirtual<0x48002238>::Value; // sad2d_spint gpio_187 +const TUint32 CONTROL_PADCONF_SAD2D_DMAREQ0 = Omap3530HwBase::TVirtual<0x4800223C>::Value; // sad2d_dmareq0 uart2_dma_tx mmc1_dma_tx +const TUint32 CONTROL_PADCONF_SAD2D_DMAREQ2 = Omap3530HwBase::TVirtual<0x48002240>::Value; // sad2d_dmareq2 uart1_dma_tx uart3_dma_tx +const TUint32 CONTROL_PADCONF_SAD2D_NTRST = Omap3530HwBase::TVirtual<0x48002244>::Value; // sad2d_ntrst +const TUint32 CONTROL_PADCONF_SAD2D_TDO = Omap3530HwBase::TVirtual<0x48002248>::Value; // sad2d_tdo +const TUint32 CONTROL_PADCONF_SAD2D_TCK = Omap3530HwBase::TVirtual<0x4800224C>::Value; // sad2d_tck +const TUint32 CONTROL_PADCONF_SAD2D_MSTDBY = Omap3530HwBase::TVirtual<0x48002250>::Value; // sad2d_mstdby +const TUint32 CONTROL_PADCONF_SAD2D_IDLEACK = Omap3530HwBase::TVirtual<0x48002254>::Value; // sad2d_idleack +const TUint32 CONTROL_PADCONF_SAD2D_SWRITE = Omap3530HwBase::TVirtual<0x48002258>::Value; // sad2d_swrite mad2d_mwrite +const TUint32 CONTROL_PADCONF_SAD2D_SREAD = Omap3530HwBase::TVirtual<0x4800225C>::Value; // sad2d_sread mad2d_mread +const TUint32 CONTROL_PADCONF_SAD2D_SBUSFLAG = Omap3530HwBase::TVirtual<0x48002260>::Value; // sad2d_sbusflag mad2d_mbusflag +const TUint32 CONTROL_PADCONF_I2C4_SCL = Omap3530HwBase::TVirtual<0x48002A00>::Value; // i2c4_scl sys_nvmode1 safe_mode +const TUint32 CONTROL_PADCONF_SYS_32K = Omap3530HwBase::TVirtual<0x48002A04>::Value; // sys_32k +const TUint32 CONTROL_PADCONF_SYS_NRESWARM = Omap3530HwBase::TVirtual<0x48002A08>::Value; // sys_nreswarm gpio_30 safe_mode +const TUint32 CONTROL_PADCONF_SYS_BOOT1 = Omap3530HwBase::TVirtual<0x48002A0C>::Value; // sys_boot1 gpio_3 safe_mode +const TUint32 CONTROL_PADCONF_SYS_BOOT3 = Omap3530HwBase::TVirtual<0x48002A10>::Value; // sys_boot3 gpio_5 safe_mode +const TUint32 CONTROL_PADCONF_SYS_BOOT5 = Omap3530HwBase::TVirtual<0x48002A14>::Value; // sys_boot5 mmc2_dir_dat3 gpio_7 safe_mode +const TUint32 CONTROL_PADCONF_SYS_OFF_MODE = Omap3530HwBase::TVirtual<0x48002A18>::Value; // sys_off_mode gpio_9 safe_mode +const TUint32 CONTROL_PADCONF_JTAG_NTRST = Omap3530HwBase::TVirtual<0x48002A1C>::Value; // jtag_ntrst +const TUint32 CONTROL_PADCONF_JTAG_TMS_TMSC = Omap3530HwBase::TVirtual<0x48002A20>::Value; // jtag_tms_tmsc +const TUint32 CONTROL_PADCONF_JTAG_EMU0 = Omap3530HwBase::TVirtual<0x48002A24>::Value; // jtag_emu0 gpio_11 safe_mode +const TUint32 CONTROL_PADCONF_SAD2D_SWAKEUP = Omap3530HwBase::TVirtual<0x48002A4C>::Value; // sad2d_swakeup +const TUint32 CONTROL_PADCONF_JTAG_TDO = Omap3530HwBase::TVirtual<0x48002A50>::Value; // jtag_tdo + +#endif /*__OMAP3530_SCM_H__*/ diff -r 29b14275133a -r e5fd00cbb70a omap3530/base_beagle.mrp --- a/omap3530/base_beagle.mrp Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/base_beagle.mrp Tue Sep 21 02:30:11 2010 +0100 @@ -14,6 +14,7 @@ binary \sf\adaptation\beagleboard\omap3530\kernel all binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\gpio all binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\i2c all +# binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\spi all binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\prcm all # binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\prm all binary \sf\adaptation\beagleboard\omap3530\omap3530_drivers\uart all @@ -35,6 +36,7 @@ exports \sf\adaptation\beagleboard\omap3530\kernel exports \sf\adaptation\beagleboard\omap3530\omap3530_drivers\gpio exports \sf\adaptation\beagleboard\omap3530\omap3530_drivers\i2c + exports \sf\adaptation\beagleboard\omap3530\omap3530_drivers\prcm # exports \sf\adaptation\beagleboard\omap3530\omap3530_drivers\prm exports \sf\adaptation\beagleboard\omap3530\omap3530_drivers\uart diff -r 29b14275133a -r e5fd00cbb70a omap3530/beagle_drivers/led/led.mmp --- a/omap3530/beagle_drivers/led/led.mmp Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/beagle_drivers/led/led.mmp Tue Sep 21 02:30:11 2010 +0100 @@ -36,11 +36,11 @@ macro USER_BUTTON_ENTERS_CRASH_DEBUGGER sourcepath . -source led.cpp +source led.cpp library VariantTarget(ecust,lib) library AsspTarget(gpio,lib) -library AsspTarget(prcm,lib) +library AsspTarget(prcm,lib) VENDORID 0x70000001 diff -r 29b14275133a -r e5fd00cbb70a omap3530/beagleboard/rom/base_beagle.iby --- a/omap3530/beagleboard/rom/base_beagle.iby Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/beagleboard/rom/base_beagle.iby Tue Sep 21 02:30:11 2010 +0100 @@ -30,30 +30,30 @@ #endif #ifndef _EABI -# ifdef _ARM4 -# define _EABI ARM4 +#ifdef _ARM4 +#define _EABI ARM4 ECHO Defaulting to ARM4 -# elif defined(_ARMV5) -# define _EABI ARMV5 +#elif defined(_ARMV5) +#define _EABI ARMV5 ECHO Defaulting to ARMV5 -# elif defined _X86GCC -# define _EABI x86gcc -# endif +#elif defined _X86GCC +#define _EABI x86gcc +#endif #endif -# ifdef _PLAT -# undef _EABI -# define _EABI _PLAT +#ifdef _PLAT +#undef _EABI +#define _EABI _PLAT ECHO Defaulting to _EABI -# endif +#endif -# ifdef _GCCE -# undef _EABI -# define _EABI GCCE -# elif defined(ABIV2) || defined(ABIv2) -# undef _EABI -# define _EABI ARMV5_ABIV2 -# endif +#ifdef _GCCE +#undef _EABI +#define _EABI GCCE +#elif defined(ABIV2) || defined(ABIv2) +#undef _EABI +#define _EABI ARMV5_ABIV2 +#endif #ifndef _KABI #define _KABI _EABI @@ -104,6 +104,7 @@ extension[VARID]=\epoc32\release\ARMV5\BUILD_DIR\_omap3530_i2c.dll \sys\bin\i2c.dll #include <../omapshared/tps65950.iby> +#include #ifdef SYMBIAN_BASE_USE_GCE // Use the new GCE compliant display driver diff -r 29b14275133a -r e5fd00cbb70a omap3530/beagleboard/rom/kernel.iby --- a/omap3530/beagleboard/rom/kernel.iby Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/beagleboard/rom/kernel.iby Tue Sep 21 02:30:11 2010 +0100 @@ -43,6 +43,7 @@ //extension[VARID]= \Epoc32\Release\##KMAIN##\##BUILD##\resman.ldd \sys\bin\resman.ldd #include +#include device[VARID]=\Epoc32\Release\ARMV5\##BUILD##\_omap3530_EUART.PDD \sys\bin\euart.pdd device[VARID]=\Epoc32\Release\ARMV5\##BUILD##\ECOMM.LDD \sys\bin\ecomm.ldd diff -r 29b14275133a -r e5fd00cbb70a omap3530/bld.inf --- a/omap3530/bld.inf Mon Sep 20 21:28:54 2010 +0100 +++ b/omap3530/bld.inf Tue Sep 21 02:30:11 2010 +0100 @@ -30,6 +30,7 @@ #include "omap3530_drivers/gpio/bld.inf" #include "omap3530_drivers/i2c/bld.inf" #include "omap3530_drivers/prcm/bld.inf" +#include "omap3530_drivers/spi/bld.inf" // #include "omap3530_drivers/prm/bld.inf" #include "omap3530_drivers/uart/bld.inf" #include "omap3530_drivers/usbcc/bld.inf" diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/bld.inf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/bld.inf Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,29 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530/omap3530_drivers/spi/bld.inf +// + + +PRJ_PLATFORMS +ARMV5 + +PRJ_EXPORTS +spi.iby rom/omap3530/ + +PRJ_MMPFILES +spi + +PRJ_TESTMMPFILES +test/t_spi_client_m +test/d_spi_client_m + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/master.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/master.cpp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,886 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// Implementation of IIC master channel for a SPI bus. +// + +#define DBGPRINT(x) +#define DBG_ERR(x) x + + +#ifdef _DEBUG +#define DEBUG_ONLY(x) //x +#else +#define DEBUG_ONLY(x) +#endif + + +// DO NOT CHANGE THESE- trying to tune the driver (unless you really know what you're doing) +// as this this is only for development purpose to tune the driver. Fifo mode is not yet enabled, but this +// doesn't affect operation. After development has been finished - these macros and #ifdefs will be removed +// entirely. For now only SINGLE_MODE should ever be defined. +//#define USE_TX_FIFO +//#define USING_TX_COUNTER +//#define PER_TRANSFER_MODE +#define SINGLE_MODE + +#include +#include +#include +#include "omap3530_spi.h" +#include "psl_init.h" +#include "master.h" + +DSpiMasterBeagle::DSpiMasterBeagle(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) : + DIicBusChannelMaster(aBusType, aChanDuplex), + iTransferEndDfc(TransferEndDfc, this, KIicPslDfcPriority) + { + iChannelNumber = aChannelNumber; + iIrqId = KMcSpiIrqId[iChannelNumber]; + iHwBase = KMcSpiRegBase[iChannelNumber]; + iState = EIdle; + DBGPRINT(Kern::Printf("DSpiMasterBeagle::DSpiMasterBeagle: at 0x%x, iChannelNumber = %d", this, iChannelNumber)); + } + +TInt DSpiMasterBeagle::DoCreate() + { + DBGPRINT(Kern::Printf("\nDSpiMasterBeagle::DoCreate() ch: %d \n", iChannelNumber)); + DBGPRINT(Kern::Printf("HW revision is %x", AsspRegister::Read32(iHwBase + MCSPI_REVISION))); + + TInt r = KErrNone; + + // Create the DFCQ to be used by the channel + if(!iDfcQ) + { + TBuf8 threadName (KIicPslThreadName); + threadName.AppendNum(iChannelNumber); + r = Kern::DfcQCreate(iDfcQ, KIicPslThreadPriority, &threadName); + if(r != KErrNone) + { + DBG_ERR(Kern::Printf("DFC Queue creation failed, channel number: %d, r = %d\n", iChannelNumber, r)); + return r; + } + } + + // PIL Base class initialization - this must be called prior to SetDfcQ(iDfcQ) + r = Init(); + if(r == KErrNone) + { + // Call base class function to set DFCQ pointers in the required objects + // This also enables the channel to process transaction requests + SetDfcQ(iDfcQ); + + // PSL DFCQ initialisation for local DFC + iTransferEndDfc.SetDfcQ(iDfcQ); + + // Bind interrupts. + r = Interrupt::Bind(iIrqId, Isr, this); + if(r < KErrNone) + { + DBG_ERR(Kern::Printf("ERROR: InterruptBind error.. %d", r)); + return r; + } + } + + // Make sure clocks are enabled (TBD: this could go to 'PowerUp/PowerDown' if using PRM) + Prcm::SetClockState( Prcm::EClkMcSpi3_F, Prcm::EClkOn ); + Prcm::SetClockState( Prcm::EClkMcSpi3_I, Prcm::EClkOn ); + // TODO:consider auto-idle for PRCM.CM_AUTOIDLE1_CORE + + SetupSpiPins(iChannelNumber); + // end of system wide settings.. + + return r; + } + +// A static method used to construct the DSpiMasterBeagle object. +DSpiMasterBeagle* DSpiMasterBeagle::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) + { + DBGPRINT(Kern::Printf("DSpiMasterBeagle::NewL(): ChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); + DSpiMasterBeagle *pChan = new DSpiMasterBeagle(aChannelNumber, aBusType, aChanDuplex); + + TInt r = KErrNoMemory; + if(pChan) + { + r = pChan->DoCreate(); + } + if(r != KErrNone) + { + delete pChan; + pChan = NULL; + } + return pChan; + } + +// This method is called by the PIL to initiate the transaction. After finishing it's processing, +// the PSL calls the PIL function CompleteRequest to indicate the success (or otherwise) of the request +TInt DSpiMasterBeagle::DoRequest(TIicBusTransaction* aTransaction) + { + DBGPRINT(Kern::Printf("\n=>DSpiMasterBeagle::DoRequest (aTransaction=0x%x)\n", aTransaction)); + + // If the pointer to the transaction passed in as a parameter, or its associated pointer to the + // header information is NULL, return KErrArgument + if(!aTransaction || !GetTransactionHeader(aTransaction)) + { + return KErrArgument; + } + + // The PSL operates a simple state machine to ensure that only one transaction is processed + // at a time - if the channel is currently busy, reject the request (PIL should not try that!) + if(iState != EIdle) + { + return KErrInUse; + } + + // copy pointer to the transaction + iCurrTransaction = aTransaction; + + // Configure the hardware to support the transaction + TInt r = KErrNone; + if(TransConfigDiffersFromPrev()) + { + r = ConfigureInterface(); + if(r != KErrNone) + { + return r; + } + } + + // start processing transfers of this transaction. + r = ProcessNextTransfers(); + return r; + } + +TBool DSpiMasterBeagle::TransConfigDiffersFromPrev() + { + TConfigSpiV01 &newHeader = (*(TConfigSpiBufV01*) (GetTransactionHeader(iCurrTransaction)))(); + + // get the slave address (i.e. known as a 'channel' for the current SPI module) + TInt slaveAddr = GET_SLAVE_ADDR(iCurrTransaction->GetBusId()); + DBGPRINT(Kern::Printf("slaveAddr %x", slaveAddr)); + + // compare it to the previous configuration.. + if(slaveAddr != iCurrSS || + newHeader.iWordWidth != iCurrHeader.iWordWidth || + newHeader.iClkSpeedHz != iCurrHeader.iClkSpeedHz || + newHeader.iClkMode != iCurrHeader.iClkMode || + newHeader.iTimeoutPeriod != iCurrHeader.iTimeoutPeriod || + newHeader.iBitOrder != iCurrHeader.iBitOrder || + newHeader.iTransactionWaitCycles != iCurrHeader.iTransactionWaitCycles || + newHeader.iSSPinActiveMode != iCurrHeader.iSSPinActiveMode) + { + iCurrSS = slaveAddr; + iCurrHeader = newHeader; //copy the header.. + return ETrue; + } + return ETrue; + } + +// Init the hardware with the data provided in the transaction and slave-address field +// (these values are already stored in the iCurrHeader) +TInt DSpiMasterBeagle::ConfigureInterface() + { + DBGPRINT(Kern::Printf("ConfigureInterface()")); + + // soft reset the SPI..(Channel 3 for now) + TUint val = AsspRegister::Read32(iHwBase + MCSPI_SYSCONFIG); + val = MCSPI_SYSCONFIG_SOFTRESET; // issue reset + + AsspRegister::Write32(iHwBase + MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); + + val = 0; // TODO will add here this 'smart-wait' stuff that was proposed earlier.. + while (!(val & MCSPI_SYSSTATUS_RESETDONE)) + val = AsspRegister::Read32(iHwBase + MCSPI_SYSSTATUS); + + //AsspRegister::Write32(iHwBase + MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_CLOCKACTIVITY_ALL_ON); + + AsspRegister::Write32(iHwBase + MCSPI_IRQSTATUS, ~0); // clear all interrupts (for now) -- normally only for channel.. + + // channel configuration + // Set the SPI1.MCSPI_CHxCONF[18] IS bit to 0 for the spi1_somi pin in receive mode. + // val = MCSPI_CHxCONF_IS; // pin selection (somi - simo) + // TODO configuration of PINS could also be configurable on a 'per SPI module' basis.. + + // Set the SPI1.MCSPI_CHxCONF[17] DPE1 bit to 0 and the SPI1.MCSPI_CHxCONF[16] DPE0 bit to 1 for the spi1.simo pin in transmit mode. + val = MCSPI_CHxCONF_DPE0; + + // Set transmit & | receive mode for transmit only mode here. If needed - it will be changed dynamically. + val |= MCSPI_CHxCONF_TRM_NO_RECEIVE; + + // set word length. + val |= SpiWordWidth(iCurrHeader.iWordWidth); + + // use the appropriate word with (assuming the data is aligned to bytes). + if(iCurrHeader.iWordWidth > ESpiWordWidth_16) + { + iWordSize = 4; + } + else if (iCurrHeader.iWordWidth > ESpiWordWidth_8) + { + iWordSize = 2; + } + else + { + iWordSize = 1; + } + + // set Slave Select / Chip select signal mode + val |= iCurrHeader.iSSPinActiveMode == ESpiCSPinActiveLow ? MCSPI_CHxCONF_EPOL_LOW : 0; + + // set the CLK POL and PHA (clock mode) + val |= SpiClkMode(iCurrHeader.iClkMode); + + // Set clock. Note that CheckHdr() will be called prior to this function for this header, + // so the value iClkSpeedHz is valid at this point, the KErrNotSupported is not possible + // so the return value check can be ommited here + val |= SpiClkValue(iCurrHeader.iClkSpeedHz); + // __ASSERT_DEBUG(val >= KErrNone, Kern::Fault("spi/master.cpp, line: ", __LINE__)); + +#ifdef USE_TX_FIFO + // enable fifo for transmission.. + // Update me: this can only set in a 'single' mode.. or for only one channel + // but at the momment IIC SPI is used in 'single' mode onlny.. + val |= MCSPI_CHxCONF_FFEW; +// val |= MCSPI_CHxCONF_FFER; // fifo enable for receive.. (TODO) +#endif + + // update the register.. + AsspRegister::Write32(iHwBase + MCSPI_CHxCONF(iCurrSS), val); + + // CS (SS) pin direction.. + val = MCSPI_SYST_SPIDATDIR0; + + // drive csx pin hight or low + val |= (iCurrHeader.iSSPinActiveMode == ESpiCSPinActiveLow)? 1 << iCurrSS : 0; + AsspRegister::Write32(iHwBase + MCSPI_SYST, val); + + // Set the MS bit to 0 to provide the clock (ie. to setup as master) +#ifndef SINGLE_MODE + AsspRegister::Write32(iHwBase + MCSPI_MODULCTRL, MCSPI_MODULCTRL_MS_MASTER); +#else + AsspRegister::Write32(iHwBase + MCSPI_MODULCTRL, MCSPI_MODULCTRL_MS_MASTER | MCSPI_MODULCTRL_SINGLE); +#endif + + return KErrNone; + } + +TInt DSpiMasterBeagle::ProcessNextTransfers() + { + DBGPRINT(Kern::Printf("DSpiMasterBeagle::ProcessNextTransfers():%s", iState==EIdle ? "first" : "next")); + + // Since new transfers are strating, clear exisiting flags + iOperation.iValue = TIicOperationType::ENop; + + // If this is the first transfer in the transaction the channel will be in state EIdle + if(iState == EIdle) + { + // Get the pointer to half-duplex transfer object.. + iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction); + + // Get the pointer to full-duplex transfer object.. + iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction); + + // Update the channel state to EBusy and initialise the transaction status + iState = EBusy; + iTransactionStatus = KErrNone; + + // start timeout timer for this transaction + StartSlaveTimeOutTimer(iCurrHeader.iTimeoutPeriod); + } + else + // If not in state EIdle, get the next transfer in the linked-list held by the transaction + { + // Get the pointer the next half-duplex transfer object.. + iHalfDTransfer = GetTferNextTfer(iHalfDTransfer); + + // Get the pointer to the next half-duplex transfer object.. + if(iFullDTransfer) + { + iFullDTransfer = GetTferNextTfer(iFullDTransfer); + } + } + + TInt r = KErrNone; + if(!iFullDTransfer && !iHalfDTransfer) + { + // There is no more to transfer - and all previous were were completed, + DBGPRINT(Kern::Printf("All transfers completed successfully")); + ExitComplete(KErrNone); + } + else + { + // Process next transfers + TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer); + + if(iFullDTransfer) + { + // For full-duplex transfer setup the read transfer first, as it doesn't + // really start anything - SPI master starts operation when Tx (or clocks)starts.. + + if(hDTrType == TIicBusTransfer::EMasterRead) + { + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead); + if(r != KErrNone) + { + return r; + } + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite); + } + else // hDTrType == TIicBusTransfer::EMasterWrite) + { + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead); + if(r != KErrNone) + { + return r; + } + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite); + } + } + else + // This is a HalfDuplex transfer - so just start it + { + r = StartTransfer(iHalfDTransfer, hDTrType); + } + } + return r; + } + +TInt DSpiMasterBeagle::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType) + { + DBGPRINT(Kern::Printf("DSpiMasterBeagle::StartTransfer() @0x%x, aType: %s", + aTransferPtr, aType == TIicBusTransfer::EMasterWrite ? "write" : "read")); + + if(aTransferPtr == NULL) + { + DBG_ERR(Kern::Printf("DSpiMasterBeagle::StartTransfer - NULL pointer\n")); + return KErrArgument; + } + + TInt r = KErrNone; + + switch(aType) + { + case TIicBusTransfer::EMasterWrite: + { + DBGPRINT(Kern::Printf("Starting EMasterWrite, duplex=%x", iFullDTransfer)); + + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer + const TDes8* desBufPtr = GetTferBuffer(aTransferPtr); + + DBGPRINT(Kern::Printf("Length %d, iWordSize %d", desBufPtr->Length(), iWordSize)); + + // Store the current address and ending address for Transmission - they are required by the ISR and DFC + iTxData = (TInt8*) desBufPtr->Ptr(); + iTxDataEnd = (TInt8*) (iTxData + desBufPtr->Length()); + if ((TInt)iTxDataEnd % iWordSize) + { + DBG_ERR(Kern::Printf("Wrong configuration - word size does not match buffer length")); + return KErrArgument; + } + + DBGPRINT(Kern::Printf("Tx: Start: %x, End %x, bytes %d", iTxData, iTxDataEnd, desBufPtr->Length())); + + // Set the flag to indicate that we'll be transmitting data + iOperation.iOp.iIsTransmitting = ETrue; + + // initiate the transmission.. + r = DoTransfer(aType); + if(r != KErrNone) + { + DBG_ERR(Kern::Printf("Starting Write failed, r = %d", r)); + } + break; + } + + case TIicBusTransfer::EMasterRead: + { + DBGPRINT(Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer)); + + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer + const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); + + // Store the current address and ending address for Reception - they are required by the ISR and DFC + iRxData = (TInt8*) aBufPtr->Ptr(); + iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length()); + + DBGPRINT(Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length())); + + // Set the flag to indicate that we'll be receiving data + iOperation.iOp.iIsReceiving = ETrue; + + // initiate the reception + r = DoTransfer(aType); + if(r != KErrNone) + { + DBG_ERR(Kern::Printf("Starting Read failed, r = %d", r)); + } + break; + } + + default: + { + DBG_ERR(Kern::Printf("Unsupported TransactionType %x", aType)); + r = KErrArgument; + break; + } + } + + return r; + } + +// Method called by StartTransfer to actually initiate the transfers. +TInt DSpiMasterBeagle::DoTransfer(TUint8 aType) + { + DBGPRINT(Kern::Printf("\nDSpiMasterBeagle::DoTransfer()")); + TInt r = KErrNone; + + AsspRegister::Write32(iHwBase + MCSPI_IRQSTATUS, ~0); + + switch(aType) + { + case TIicBusTransfer::EMasterWrite: + { + // enable the channel here.. + AsspRegister::Write32(iHwBase + MCSPI_CHxCTRL(iCurrSS), MCSPI_CHxCTRL_EN); + + AsspRegister::Modify32(iHwBase + MCSPI_IRQSTATUS, 0, + MCSPI_IRQ_TX_EMPTY(iCurrSS) /*| MCSPI_IRQ_TX_UNDERFLOW(iCurrSS)*/); + + AsspRegister::Modify32(iHwBase + MCSPI_IRQENABLE, 0, + MCSPI_IRQ_TX_EMPTY(iCurrSS) /*| MCSPI_IRQ_TX_UNDERFLOW(iCurrSS)*/); + +#ifdef SINGLE_MODE + // in SINGLE mode needs to manually assert CS line for current + AsspRegister::Modify32(iHwBase + MCSPI_CHxCONF(iCurrSS), 0, MCSPI_CHxCONF_FORCE); + + // change the pad config - now the SPI drives the line appropriately.. + SetCsActive(iChannelNumber, iCurrSS, iCurrHeader.iSSPinActiveMode); +#endif /*SINGLE_MODE*/ + +#ifdef USE_TX_FIFO + const TInt KTxFifoThreshold = 8; + TUint numWordsToTransfer = (iTxDataEnd - iTxData); + TUint wordsToWrite = Min(numWordsToTransfer/iWordSize, KTxFifoThreshold/iWordSize); + + + TInt iAlmostFullLevel = 0; + TInt iAlmostEmptyLevel = 1; //KTxFifoThreshold; + + // setup FIFOs + AsspRegister::Write32(iHwBase + MCSPI_XFERLEVEL, + MCSPI_XFERLEVEL_WCNT(0) | // total num words + MCSPI_XFERLEVEL_AFL(iAlmostFullLevel) | // Rx almost full + MCSPI_XFERLEVEL_AEL(iAlmostEmptyLevel) ); // Tx almost empty + + // copy data to fifo.. + for (TInt i = 0; i < wordsToWrite; i++) + { + iTxData += iWordSize; + AsspRegister::Write32(iHwBase + MCSPI_TXx(iCurrSS), *(iTxData -iWordSize)); + } + +#else /*USE_TX_FIFO*/ + + TUint val = 0; + for (TInt i = 0; i < iWordSize; i++) + { + val |= (*iTxData++) << i * 8; + } + + DEBUG_ONLY(DumpCurrentStatus("DoTransfer(Write)")); + AsspRegister::Write32(iHwBase + MCSPI_TXx(iCurrSS), val); +#endif /*USE_TX_FIFO*/ + + // enable system interrupt + Interrupt::Enable(iIrqId); + break; + } + case TIicBusTransfer::EMasterRead: + { + // enable transmit and receive.. + AsspRegister::Modify32(iHwBase + MCSPI_CHxCONF(iCurrSS), MCSPI_CHxCONF_TRM_NO_RECEIVE, 0); + + // for single read (not duplex) one way to to allow clock generation is to enable Tx + // and write '0' to Txregister (just like in duplex transaction). We also need to assert Cs line. + if(!iFullDTransfer) + { + // enable the channel.. + AsspRegister::Write32(iHwBase + MCSPI_CHxCTRL(iCurrSS), MCSPI_CHxCTRL_EN); + + // enable TX and RX Empty interrupts + AsspRegister::Modify32(iHwBase + MCSPI_IRQSTATUS, 0, MCSPI_IRQ_TX_EMPTY(iCurrSS) | MCSPI_IRQ_RX_FULL(iCurrSS)); + AsspRegister::Modify32(iHwBase + MCSPI_IRQENABLE, 0, MCSPI_IRQ_TX_EMPTY(iCurrSS) | MCSPI_IRQ_RX_FULL(iCurrSS)); +#ifdef SINGLE_MODE + // in SINGLE mode needs to manually assert CS line for current + AsspRegister::Modify32(iHwBase + MCSPI_CHxCONF(iCurrSS), 0, MCSPI_CHxCONF_FORCE); + + // change the pad config - now the SPI drives the line appropriately.. + SetCsActive(iChannelNumber, iCurrSS, iCurrHeader.iSSPinActiveMode); +#endif /*SINGLE_MODE*/ + } + else + { + // enable only interrupts for RX here. Tx is handled in EMasterWrite case above. + AsspRegister::Write32(iHwBase + MCSPI_IRQSTATUS, MCSPI_IRQ_RX_FULL(iCurrSS)); + AsspRegister::Write32(iHwBase + MCSPI_IRQENABLE, MCSPI_IRQ_RX_FULL(iCurrSS)); + } + + DEBUG_ONLY(DumpCurrentStatus("DoTransfer(Read)")); + // and enable system interrupts + if(!iFullDTransfer) + Interrupt::Enable(iIrqId); + break; + } + default: + { + DBG_ERR(Kern::Printf("Unsupported TransactionType %x", aType)); + r = KErrArgument; + break; + } + } + + return r; + } + +#ifdef _DEBUG +static TInt IsrCnt = 0; +void DSpiMasterBeagle::DumpCurrentStatus(const TInt8* aWhere /*=NULL*/) + { + if(aWhere) + Kern::Printf("------ Status (%s)--------", aWhere); + else + Kern::Printf("------ Status --------"); + Kern::Printf("\niTransactionStatus: %d", iTransactionStatus); + Kern::Printf("iTransferEndDfc %s queued", iTransferEndDfc.Queued() ? "" : "NOT"); + + if(iOperation.iOp.iIsTransmitting) + { + Kern::Printf("TX STATUS:"); + Kern::Printf(" iTxData %x", iTxData); + Kern::Printf(" iTxDataEnd %x", iTxDataEnd); + Kern::Printf(" left to write: %x (words)", (iTxDataEnd - iTxData)/iWordSize); + } + + if(iOperation.iOp.iIsReceiving) + { + Kern::Printf("RX STATUS:"); + Kern::Printf(" iRxData %x", iRxData); + Kern::Printf(" iRxDataEnd %x", iRxDataEnd); + Kern::Printf(" left to read: %x (words)", (iRxDataEnd - iRxData)/iWordSize); + } + Kern::Printf(" iCurrSS %d",iCurrSS); + + Kern::Printf("IsrCnt %d", IsrCnt); + TUint status = AsspRegister::Read32(iHwBase + MCSPI_IRQSTATUS); + Kern::Printf("MCSPI_IRQSTATUS (0x%x):", status); + if(status & MCSPI_IRQ_TX_EMPTY(iCurrSS)) + Kern::Printf(" MCSPI_IRQ_TX_EMPTY"); + if(status & MCSPI_IRQ_TX_UNDERFLOW(iCurrSS)) + Kern::Printf(" MCSPI_IRQ_TX_UNDERFLOW"); + if(!iCurrSS && status & MCSPI_IRQ_RX_OVERFLOW) + Kern::Printf(" MCSPI_IRQ_RX_OVERFLOW"); + if(status & MCSPI_IRQ_RX_FULL(iCurrSS)) + Kern::Printf(" MCSPI_IRQ_RX_FULL"); + + Kern::Printf("MCSPI_CHxSTAT(%d):", iCurrSS); + status = AsspRegister::Read32(iHwBase + MCSPI_CHxSTAT(iCurrSS)); + if(status & MCSPI_CHxSTAT_RXFFF) + Kern::Printf(" MCSPI_CHxSTAT_RXFFF"); + if(status & MCSPI_CHxSTAT_RXFFE) + Kern::Printf(" MCSPI_CHxSTAT_RXFFE"); + if(status & MCSPI_CHxSTAT_TXFFF) + Kern::Printf(" MCSPI_CHxSTAT_TXFFF"); + if(status & MCSPI_CHxSTAT_TXFFE) + Kern::Printf(" MCSPI_CHxSTAT_TXFFE"); + if(status & MCSPI_CHxSTAT_EOT) + Kern::Printf(" MCSPI_CHxSTAT_EOT"); + if(status & MCSPI_CHxSTAT_TXS) + Kern::Printf(" MCSPI_CHxSTAT_TXS"); + if(status & MCSPI_CHxSTAT_RXS) + Kern::Printf(" MCSPI_CHxSTAT_RXS"); + + Kern::Printf("MCSPI_XFERLEVEL:"); + status = AsspRegister::Read32(iHwBase + MCSPI_XFERLEVEL); + Kern::Printf(" MCSPI_XFERLEVEL_WCNT %d", status >> MCSPI_XFERLEVEL_WCNT_OFFSET); + Kern::Printf(" MCSPI_XFERLEVEL_AFL %d", (status >> MCSPI_XFERLEVEL_AFL_OFFSET) & 0x3F); + Kern::Printf(" MCSPI_XFERLEVEL_AEL %d\n", (status >> MCSPI_XFERLEVEL_AEL_OFFSET) & 0x1F); + Kern::Printf("---------------------------------------/*\n\n\n"); + } +#endif + +void DSpiMasterBeagle::Isr(TAny* aPtr) + { + DSpiMasterBeagle *a = (DSpiMasterBeagle*) aPtr; + DEBUG_ONLY(IsrCnt++); + DEBUG_ONLY(a->DumpCurrentStatus("Isr entry")); + + TUint32 status = AsspRegister::Read32(a->iHwBase + MCSPI_IRQSTATUS); + AsspRegister::Write32(a->iHwBase + MCSPI_IRQSTATUS, status); // clear status bits.. + + // TX_EMPTY - when an item (or number of items if FIFO is used) was transmitted.. + if(status & MCSPI_IRQ_TX_EMPTY(a->iCurrSS)) + { + + if(a->iOperation.iOp.iIsTransmitting) + { +#ifdef USE_TX_FIFO + // when FIFO is used - should write (at least) the MCSPI_XFERLEVEL_AFL + 1 words to this register.. + while(a->iTxData != a->iTxDataEnd) + { + AsspRegister::Write32(a->iHwBase + MCSPI_TXx(a->iCurrSS), *a->iTxData); + a->iTxData += a->iWordSize; // Then increment the pointer to the data.s + + if(AsspRegister::Read32(a->iHwBase + MCSPI_CHxSTAT(a->iCurrSS)) & MCSPI_CHxSTAT_TXFFF) + { + break; + } + } +#else + // transfer next word.. + if(a->iTxData != a->iTxDataEnd) + { + TUint val = 0; + for (TInt i = 0; i < a->iWordSize; i++) + { + val |= (*a->iTxData++) << i * 8; + } + AsspRegister::Write32(a->iHwBase + MCSPI_TXx(a->iCurrSS), val); + } + + // check again - if this was the last one..and we're not waiting for rx - end transfer + if(a->iTxData == a->iTxDataEnd && !a->iOperation.iOp.iIsReceiving) + { + Interrupt::Disable(a->iIrqId); + a->iTransferEndDfc.Add(); + } +#endif + } + else + { + // writing a 'dummy' word (for read only transferss (writing 0 doesn't change line state) + AsspRegister::Write32(a->iHwBase + MCSPI_TXx(a->iCurrSS), 0); + } + } + + if(status & MCSPI_IRQ_RX_FULL(a->iCurrSS)) + { + if(a->iOperation.iOp.iIsReceiving) + { + if(a->iRxDataEnd != a->iRxData) + { + TUint8 nextRxValue = AsspRegister::Read32(a->iHwBase + MCSPI_RXx(a->iCurrSS)); + *a->iRxData = nextRxValue; + a->iRxData += a->iWordSize; + } + + // If the Rx buffer is now full, finish the transmission. + if(a->iRxDataEnd == a->iRxData) + { + Interrupt::Disable(a->iIrqId); + a->iTransferEndDfc.Add(); + } + } + } + +#if 0 // TODO - probably master, as it creates CLK for slave - will never have to bother with this.. + if(status & MCSPI_IRQ_TX_UNDERFLOW(a->iCurrSS)) + { + DBG_ERR(Kern::Printf("Underflow")); + a->iTransactionStatus = KErrUnderflow; + + // disable the channel.. + AsspRegister::Modify32(a->iHwBase + MCSPI_CHxCTRL(0), MCSPI_CHxCTRL_EN, 0); + Interrupt::Disable(a->iIrqId); + DEBUG_ONLY(a->DumpCurrentStatus("TxUnderflow")); + DBG_ERR(Kern::Fault("TxUnderflow", 0)); + } +#endif +#if defined(USE_TX_FIFO) && defined(USING_TX_COUNTER) + if(status & MCSPI_IRQSTATUS_EOW) + { + Kern::Printf("EOW"); + // TODO: end of transfer.. + } +#endif + + // end of ISR processing + DEBUG_ONLY(a->DumpCurrentStatus("Isr end")); + } + +void DSpiMasterBeagle::TransferEndDfc(TAny* aPtr) + { + DBGPRINT(Kern::Printf("DSpiMasterBeagle::TransferEndDfc")); + DSpiMasterBeagle *a = (DSpiMasterBeagle*) aPtr; + + TUint chanStatus = AsspRegister::Read32(a->iHwBase + MCSPI_CHxSTAT(a->iCurrSS)); + if(a->iOperation.iOp.iIsTransmitting) + { + TUint expected = MCSPI_CHxSTAT_EOT | MCSPI_CHxSTAT_TXS; + +#ifdef USE_TX_FIFO + while(!AsspRegister::Read32(a->iHwBase + MCSPI_CHxSTAT(a->iCurrSS)) & MCSPI_CHxSTAT_TXFFE); +#endif + while(chanStatus & expected != expected) + { + chanStatus = AsspRegister::Read32(a->iHwBase + MCSPI_CHxSTAT(a->iCurrSS)); + } + } + + if(a->iOperation.iOp.iIsReceiving) + { + TUint expected = MCSPI_CHxSTAT_RXS; + + while(chanStatus & expected != expected) + { + chanStatus = AsspRegister::Read32(a->iHwBase + MCSPI_CHxSTAT(a->iCurrSS)); + } + __ASSERT_DEBUG(a->iRxDataEnd == a->iRxData, + Kern::Fault("SPI master: exiting not having received all?", 12)); + } + + // make sure the CS pin is asserted.. + if(a->iCurrHeader.iSSPinActiveMode == ESpiCSPinActiveLow) + { + AsspRegister::Modify32(a->iHwBase + MCSPI_SYST, 0, 1 << a->iCurrSS); + } + else + { + AsspRegister::Modify32(a->iHwBase + MCSPI_SYST, 1 << a->iCurrSS, 0); + } + +#ifdef SINGLE_MODE + // manually de-assert CS line for this channel + AsspRegister::Modify32(a->iHwBase + MCSPI_CHxCONF(a->iCurrSS), MCSPI_CHxCONF_FORCE, 0); + + // drive csx pin high or low. Doing this here causes, that CS lines are toggled for each transfers. + TUint val = (a->iCurrHeader.iSSPinActiveMode == ESpiCSPinActiveLow)? 1 << a->iCurrSS : 0; + if (val) + { + AsspRegister::Modify32(a->iHwBase + MCSPI_SYST, 0, val); + } + // put the CS signal to 'inactive' state (as on channel disable it would have a glitch) + SetCsInactive(a->iChannelNumber, a->iCurrSS, a->iCurrHeader.iSSPinActiveMode); + +#endif + + // disable the channel + AsspRegister::Modify32(a->iHwBase + MCSPI_CHxCTRL(0), MCSPI_CHxCTRL_EN, 0); + + // Start the next transfer for this transaction, if any remain + if(a->iState == EBusy) + { + TInt err = a->ProcessNextTransfers(); + if(err != KErrNone) + { + // If the next transfer could not be started, complete the transaction with + // the returned error code + a->ExitComplete(err); + } + } + } + +void DSpiMasterBeagle::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/) + { + DBGPRINT(Kern::Printf("DSpiMasterBeagle::ExitComplete, aErr %d, aComplete %d", aErr, aComplete)); + + // make sure CS is in inactive state (for the current / last transaction) on error + if(!aComplete) + { + SetCsInactive(iChannelNumber, iCurrSS, iCurrHeader.iSSPinActiveMode); + } + + // disable this channel (and reset..) + AsspRegister::Modify32(iHwBase + MCSPI_CHxCTRL(iCurrSS), MCSPI_CHxCTRL_EN, 0); + AsspRegister::Write32(iHwBase + MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); + + // Disable interrupts for the channel + Interrupt::Disable(iIrqId); + + // Cancel any timers and DFCs.. + CancelTimeOut(); + iTransferEndDfc.Cancel(); + + // Change the channel state back to EIdle + iState = EIdle; + + // Call the PIL method to complete the request + if(aComplete) + { + CompleteRequest(aErr); + } + } + +#ifdef _DEBUG +void DumpHeader(TConfigSpiV01& aHeader) + { + Kern::Printf("header:"); + Kern::Printf("iWordWidth %d (%d bits)", aHeader.iWordWidth, (SpiWordWidth(aHeader.iWordWidth)) >> MCSPI_CHxCONF_WL_OFFSET + 1); + Kern::Printf("iClkSpeedHz %d", aHeader.iClkSpeedHz); + Kern::Printf("iClkMode %d", aHeader.iClkMode); + Kern::Printf("iTimeoutPeriod %d", aHeader.iTimeoutPeriod); + Kern::Printf("iBitOrder %d", aHeader.iBitOrder); + Kern::Printf("iTransactionWaitCycles %d", aHeader.iTransactionWaitCycles); + Kern::Printf("iSSPinActiveMode %d", aHeader.iSSPinActiveMode); + } +#endif + +// virtual method called by the PIL when a transaction is queued (with QueueTransaction). +// This is done in the context of the Client's thread. +// The PSL is required to check that the transaction header is valid for this channel. +TInt DSpiMasterBeagle::CheckHdr(TDes8* aHdrBuff) + { + TInt r = KErrNone; + if(!aHdrBuff) + { + r = KErrArgument; + } + else + { + TConfigSpiV01 &header = (*(TConfigSpiBufV01*) (aHdrBuff))(); + + // check if word width and clock are supported + if(SpiWordWidth(header.iWordWidth) < KMinSpiWordWidth || + SpiClkValue(header.iClkSpeedHz) < 0 || // == KErrNotSupported + header.iBitOrder == ELsbFirst) // this SPI only transmits MSB fist + { +#ifdef _DEBUG + if(header.iBitOrder == ELsbFirst) + DBG_ERR(Kern::Printf("iClkSpeedHz value (%d) is not supported", header.iClkSpeedHz)); + if(SpiClkValue(header.iClkSpeedHz) < 0) + DBG_ERR(Kern::Printf("iClkSpeedHz: %d is not supported", header.iClkSpeedHz)); + if((SpiWordWidth(header.iWordWidth)+ 1) >> MCSPI_CHxCONF_WL_OFFSET < KMinSpiWordWidth) + DBG_ERR(Kern::Printf("iWordWidth: %d is not supported, min value is: %d", + SpiWordWidth(header.iWordWidth), KMinSpiWordWidth)); + DumpHeader(header); +#endif + r = KErrNotSupported; + DBG_ERR(Kern::Printf("DSpiMasterBeagle::CheckHdr()failed, r = %d", r)); + } + } + return r; + } + +// This method is called by the PIL in the case of expiry of a timer for a transaction. +// TODO: this name is confusing - it could be changed in the PIL to reflect it's real purpose(TBD) +// It has NOTHING to do with a Slave (i.e. slave might be completely silent for SPI-and master won't notice it!) +TInt DSpiMasterBeagle::HandleSlaveTimeout() + { + DBG_ERR(Kern::Printf("HandleSlaveTimeout")); + + // Stop the PSL's operation, and inform the PIL of the timeout + ExitComplete(KErrTimedOut, EFalse); + + return KErrTimedOut; + } + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/master.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/master.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,93 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// omap3530/omap3530_drivers/spi/master.h +// + + +#ifndef __OMAP3530_SPI_MASTER_H__ +#define __OMAP3530_SPI_MASTER_H__ + +#include + +_LIT(KIicPslThreadName,"SpiChannelThread_"); +const TInt KIicPslDfcPriority = 0; +const TInt KIicPslThreadPriority = 24; + +// class declaration for SPI master +class DSpiMasterBeagle : public DIicBusChannelMaster + { +public: + static DSpiMasterBeagle* New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex); + virtual TInt DoRequest(TIicBusTransaction* aTransaction); // Gateway function for PSL implementation + +private: + DSpiMasterBeagle(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex); + + // Override base-class pure virtual methods + virtual TInt DoCreate(); + virtual TInt CheckHdr(TDes8* aHdr); + virtual TInt HandleSlaveTimeout(); + + // Internal methods + TInt ConfigureInterface(); + TBool TransConfigDiffersFromPrev(); + TInt ProcessNextTransfers(); + TInt StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType); + TInt DoTransfer(TUint8 aType); + static void Isr(TAny* aPtr); + static void TransferEndDfc(TAny* aPtr); + void ExitComplete(TInt aErr, TBool aComplete = ETrue); + +#ifdef _DEBUG + void DumpCurrentStatus(const TInt8* aWhere = NULL); +#endif + +private: + TDfc iTransferEndDfc; + TIicOperationType iOperation; + TUint8 iWordSize; + + TInt8 iTxFifoThreshold; + enum TMyState + { + EIdle, + EBusy + }; + TMyState iState; + + TInt iIrqId; + TLinAddr iHwBase; + + // Pointers used to store current transfers information + TIicBusTransfer* iHalfDTransfer; + TIicBusTransfer* iFullDTransfer; + + // Pointer to the current transaction. + TIicBusTransaction* iCurrTransaction; + + // Pointers to buffers used for Rx and Tx transfers + TInt8 *iTxData; + TInt8 *iRxData; + TInt8 *iTxDataEnd; + TInt8 *iRxDataEnd; + + // global status of the transaction + volatile TInt iTransactionStatus; + + TConfigSpiV01 iCurrHeader; + TInt iCurrSS; + }; + +#endif //__OMAP3530_SPI_MASTER_H__ diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/omap3530_spi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/omap3530_spi.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,450 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530/omap3530_drivers/spi/omap3530_spi.h +// +// This file contains definitions to internal SPI implementation and register definitions +// It is not intended to be exported - SPI registers must not be modified from outside of +// the driver! +// + +#ifndef __OMAP3530_SPI_H__ +#define __OMAP3530_SPI_H__ + +#include + + +#define BIT_MASK(shift,len) (((1u << (len)) - 1) << (shift)) +#define GET_BITS(w,shift,len) (((w) >> (shift)) & ((1 << (len)) - 1)) +#define SET_BITS(w,set,shift,len) ((w) &= ~BIT_MASK(shift, len), (w) |= ((set) << (shift))) + + +// Device Instance Summary +const TUint MCSPI1_phys = 0x48098000; // 4Kbytes +const TUint MCSPI2_phys = 0x4809A000; // 4Kbytes +const TUint MCSPI3_phys = 0x480B8000; // 4Kbytes +const TUint MCSPI4_phys = 0x480BA000; // 4Kbytes + +const TUint MCSPI1 = Omap3530HwBase::TVirtual::Value; +const TUint MCSPI2 = Omap3530HwBase::TVirtual::Value; +const TUint MCSPI3 = Omap3530HwBase::TVirtual::Value; +const TUint MCSPI4 = Omap3530HwBase::TVirtual::Value; + +// map of SPI base addresses.. +const TUint KMcSpiRegBase[] = + { + Omap3530HwBase::TVirtual::Value, //McSPI module 1 + Omap3530HwBase::TVirtual::Value, //McSPI module 2 + Omap3530HwBase::TVirtual::Value, //McSPI module 3 + Omap3530HwBase::TVirtual::Value //McSPI module 4 + }; + + +//.. and IRQ lines for SPI channels +const TUint KMcSpiIrqId[] = + { + EOmap3530_IRQ65_SPI1_IRQ, //McSPI module 1 + EOmap3530_IRQ66_SPI2_IRQ, //McSPI module 2 + EOmap3530_IRQ91_SPI3_IRQ, //McSPI module 3 + EOmap3530_IRQ48_SPI4_IRQ //McSPI module 4 + }; + +// available channels per module (i.e. number of 'slave select' inputs/outpus per module) +const TUint KMcSpiNumChannels[] = + { + 4, // channels 0 - 3 + 2, // channels 0 - 1 + 2, // channels 0 - 1 + 1 // channel 0 only + }; + +//--------------------------------------------------------------- +// MCSPI Registers offsets and bits definitions +//---------------------------------- +// MCSPI_REVISION +//---------------------------------- +const TUint MCSPI_REVISION = 0x00; + + +//---------------------------------- +// MCSPI_SYSCONFIG +//---------------------------------- +const TUint MCSPI_SYSCONFIG = 0x10; + +// 9:8 CLOCKACTIVITY Clocks activity during wake up mode period +//0x0: Interface and Functional clocks may be switched off. +//0x1: Interface clock is maintained. Functional clock may be switched off. +//0x2: Functional clock is maintained. Interface clock may be switched off. +//0x3: Interface and Functional clocks are maintained. +const TUint MCSPI_SYSCONFIG_CLOCKACTIVITY_ALL_OFF = 0 << 8; +const TUint MCSPI_SYSCONFIG_CLOCKACTIVITY_INT_ON_FUN_OFF = 1 << 8; +const TUint MCSPI_SYSCONFIG_CLOCKACTIVITY_INT_OFF_FUN_ON = 2 << 8; +const TUint MCSPI_SYSCONFIG_CLOCKACTIVITY_ALL_ON = 3 << 8; + +// 4:3 SIDLEMODE Power management +const TUint MCSPI_SYSCONFIG_SIDLEMODE_ALWAYS = 0 << 3; +const TUint MCSPI_SYSCONFIG_SIDLEMODE_IGNORE = 1 << 3; +const TUint MCSPI_SYSCONFIG_SIDLEMODE_COND = 2 << 3; +const TUint MCSPI_SYSCONFIG_ENAWAKEUP = 1 << 2; // 0x1: Wake-up capability enabled +const TUint MCSPI_SYSCONFIG_SOFTRESET = 1 << 1; // Software reset. Read always returns 0. +const TUint MCSPI_SYSCONFIG_AUTOIDLE = 1 << 0; // Internal interface Clock gating strategy + + +//---------------------------------- +// MCSPI_SYSSTATUS +//---------------------------------- +const TUint MCSPI_SYSSTATUS = 0x14; +const TUint MCSPI_SYSSTATUS_RESETDONE = 1 << 0; + + +//---------------------------------- +// MCSPI_IRQSTATUS +//---------------------------------- +const TUint MCSPI_IRQSTATUS = 0x18; // for each bit- write 0x1: reset status, Read 0x1: Event is pending. +const TUint MCSPI_IRQSTATUS_EOW = 1 << 17; // End of word count event when a channel is enabled using +const TUint MCSPI_IRQSTATUS_WKS = 1 << 16; // Wake-up event in slave mode when an active control signal is detected +const TUint MCSPI_IRQSTATUS_RX3_FULL = 1 << 14; // MCSPI_RX3 register is full (only when channel 3 is enabled) +const TUint MCSPI_IRQSTATUS_TX3_UDERFLOW = 1 << 13; // MCSPI_TX3 register underflow (only when channel 3 is enabled)(1) +const TUint MCSPI_IRQSTATUS_TX3_EMPTY = 1 << 12; // MCSPI_TX3 register is empty (only when channel 3 is enabled)(2) +const TUint MCSPI_IRQSTATUS_RX2_FULL = 1 << 10; // MCSPI_RX2 register full (only when channel 2 is enabled) +const TUint MCSPI_IRQSTATUS_TX2_UNDERFLOW = 1 << 9; // MCSPI_TX2 register underflow (only when channel 2 is enabled)(1) +const TUint MCSPI_IRQSTATUS_TX2_EMPTY = 1 << 8; // MCSPI_TX2 register empty (only when channel 2 is enabled)(2) +const TUint MCSPI_IRQSTATUS_RX1_FULL = 1 << 6; // MCSPI_RX1 register full (only when channel 1 is enabled) +const TUint MCSPI_IRQSTATUS_TX1_UNDERFLOW = 1 << 5; // MCSPI_TX1 register underflow (only when channel 1 is enabled)(1) +const TUint MCSPI_IRQSTATUS_TX1_EMPTY = 1 << 4; // MCSPI_TX1 register empty (only when channel 1 is enabled)(3) +const TUint MCSPI_IRQSTATUS_RX0_OVERFLOW = 1 << 3; // MCSPI_RX0 register overflow (only in slave mode) +const TUint MCSPI_IRQSTATUS_RX0_FULL = 1 << 2; // MCSPI_RX0 register full (only when channel 0 is enabled) +const TUint MCSPI_IRQSTATUS_TX0_UNDERFLOW = 1 << 1; // MCSPI_TX0 register underflow (only when channel 0 is +const TUint MCSPI_IRQSTATUS_TX0_EMPTY = 1 << 0; // MCSPI_TX0 register empty (only when channel 0 is enabled)(3) + +//---------------------------------- +// MCSPI_IRQENABLE +//---------------------------------- +//0x0: Interrupt disabled +//0x1: Interrupt enabled +const TUint MCSPI_IRQENABLE = 0x1C; +const TUint MCSPI_IRQENABLE_EOWKE = 1 << 17; // End of Word count Interrupt Enable. +const TUint MCSPI_IRQENABLE_WKE = 1 << 16; // Wake-up event interrupt enable in slave mode when an +const TUint MCSPI_IRQENABLE_RX3_FULL = 1 << 14; // MCSPI_RX3 register full interrupt enable (channel 3) +const TUint MCSPI_IRQENABLE_TX3_UNDERFLOW = 1 << 13; // MCSPI_TX3 register underflow interrupt enable (channel 3) +const TUint MCSPI_IRQENABLE_TX3_EMPTY = 1 << 12; // MCSPI_TX3 register empty interrupt enable (channel 3) +const TUint MCSPI_IRQENABLE_RX2_FULL = 1 << 10; // MCSPI_RX2 register full interrupt enable (channel 2) +const TUint MCSPI_IRQENABLE_TX2_UNDERFLOW = 1 << 9; // MCSPI_TX2 register underflow interrupt enable (channel 2) +const TUint MCSPI_IRQENABLE_TX2_EMPTY = 1 << 8; // MCSPI_TX2 register empty interrupt enable (channel 2) +const TUint MCSPI_IRQENABLE_RX1_FULL = 1 << 6; // MCSPI_RX1 register full interrupt enable (channel 1) +const TUint MCSPI_IRQENABLE_TX1_UNDERFLOW = 1 << 5; // MCSPI_TX1 register underflow interrupt enable (channel 1) +const TUint MCSPI_IRQENABLE_TX1_EMPTY = 1 << 4; // MCSPI_TX1 register empty interrupt enable (channel 1) +const TUint MCSPI_IRQENABLE_RX0_OVERFLOW = 1 << 3; // MCSPI_RX0 register overflow interrupt enable (channel 0) (only Slave) +const TUint MCSPI_IRQENABLE_RX0_FULL = 1 << 2; // MCSPI_RX0 register full interrupt enable (channel 0) +const TUint MCSPI_IRQENABLE_TX0_UNDERFLOW = 1 << 1; // MCSPI_TX0 register underflow interrupt enable (channel 0) +const TUint MCSPI_IRQENABLE_TX0_EMPTY = 1 << 0; // MCSPI_TX0 register empty interrupt enable (channel 0) + +// macros to get these flags depending on the channel number..and ommited ENABLE +// - as they are the same for MCSPI_IRQSTATUS register +#define MCSPI_IRQ_RX_OVERFLOW MCSPI_IRQENABLE_RX0_OVERFLOW // Channel 0 only / slave mode only +#define MCSPI_IRQ_RX_FULL(chan) (MCSPI_IRQENABLE_RX0_FULL << ((chan)*4)) +#define MCSPI_IRQ_TX_UNDERFLOW(chan) (MCSPI_IRQENABLE_TX0_UNDERFLOW << ((chan)*4)) +#define MCSPI_IRQ_TX_EMPTY(chan) (MCSPI_IRQENABLE_TX0_EMPTY << ((chan)*4)) + + +//---------------------------------- +// MCSPI_WAKEUPENABL +//---------------------------------- +const TUint MCSPI_WAKEUPENABL = 0x20; +const TUint MCSPI_WAKEUPENABL_WKEN = 1 << 0; //0x1: The event is allowed to wake-up the system + + +//---------------------------------- +// MCSPI_SYST +//---------------------------------- +const TUint MCSPI_SYST = 0x24; +const TUint MCSPI_SYST_SSB = 1 << 11; // Set status bit: 0x1: Force to 1 all status bits of MCSPI_ IRQSTATUS +const TUint MCSPI_SYST_SPIENDIR = 1 << 10; // spim_cs and spim_clk direction: 0x0: Output (as in master mode), 0x1: Input (as in slave mode) +const TUint MCSPI_SYST_SPIDATDIR1 = 1 << 9; // SPIDAT[1] (spim_simo) direction- 0x0: Output, 0x1: Input +const TUint MCSPI_SYST_SPIDATDIR0 = 1 << 8; // Set the direction of the SPIDAT[0] (spim_somi) RW 0 +const TUint MCSPI_SYST_WAKD = 1 << 7; // SWAKEUP output 0x0: The pin is driven low, 0x1: The pin is driven high. +const TUint MCSPI_SYST_SPICLK = 1 << 6; // spim_clk line (signal data value) RW 0 +const TUint MCSPI_SYST_SPIDAT_1 = 1 << 5; // spim_somi line (signal data value) RW 0 +const TUint MCSPI_SYST_SPIDAT_0 = 1 << 4; // spim_simo line (signal data value) +const TUint MCSPI_SYST_SPIEN_3 = 1 << 3; // spim_cs3 line (signal data value) RW 0 +const TUint MCSPI_SYST_SPIEN_2 = 1 << 2; // spim_cs2 line (signal data value) RW 0 +const TUint MCSPI_SYST_SPIEN_1 = 1 << 1; // spim_cs1 line (signal data value) RW 0 +const TUint MCSPI_SYST_SPIEN_0 = 1 << 0; // spim_cs0 line (signal data value) RW 0 + + +//---------------------------------- +// MCSPI_MODULCTRL +//---------------------------------- +const TUint MCSPI_MODULCTRL = 0x28; +const TUint MCSPI_MODULCTRL_SYSTEM_TEST = 1 << 3; // 0x0: Functional mode, 0x1: System test mode (SYSTEST) +const TUint MCSPI_MODULCTRL_MS_SLAVE = 1 << 2; // Master / Slave mode 0x0: Master, 0x1: Slave +const TUint MCSPI_MODULCTRL_MS_MASTER = 0; // this is spurious definition -> not to write '0' magic number -defined this.. +const TUint MCSPI_MODULCTRL_SINGLE = 1 << 0; // Single forced channel/multichannel (master mode only) + // MCSPI_CHxCONF_FORCE bit has to be set in this mode, TURBO cleared (recomended) + + +//---------------------------------- +// MCSPI_CHxCONF - channel config +//---------------------------------- +// x = 0 to 3 for MCSPI1. +// x = 0 to 1 for MCSPI2 and MCSPI3. +// x = 0 for MCSPI4. +#define MCSPI_CHxCONF(x) (0x2C + 0x14 * (x)) +const TUint MCSPI_CHxCONF_CLKG = 1 << 29; // Clock divider granularity. +const TUint MCSPI_CHxCONF_FFER = 1 << 28; // FIFO enabled for Receive. +const TUint MCSPI_CHxCONF_FFEW = 1 << 27; // FIFO enabled for Transmit. Only one channel can have this bit field set. + +// [26:25] TCS Chip select time control +// Defines the number of interface clock cycles between CS toggling and first (or last) edge of SPI clock. +const TUint MCSPI_CHxCONF_TCS_0_5 = 0 << 25; // 0x0: 0.5 clock cycle +const TUint MCSPI_CHxCONF_TCS_1_5 = 1 << 25; // 0x1: 1.5 clock cycles +const TUint MCSPI_CHxCONF_TCS_2_5 = 2 << 25; // 0x2: 2.5 clock cycles +const TUint MCSPI_CHxCONF_TCS_3_5 = 3 << 25; // 0x3: 3.5 clock cycles + +const TUint MCSPI_CHxCONF_SBPOL = 1 << 24; // Start bit polarity (0: spi word is command, 1: spi word is data) +const TUint MCSPI_CHxCONF_SBE = 1 << 23; // Start bit enable - 0x1: Start bit D/CX added before transfer. +const TUint MCSPI_CHxCONF_FORCE = 1 << 20; // Manual spim_csx assertion to keep spim_csx active between SPI words. + // (single channel master mode only)- MCSPI_MODULCTRL_SINGLE has to be set +const TUint MCSPI_CHxCONF_TURBO = 1 << 19; // Turbo mode +const TUint MCSPI_CHxCONF_IS = 1 << 18; // Input select- 0x0: (spim_somi), 0x1: (spim_simo) selected for reception +const TUint MCSPI_CHxCONF_DPE1 = 1 << 17; // Transmission enable for data line 1 (spim_simo) RW 0x1 +const TUint MCSPI_CHxCONF_DPE0 = 1 << 16; // Transmission enable for data line 0 (spim_somi) +const TUint MCSPI_CHxCONF_DMAR = 1 << 15; // DMA Read request +const TUint MCSPI_CHxCONF_DMAW = 1 << 14; // DMA Write request. + +// 13:12 TRM Transmit/receive modes +const TUint MCSPI_CHxCONF_TRM_TRANSMIT_RECEIVE = 0 << 12; +const TUint MCSPI_CHxCONF_TRM_RECEIVE_ONLY = 1 << 12; +const TUint MCSPI_CHxCONF_TRM_TRANSMIT_ONLY = 2 << 12; +// these are to be cleared in the register +const TUint MCSPI_CHxCONF_TRM_NO_TRANSMIT = 1 << 12; +const TUint MCSPI_CHxCONF_TRM_NO_RECEIVE = 2 << 12; + + +// 11:7 WL SPI word length0 +// values:<0-3> reserved, allowed:<4-31> => word_size = value + 1 (i.e. for 4: word size = 5) +const TInt KMinSpiWordWidth = 5; +const TUint MCSPI_CHxCONF_WL_OFFSET = 7; +#define MCSPI_CHxCONF_WL(x) ( (((x) - 1) & BIT_MASK(0, 5)) << MCSPI_CHxCONF_WL_OFFSET ) + +const TUint MCSPI_CHxCONF_EPOL_LOW = 1 << 6; // spim_csx polarity 0x0: active high, 0x1: active low + +// A programmable clock divider divides the SPI reference clock +//5:2 CLKD Frequency divider for spim_clk (for master device only) +const TUint MCSPI_CHxCONF_CLKD_48M = 0x0 << 2; //0x0: 1 = 48 MHz +const TUint MCSPI_CHxCONF_CLKD_24M = 0x1 << 2; //0x1: 2 = 24 MHz +const TUint MCSPI_CHxCONF_CLKD_12M = 0x2 << 2; //0x2: 4 = 12 MHz +const TUint MCSPI_CHxCONF_CLKD_6M = 0x3 << 2; //0x3: 8 = 6 MHz +const TUint MCSPI_CHxCONF_CLKD_3M = 0x4 << 2; //0x4: 16 = 3 MHz +const TUint MCSPI_CHxCONF_CLKD_1500k = 0x5 << 2; //0x5: 32 = 1.5 MHz +const TUint MCSPI_CHxCONF_CLKD_750k = 0x6 << 2; //0x6: 64 = 750 kHz +const TUint MCSPI_CHxCONF_CLKD_375k = 0x7 << 2; //0x7: 128 = 375 kHz +const TUint MCSPI_CHxCONF_CLKD_187k = 0x8 << 2; //0x8: 256 = 187.5 kHz +const TUint MCSPI_CHxCONF_CLKD_93k = 0x9 << 2; //0x9: 512 = 93.75 kHz +const TUint MCSPI_CHxCONF_CLKD_46k = 0xA << 2; //0xA: 1024 = 46.875 kHz +const TUint MCSPI_CHxCONF_CLKD_23k = 0xB << 2; //0xB: 2048 = 23.437,5 kHz +const TUint MCSPI_CHxCONF_CLKD_11k = 0xC << 2; //0xC: 4096 = 11.718,75 kHz +const TUint MCSPI_K48MHz = 48000000; + +const TUint MCSPI_CHxCONF_POL = 1 << 1; // spim_clk polarity 0x0: active high, 0x1: active low +const TUint MCSPI_CHxCONF_PHA = 1 << 0; // spim_clk phase +// 0x0: Data are latched on odd-numbered edges of spim_clk. +// 0x1: Data are latched on even-numbered edges of spim_clk. + + +//---------------------------------- +// MCSPI_CHxSTAT +//---------------------------------- +// x = 0 to 3 for MCSPI1. +// x = 0 to 1 for MCSPI2 and MCSPI3. +// x = 0 for MCSPI4. +#define MCSPI_CHxSTAT(x) (0x30 + 0x14 * (x)) +const TUint MCSPI_CHxSTAT_RXFFF = 1 << 6; // Channel x FIFO Receive Buffer Full +const TUint MCSPI_CHxSTAT_RXFFE = 1 << 5; // Channel x FIFO Receive Buffer Empty +const TUint MCSPI_CHxSTAT_TXFFF = 1 << 4; // Channel x FIFO Transmit Buffer Full +const TUint MCSPI_CHxSTAT_TXFFE = 1 << 3; // Channel x FIFO Transmit Buffer Empty +const TUint MCSPI_CHxSTAT_EOT = 1 << 2; // Channel x end-of-transfer status. +const TUint MCSPI_CHxSTAT_TXS = 1 << 1; // Channel x MCSPI_TXx register status R 0x0 +const TUint MCSPI_CHxSTAT_RXS = 1 << 0; // Channel x MCSPI_RXx register status R 0x0 + + +//---------------------------------- +// MCSPI_CHxCTRL +//---------------------------------- +// x = 0 to 3 for MCSPI1. +// x = 0 to 1 for MCSPI2 and MCSPI3. +// x = 0 for MCSPI4. +#define MCSPI_CHxCTRL(x) (0x34 + 0x14 * (x)) +//15:8 EXTCLK Clock ratio extension: This register is used to concatenate with RW 0x00 +const TUint MCSPI_CHxCTRL_EXTCLK_1 = 0x00 << 8; //0x0: Clock ratio is CLKD + 1 +const TUint MCSPI_CHxCTRL_EXTCLK_1_16 = 0x01 << 8; //0x1: Clock ratio is CLKD + 1 + 16 +const TUint MCSPI_CHxCTRL_EXTCLK_1_4080 = 0xff << 8; //0xFF: Clock ratio is CLKD + 1 + 4080 +const TUint MCSPI_CHxCTRL_EN = 0x01 << 0; // Channel enable + + +//---------------------------------- +// MCSPI_TXx - Channel x Data to transmit +//---------------------------------- +// x = 0 to 3 for MCSPI1. +// x = 0 to 1 for MCSPI2 and MCSPI3. +// x = 0 for MCSPI4. +#define MCSPI_TXx(x) (0x38 + 0x14 * (x)) // Channel x Data to transmit + + +//---------------------------------- +// MCSPI_RXx - Channel x Received Data +//---------------------------------- +// x = 0 to 3 for MCSPI1. +// x = 0 to 1 for MCSPI2 and MCSPI3. +// x = 0 for MCSPI4. +#define MCSPI_RXx(x) (0x3C + 0x14 * (x)) // Channel x Received Data + + +//---------------------------------- +// MCSPI_XFERLEVEL +//---------------------------------- +const TUint MCSPI_XFERLEVEL = 0x7C; +const TUint MCSPI_XFERLEVEL_WCNT_OFFSET = 16; // [31:16] WCNT Spi word counter -> how many bytes are transfered to FIFO before tx is enabled +const TUint MCSPI_XFERLEVEL_AFL_OFFSET = 8; // 13:8 AFL Buffer Almost Full. 0x0: One byte , 0x1: 2 bytes, x3E: 63 bytes.. etc +const TUint MCSPI_XFERLEVEL_AEL_OFFSET = 0; // 5:0 AEL Buffer Almost Empty (threshold?) 0x0: One byte. 0x1: 2 bytes.. + +#define MCSPI_XFERLEVEL_WCNT(x) ((x) << MCSPI_XFERLEVEL_WCNT_OFFSET) +#define MCSPI_XFERLEVEL_AFL(x) (((x) << MCSPI_XFERLEVEL_AFL_OFFSET)) +#define MCSPI_XFERLEVEL_AEL(x) (((x) << MCSPI_XFERLEVEL_AEL_OFFSET)) + + +//---------------------------------- +// PAD (PIN) configuration for SPI +//---------------------------------- + +//#define SPI_CHANNEL_3_PIN_OPTION_2 // TODO - move this to mmp file! +//#define SPI_CHANNEL_3_PIN_OPTION_3 + +// flags for CS signal pins - in order to keep them in certain state when SPI is inactive.. +const TUint KCsPinUp = SCM::EPullUdEnable | SCM::EPullTypeSelect; // +const TUint KCsPinDown = SCM::EPullUdEnable; // + +const TUint KCsPinOffHi = SCM::EOffOutEnable | SCM::EOffOutValue; // +const TUint KCsPinOffLow = SCM::EOffOutEnable; // + +const TUint KCsPinModeUp = /*KCsPinUp |*//* KCsPinOffHi |*/ SCM::EInputEnable; +const TUint KCsPinModeDown = KCsPinDown | KCsPinOffLow; + +const TUint KMaxSpiChannelsPerModule = 4; // there are max 4 channels (McSPI 1) + +struct TPinConfig + { + TLinAddr iAddress; + SCM::TLowerHigherWord iMswLsw; + TUint16 iFlags; + }; + +struct TSpiPinConfig + { + TPinConfig iClk; + TPinConfig iSimo; + TPinConfig iSomi; + TPinConfig iCs[KMaxSpiChannelsPerModule]; + }; + +const TSpiPinConfig TSpiPinConfigMcSpi1 = + { + {CONTROL_PADCONF_MCSPI1_CLK, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi1_clk + {CONTROL_PADCONF_MCSPI1_CLK, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi1_simo + {CONTROL_PADCONF_MCSPI1_SOMI, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi1_somi + { + {CONTROL_PADCONF_MCSPI1_SOMI, SCM::EMsw, SCM::EMode1}, // mcspi1_cs0 + {CONTROL_PADCONF_MCSPI1_CS1, SCM::ELsw, SCM::EMode1}, // mcspi1_cs1 + {CONTROL_PADCONF_MCSPI1_CS1, SCM::EMsw, SCM::EMode1}, // mcspi1_cs2 + {CONTROL_PADCONF_MCSPI1_CS3, SCM::ELsw, SCM::EMode1}, // mcspi1_cs3 + } + }; + +const TSpiPinConfig TSpiPinConfigMcSpi2 = + { + {CONTROL_PADCONF_MCSPI1_CS3, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi2_clk + {CONTROL_PADCONF_MCSPI2_SIMO, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi2_simo + {CONTROL_PADCONF_MCSPI2_SIMO, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi2_somi + { + {CONTROL_PADCONF_MCSPI2_CS0, SCM::ELsw, SCM::EMode1}, // mcspi2_cs0 + {CONTROL_PADCONF_MCSPI2_CS0, SCM::EMsw, SCM::EMode1}, // mcspi2_cs1 + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + } + }; + + +#if defined(SPI_CHANNEL_3_PIN_OPTION_2) +const TSpiPinConfig TSpiPinConfigMcSpi3 = + { + {CONTROL_PADCONF_DSS_DATA18, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_clk + {CONTROL_PADCONF_DSS_DATA18, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_simo + {CONTROL_PADCONF_DSS_DATA20, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_somi + { + {CONTROL_PADCONF_DSS_DATA20, SCM::EMsw, SCM::EMode1}, // mcspi3_cs0 + {CONTROL_PADCONF_DSS_DATA20, SCM::ELsw, SCM::EMode1}, // mcspi3_cs1 + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + } + }; +#elif defined(SPI_CHANNEL_3_PIN_OPTION_3) +const TSpiPinConfig TSpiPinConfigMcSpi3 = + { + {CONTROL_PADCONF_ETK_D2, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_clk + {CONTROL_PADCONF_ETK_D0, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_simo + {CONTROL_PADCONF_ETK_D0, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_somi + { + {CONTROL_PADCONF_ETK_D2, SCM::ELsw, SCM::EMode1}, // mcspi3_cs0 + {CONTROL_PADCONF_ETK_D6, SCM::EMsw, SCM::EMode1}, // mcspi3_cs1 + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + } + }; +#else // default option (for beagle- these are pins on the extension header) +const TSpiPinConfig TSpiPinConfigMcSpi3 = + { + {CONTROL_PADCONF_MMC2_CLK, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_clk + {CONTROL_PADCONF_MMC2_CLK, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_simo + {CONTROL_PADCONF_MMC2_DAT0, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi3_somi + { + {CONTROL_PADCONF_MMC2_DAT2, SCM::EMsw, SCM::EMode1}, // mcspi3_cs0 + {CONTROL_PADCONF_MMC2_DAT2, SCM::ELsw, SCM::EMode1}, // mcspi3_cs1 + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + } + }; +#endif + +const TSpiPinConfig TSpiPinConfigMcSpi4 = + { + {CONTROL_PADCONF_MCBSP1_CLKR, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi4_clk + {CONTROL_PADCONF_MCBSP1_DX, SCM::ELsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi4_simo + {CONTROL_PADCONF_MCBSP1_DX, SCM::EMsw, SCM::EMode1 | SCM::EInputEnable}, // mcspi4_somi + { + {CONTROL_PADCONF_MCBSP_CLKS, SCM::EMsw, SCM::EMode1}, // mcspi3_cs0 + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + {0, SCM::ELsw, 0}, // not supported + } + }; + +const TSpiPinConfig ModulePinConfig[] = + { + TSpiPinConfigMcSpi1, + TSpiPinConfigMcSpi2, + TSpiPinConfigMcSpi3, + TSpiPinConfigMcSpi4 + }; + +#include "omap3530_spi.inl" + +#endif /* __OMAP3530_SPI_H__ */ diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/omap3530_spi.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/omap3530_spi.inl Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,118 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// Description: +// omap3530/omap3530_drivers/spi/omap3530_spi.inl +// +// This file contains definitions to internal SPI implementation. +// It is not intended to be exported - SPI registers must not be modified from outside of +// the driver! +// + + +// This sets the CS line to inactive mode (Specify aActiveMode as appropriate for configuration) +// The CS pin will be put back to the opposite mode - using GPIO.. +inline void SetCsInactive(TInt aModule, TInt aChannel, TSpiSsPinMode aActiveMode) + { + //__ASSERT_DEBUG(aModule < KMaxSpiChannelsPerModule, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels + // set the pin to the opposite to the currently active CS mode.. + TUint16 csPinOptions = aActiveMode == ESpiCSPinActiveLow ? KCsPinModeUp : KCsPinModeDown; + const TPinConfig& csConf = ModulePinConfig[aModule].iCs[aChannel]; + __ASSERT_DEBUG(csConf.iAddress, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels + + // now switch the pin mode.. + SCM::SetPadConfig(csConf.iAddress, csConf.iMswLsw, SCM::EMode4 | csPinOptions); // always go to mode 4 (gpio) + } + +void SetCsActive(TInt aModule, TInt aChannel, TSpiSsPinMode aActiveMode) + { + //__ASSERT_DEBUG(aModule < KMaxSpiChannelsPerModule, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels + TUint16 csPinOptions = aActiveMode == ESpiCSPinActiveLow ? KCsPinModeUp : KCsPinModeDown; + const TPinConfig &csConf = ModulePinConfig[aModule].iCs[aChannel]; + __ASSERT_DEBUG(csConf.iAddress, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels + + // now switch the pin mode back to the SPI + SCM::SetPadConfig(csConf.iAddress, csConf.iMswLsw, csConf.iFlags | csPinOptions); // revert to intended mode + } + + +// Setup pad function for SPI pins.. +void SetupSpiPins(TUint aSpiModule) + { + //__ASSERT_DEBUG(aModule < KMaxSpiChannelsPerModule, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels + const TSpiPinConfig& pinCnf = ModulePinConfig[aSpiModule]; + SCM::SetPadConfig(pinCnf.iClk.iAddress, pinCnf.iClk.iMswLsw, pinCnf.iClk.iFlags); + SCM::SetPadConfig(pinCnf.iSimo.iAddress, pinCnf.iSimo.iMswLsw, pinCnf.iSimo.iFlags); + SCM::SetPadConfig(pinCnf.iSomi.iAddress, pinCnf.iSomi.iMswLsw, pinCnf.iSomi.iFlags); + // Cs pins are set dynamically during operations. + } + +// helper function - returns appropriate value for the register for a given mode +inline TUint32 SpiClkMode(TSpiClkMode aClkMode) + { + // (POL) (PHA) + // 0 0 Mode 0: spim_clk is active high and sampling occurs on the rising edge. + // 0 1 Mode 1: spim_clk is active high and sampling occurs on the falling edge. + // 1 0 Mode 2: spim_clk is active low and sampling occurs on the falling edge. + // 1 1 Mode 3: spim_clk is active low and sampling occurs on the rising edge. + + TUint val = 0; + switch(aClkMode) + { + //case ESpiPolarityLowRisingEdge: // Active high, odd edges + /*val |= MCSPI_CHxCONF_POL;*/ // 0 (not set) + /*val |= MCSPI_CHxCONF_PHA;*/ // 0 (not set) + //break; // commented out - it's only for reference - there's nothing to change + + case ESpiPolarityLowFallingEdge: // Active high, even edges + /*val |= MCSPI_CHxCONF_POL;*/ // 0 (not set) + val |= MCSPI_CHxCONF_PHA; // 1 + break; + + case ESpiPolarityHighFallingEdge: // Active low, odd edges + val |= MCSPI_CHxCONF_POL; // 1 + /*val |= MCSPI_CHxCONF_PHA;*/ // 0 (not set) + break; + + case ESpiPolarityHighRisingEdge: // Active low, even edges + val |= MCSPI_CHxCONF_POL; // 1 + val |= MCSPI_CHxCONF_PHA; // 1 + break; + } + return val; + } + +// helper function - returns appropriate value for the register for a given frequency (or error->if not found) +inline TInt SpiClkValue(TInt aClkSpeedHz) + { + for (TInt val = 0; val < 0xD; val++) // only loop through all possible values.. + { + if(MCSPI_K48MHz >> val == aClkSpeedHz) + { + return (val << 2); // return value ready for the register + } + } + return KErrNotFound; + } + +inline TInt SpiWordWidth(TSpiWordWidth aWidth) + { + TInt val = 0; + switch(aWidth) + { + case ESpiWordWidth_8: val |= MCSPI_CHxCONF_WL(8); break; + case ESpiWordWidth_10: val |= MCSPI_CHxCONF_WL(10); break; + case ESpiWordWidth_12: val |= MCSPI_CHxCONF_WL(12); break; + case ESpiWordWidth_16: val |= MCSPI_CHxCONF_WL(16); break; +// case ESpiWordWidth_32: val |= MCSPI_CHxCONF_WL(32); break; // TODO uncomment when fix for Bug 3665 is released + } + return val; + } + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/psl_init.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/psl_init.cpp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,156 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// omap3530/omap3530_drivers/spi/psl_init.cpp +// + +#include +#include + +#include "psl_init.h" + +#ifdef MASTER_MODE +#include "master.h" +#endif +#ifdef SLAVE_MODE +#include "slave.h" +#endif + +#if !defined(MASTER_MODE) && !defined(SLAVE_MODE) +#error "Should select at least one type of SPI channels.." +#endif + +DECLARE_STANDARD_EXTENSION() + { + // Array of pointers to the Channels that the PSL creates, for registration with the Bus Controller + // Use a local array, since the IIC Controller operates on a copy of the array entries. + DIicBusChannel* channelPtrArray[KIicPslNumOfChannels]; + + TInt r = KErrNone; + +#ifdef MASTER_MODE +#ifndef SLAVE_MODE + // If only MASTER_MODE is declared - Create only DIicBusChannelMasterPsl channels + __KTRACE_OPT(KIIC, Kern::Printf("\n\nCreating DIicBusChannelMasterPsl only\n")); + + DIicBusChannel* chan = NULL; + for (TInt i = 0; i < KIicPslNumOfChannels; ++i) + { + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL +// chan = DSpiMasterBeagle::New(i, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if((TInt)KIicPslNumOfChannels == 1)// TODO: hack - only for the time being - enable channel 3 + chan = DSpiMasterBeagle::New(2, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + else + { + Kern::Printf("remove hack from here: %s,line %d", __FILE__, __LINE__); + return KErrGeneral; + } + + if (!chan) + { + return KErrNoMemory; + } + channelPtrArray[i] = chan; + } + +#else /*SLAVE_MODE*/ + // Master and Slave functionality is available, so create Master, Slave and MasterSlave Channels + // Create channel 0 as Master, channel 1 as a Slave, and channel 2 as MasterSlave. + __KTRACE_OPT(KIIC, Kern::Printf("\n\nCreating Master, Slave and MasterSlave channels\n")); + + DIicBusChannel* chan = NULL; + + // Master channel + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chan = DSpiMasterBeagle::New(0, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if (!chan) + { + return KErrNoMemory; + } + channelPtrArray[0] = chan; + + // Slave channel + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chan = DSpiSlaveBeagle::New(1, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if (!chan) + { + return KErrNoMemory; + } + channelPtrArray[1] = chan; + + // MasterSlave channel + // MasterSlave channels are not for derivation; instead, they have a pointer to a (derived) Master channel + // and a pointer to a (derived) Slave channel + DIicBusChannel* chanM = NULL; + DIicBusChannel* chanS = NULL; + // For MasterSlave channel, the channel number for both the Master and Slave channels must be the same + TInt msChanNum = 2; + // Master channel + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chanM = DSpiMasterBeagle::New(msChanNum, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if (!chanM) + { + return KErrNoMemory; + } + // Slave channel + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chanS = DSpiSlaveBeagle::New(msChanNum, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if (!chanS) + { + return KErrNoMemory; + } + // MasterSlave channel + // The first argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chan = new DIicBusChannelMasterSlave(DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex, (DIicBusChannelMaster*)chanM, (DIicBusChannelSlave*)chanS); + if (!chan) + { + return KErrNoMemory; + } + r = ((DIicBusChannelMasterSlave*)chan)->DoCreate(); + channelPtrArray[2] = chan; + + +#endif /*SLAVE_MODE*/ +#else /*MASTER_MODE*/ + +#ifdef SLAVE_MODE + // If only SLAVE_MODE is declared - Create all as DSpiSlaveBeagle channels + __KTRACE_OPT(KIIC, Kern::Printf("\n\nCreating DSpiSlaveBeagle only\n")); + + DIicBusChannel* chan = NULL; + for (TInt i = 0; i < KIicPslNumOfChannels; ++i) + { + // The first argument repesents the PSL-assigned channel number + // The second argument, DIicBusChannel::ESpi, should be replaced with the relevant bus type for the PSL + chan = DSpiSlaveBeagle::New(i, DIicBusChannel::ESpi, DIicBusChannel::EFullDuplex); + if (!chan) + { + return KErrNoMemory; + } + channelPtrArray[i] = chan; + } + +#endif +#endif /*MASTER_MODE*/ + + // Register them with the Bus Controller + r = DIicBusController::RegisterChannels(channelPtrArray, KIicPslNumOfChannels); + + return r; + } diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/psl_init.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/psl_init.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// omap3530/omap3530_drivers/spi/psl_init.h +// + + +#ifndef __OMAP3530_SPI_PSL_H__ +#define __OMAP3530_SPI_PSL_H__ + +const TInt KIicPslNumOfChannels = 1; // Number of channels supported // TODO only one for now.. + +struct TIicOperationType + { + enum TOperation + { + ENop = 0x00, + ETransmitOnly = 0x01, + EReceiveOnly = 0x02, + ETransmitReceive = 0x03 + }; + + struct TOp + { + TUint8 iIsTransmitting : 1; + TUint8 iIsReceiving : 1; + TUint8 iRest : 6; + }; + + union + { + TOp iOp; + TUint8 iValue; + }; + }; + +#endif /*__OMAP3530_SPI_PSL_H__*/ diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/slave.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/slave.cpp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,804 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// omap3530/omap3530_drivers/spi/slave.cpp +// + + +#include +#include +#include "psl_init.h" +#include "slave.h" + +#error "Trying to use the SPI slave, but it's not implemented yet!" + +// The timeout period to wait for a response from the client, expressed in milliseconds +// This is converted to timer ticks by the PIL, so the maximum value is 2147483. +// The value should be selected to allow for the longest, slowest transfer +// const TInt KClientWaitTime = 2; // 2mS, when debugging might set up to KMaxWaitTime + + +// In an SMP system, use a spin lock to guard access to member variables iTrigger and iInProgress +#ifdef __SMP__ +static TSpinLock IicPslSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3); +#endif + +// Callback function for the iHwGuardTimer timer. +// +// Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut +// +void DSpiSlaveBeagle::TimeoutCallback(TAny* aPtr) + { + __KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback")); + DSpiSlaveBeagle *a = (DSpiSlaveBeagle*) aPtr; + a->iTransactionStatus = KErrTimedOut; + } + + +// Static method called by the ISR when the Master has ended a transfer +// +// The method checks and reports the Rx and Tx status to the PIL by calling NotifyClient with a bitmask described as follows:. +// - If a Tx transfer has ended before all the data was transmitted, bitmask = (ETxAllBytes | ETxOverrun) +// - If a Tx transfer has ended and all the data was transmitted, bitmask = ETxAllBytes +// - If a Rx transfer has ended before the expected amount of data was received, bitmask = (ERxAllBytes | ERxUnderrun) +// - If a Rx transfer has ended and the expected amount of data was received, bitmask = ERxAllBytes +// +void DSpiSlaveBeagle::NotifyClientEnd(DSpiSlaveBeagle* aPtr) + { + __KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger)); + + // Since a transfer has ended, may wish to disable interrupts at this point + // This will likely be supported with calls similar to the following: + // AsspRegister::Write32(aPtr->iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask); + // Interrupt::Disable(aPtr->iRxInterruptId); + // Interrupt::Disable(aPtr->iTxInterruptId); + + // iTrigger will have bits ETransmit and EReceive set according to the operation requested in the call to DoRequest + // Use variable flag for the bitmask to pass into the PIL method NotifyClient + TInt flag = 0; + if(aPtr->iTrigger & EReceive) + { + // Requested Rx operation has ended - check for RxUnderrun + flag = ERxAllBytes; + if(aPtr->iRxDataEnd != aPtr->iRxData) + { + flag |= ERxUnderrun; + } + } + if(aPtr->iTrigger & ETransmit) + { + // Requested Tx operation has ended - check for RxOverrun + flag |= ETxAllBytes; + if(aPtr->iTxDataEnd != aPtr->iTxData) + { + flag |= ETxOverrun; + } + } + aPtr->NotifyClient(flag); + } + + +// ISR Handler +// +// The ISR handler identifies the cause of the interrupt that lead to its invocation: +// if the cause was transfer-related, it calls the PIL function NotifyClient to report a summary of the transfer status; +// if the cause was completion of asynchronous channel capture, PIL function ChanCaptureCallback is called +// +// The ISR also clears the source of the interrupt, and (for transfer-related interrupts) transfers the next data +// between buffers and the hardware and updates the member variable iInProgress to indicate if a transfer has started or +// ended. If a transfer has ended before the expected amount of data has been transfered it calls function NotifyClientEnd. +// +void DSpiSlaveBeagle::IicPslIsr(TAny* /*aPtr*/) + { + // DSpiSlaveBeagle *a = (DSpiSlaveBeagle*) aPtr; + + // TInt intState = 0; // Variable to support use of spin lock + + // TInt trigger = 0; // Record the Rx and Tx transfers + + // TUint32 intStatus = 0; // Record of the interrupts that are being reported + + // Identify the cause of the interrupt. If this can be achieved by reading a single register, + // code similar to the following could be used: + // intStatus = AsspRegister::Read32(a->iChannelBase + KIntStatusOffset); + + // Optional (not required if asynchronous channel capture is not supported) + // If the cause of the interrupt is completion of asynchronous channel capture, the ISR will check the appropriate + // indicator for confirmation of success - for a real PSL, this may be by querying a bitmask in a register. For the template PSL, + // however, a dummy member variable (iAsyncConfig) has been used to represent the asynchronous operation instead. + // + // if(iAsyncConfig == 1) // Replace with a check of the indicator that the interrupt was due to asynchrous channel capture + // { + // // The PIL function ChanCaptureCallback is now to be invoked. It takes as an argument either KErrNone or a + // // system-wide error code to indicate the cause of failure. For a real PSL, the argument would likely be determined + // // by reading a bitmask in a status register - but for the template port, just use KErrNone. + // // + // a->ChanCaptureCallback(KErrNone); + // return; + // } + + // If an interrupt indicates that a transfer has started, or that it has now ended, (such as a chip select + // line transition for a SPI bus the member variable iInProgress should be modified accordingly. This should + // be done under the guard of spin lock macros since iInProgress can be accessed in the context of the Client + // thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted: + // intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + // iInProgress> + // __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + // + // If a transfer has ended before the expected amount of data has been transfered, function NotifyClientEnd + // should be called, as follows: + // a->NotifyClientEnd(a); + // return; // Return now - the interrupt indicated transfer end, not receipt or transmission of data. + + // The transfers that had been started are indicated by the bitmask held in member variable iTrigger. + // This must be accessed under the guard of a spin lock since it can be accessed in the context of the + // Client thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted: + // intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + // trigger = a->iTrigger; + // __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + // If the interrupt was raised for a Tx event, and a Tx transfer had been started (so the interrupt was not spurious) + // then either prepare the next data to send, or, if all the data has been sent, call the PIL function NotifyClient + // with bitmask (ETxAllBytes | ETxUnderrun) so that, if the Client specified a ETxUnderrun notification, it will be alerted + // and can determine whether another buffer of data should be provide for transmission. + // Code similar to the following could be used: + // if(intStatus & KTxInterruptBitMask) + // { + // if(trigger & ETransmit) + // { + // // Interrupt was not spurious + // if(a->iTxData == a->iTxDataEnd) + // { + // // All the data to be transmitted has been sent, so call the PIL method NotifyClient + // a->NotifyClient(ETxAllBytes | ETxUnderrun); + // } + // else + // { + // // There is more data to be sent + // // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted + // // but if operating in 16-bit mode, bytes may need arranging for + // // endianness + // + // // Write to the Tx register with something similar to the following: + // // AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue); + // + // iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed + // // (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented + // // by the number of bytes specified in iWordSize + // } + // } + // } + + // If the interrupt was raised for a Rx event, and a Rx transfer had been started (so the interrupt was not spurious) + // read the received data from the hardware to the buffer. If a Rx FIFO is being used, use a loop to drain it - until + // the FIFO is empty or the buffer is full. If data remains after the buffer is full, an RxOverrun condition has occurred + // - so the PIL function NotifyClient should be called with bitmask (ERxAllBytes | ERxOverrun) so that, if the Client specified + // a ERxOverrun notification, it will be alerted and can determine whether another buffer should be provided to continue reception. + // Code similar to the following could be used: + // if(intStatus & KRxInterruptBitMask) + // { + // if(trigger & EReceive) + // { + // // Interrupt was not spurious + // while(AsspRegister::Read32(a->iChannelBase + KRxFifoLevelOffset)) + // { + // if((a->iRxData - a->iRxDataEnd) >= a->iWordSize) + // { + // // Space remains in the buffer, so copy the received data to it + // TUint8 nextRxValue = AsspRegister::Read32(a->iChannelBase + KRxFifoOffset); + // *a->iRxData = nextRxValue; // For this example, assumes one byte of data is to be transmitted + // // but if operating in 16-bit mode, bytes may need arranging for + // // endianness + // + // a->iRxData += a->iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed + // // (iWordSize=1), but if operating in 16-bit mode iRxData would be incremented + // // by the number of bytes specified in iWordSize + // } + // else + // { + // // The buffer is full but more data has been received - so there is an RxOverrun condition + // // Disable the hardware from receiving any more data and call the PIL function NotifyClient + // // with bitmask (ERxAllBytes | ERxOverrun). + // AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoDisableBitMask); + // a->NotifyClient(ERxAllBytes | ERxOverrun); + // break; + // } + // } + // } + // else + // { + // // If the interrupt was spurious, ignore the data, and reset the FIFO + // AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoClearBitMask); + // } + + // Once the interrupts have been processed, clear the source. If this can be achieve by writing to + // a single register, code similar to the following could be used: + // AsspRegister::Write32(a->iChannelBase + KIntStatusOffset, KAIntBitMask); + + } + + +// Constructor, first stage +// +// The PSL is responsible for setting the channel number - this is passed as the first parameter to +// this overload of the base class constructor +// +DSpiSlaveBeagle::DSpiSlaveBeagle(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) : + DIicBusChannelSlave(aBusType, aChanDuplex, 0), // Base class constructor. Initalise channel ID to zero. + iHwGuardTimer(TimeoutCallback, this) // Timer to guard against hardware timeout + { + iChannelNumber = aChannelNumber; + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::DSpiSlaveBeagle, iChannelNumber = %d\n", iChannelNumber)); + } + + +// Second stage construction +// +// Allocate and initialise objects required by the PSL channel implementation +// +TInt DSpiSlaveBeagle::DoCreate() + { + __KTRACE_OPT(KIIC, Kern::Printf("\nDSpiSlaveBeagle::DoCreate, ch: %d \n", iChannelNumber)); + + TInt r = KErrNone; + + // PIL Base class initialization. + r = Init(); + if(r == KErrNone) + { + // At a minimum, this function must set the channel's unique channel ID. + // When the channel is captured, this value will be combined with an instance count + // provided by the PIL to generate a value that will be used by a client as a unique + // identifer in subsequent calls to the Slave API. + // + // There is no set format for the ID, it just needs to be unique. + // Un-comment and complete the following line: +// iChannelId = + + // This method may also be concerned with setting the base register address iChannelBase), and allocating + // any objects that will be required to support operaton until the channel is deleted. + // + // Un-comment and complete the following line: +// iChannelBase = + } + return r; + } + +// static method used to construct the DSpiSlaveBeagle object. +DSpiSlaveBeagle* DSpiSlaveBeagle::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) + { + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); + DSpiSlaveBeagle *pChan = new DSpiSlaveBeagle(aChannelNumber, aBusType, aChanDuplex); + + TInt r = KErrNoMemory; + if (pChan) + { + r = pChan->DoCreate(); + } + if (r != KErrNone) + { + delete pChan; + pChan = NULL; + } + + return pChan; + } + + +// Validates the configuration information specified by the client when capturing the channel +// +// Called by the PIL as part of the Slave CaptureChannel processing +// +// If the pointer to the header is NULL, return KErrArgument. +// If the content of the header is not valid for this channel, return KErrNotSupported. +// +TInt DSpiSlaveBeagle::CheckHdr(TDes8* aHdrBuff) + { + TInt r = KErrNone; + + if(!aHdrBuff) + { + r = KErrArgument; + } + else + { + // Check that the contents of the header are valid + // + // The header will be specific to a particular bus type. Using a fictional + // bus type Abc,code similar to the following could be used to validate each + // member of the header: + // + // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff; + // TConfigAbcV01 &abcHeader = (*headerBuf)(); + // if( (abcHeader.iHeaderMember < ESomeMinValue) || + // (abcHeader.iHeaderMember > ESomeMaxValue)) + // { + // __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember)); + // r = KErrNotSupported; + // } + + } + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::CheckHdr() r %d", r)); + + return r; + } + + +// Method called in the context of the client thread, as a consequence of the PSL invocation of the +// PIL method NotifyClient when a bus event occurs. +// +// This method updates the bitmask of requested operations (held in member variable iTrigger) and the +// PIL counts of data received and transmitted. If the event was a bus error, the bitmask of requested operations +// is cleared. +// +void DSpiSlaveBeagle::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb) + { + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::ProcessData(), trigger: %x\n", aTrigger)); + + TInt intState; + + // If using the iInProgress member variable to indicate transitions on a chip-select line, and an interrupt + // occurred as a transfer was to end, then must ensure the transmission of data has ceased. + // + // Must use spin lock to guard access since iInProgress is accessed by the ISR + // + TInt inProgress; + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + inProgress = iInProgress; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + // + if(!inProgress && // Transfer has now ended + (aTrigger & (ERxAllBytes | ETxAllBytes))) // Master has not yet finished transferring data + { + // Use the guard timer to make sure that transfer ends with an expected time - if this does not cease + // before the timer expires, iTransactionStatus will be set to KErrTimedOut by the callback function TimeoutCallback + // + // Poll the relevant register to check for transfer activity, using code similar to the following: + // TInt8 transferring = AsspRegister::Read32(iChannelBase + KStatusRegisterOffset) & KTransferringBitMask); + // For the template port, use a dummy variable instead of the register access (transferring = 1) + // + TInt8 transferring = 1; + iTransactionStatus = KErrNone; + iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); + + while((iTransactionStatus == KErrNone) && + transferring); // Replace transferring with a register read, as described above + + // At this point, either the transfer has ceased, or the timer expired - in either case, may disable the interrupt + // for the transfer now, using code similar to the following: + // AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask); + + // Check for guard timer expiry + if(iTransactionStatus != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::ProcessData - Transaction timed-out")); + return; + } + else + { + iHwGuardTimer.Cancel(); + } + + // If all transfer activity has now ceased, clear iTrigger + // Must use spin lock to guard access since iInProgress is accessed by the ISR + // + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger = 0; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + } + + // If the PSL called the PIL function NotifyClient to indicate transfer activity (or error), the reason + // will be specified as a bitmask in aTrigger + // - if a Rx event occurred, the ERxAllBytes flag will be set + // - if a Tx event occurred, the ETxAllBytes flag will be set + // - if a bus error occurred, the EGeneralBusError flag will be set + // + if(aTrigger & ERxAllBytes) + { + __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Buf: %x\n", iRxData)); + __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Bufend: %x\n", iRxDataEnd)); + + // Clear the internal EReceive flag + // This must be done under guard of a spin lock since iTrigger is accessed by the ISR + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger &= ~EReceive; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + // Update the PIL count of Rx data (in the Callback object) + aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize)); + } + // + if(aTrigger & ETxAllBytes) + { + __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Buf: %x\n", iTxData)); + __KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Bufend: %x\n", iTxDataEnd)); + + // Clear the internal ETransmit flag.. + // This must be done under guard of a spin lock since iTrigger is accessed by the ISR + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger &= ~ETransmit; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + // Update the PIL count of Tx data (in the Callback object) + aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize)); + } + // + if(aTrigger & EGeneralBusError) + { + __KTRACE_OPT(KIIC, Kern::Printf("BusError..")); + + // Clear and disable relevant interrupts, possibly using code similar to the following: + // AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask); + + // Clear internal flags + // This must be done under guard of a spin lock since iTrigger is accessed by the ISR + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger = 0; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + } + + // Set the callback's trigger, for use by the PIL + aCb->SetTrigger(aTrigger | aCb->GetTrigger()); + } + + + +// Method to initialise the hardware in accordance with the data provided by the Client +// in the configuration header when capturing the channel +// +// This method is called from DoRequest and is expected to return a value to indicate success +// or a system wide error code to inform of the failure +// +TInt DSpiSlaveBeagle::ConfigureInterface() + { + __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); + + TInt r = KErrNone; + + // The header is stored in member variable iConfigHeader, and will be specific to a particular bus type. + // Using a fictional bus type Abc, code similar to the following could be used to access each + // member of the header: + // + // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) iConfigHeader; + // TConfigAbcV01 &abcHeader = (*headerBuf)(); + // TInt value = abcHeader.iTintMember; + + // Initialising the hardware may be achieved with calls similar to the following: + // AsspRegister::Write32(a->iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask); + // GPIO::SetPinMode(aPinId, GPIO::EEnabled); + + // Binding an ISR may be achieved with calls similar to the following: + // r = Interrupt::Bind(iRxInterruptId, DSpiSlaveBeagle::IicPslIsr, this); + // r = Interrupt::Bind(iTxInterruptId, DSpiSlaveBeagle::IicPslIsr, this); + // Enabling interrupts may be achieved with calls similar to the following: + // r = Interrupt::Enable(iRxInterruptId); + // r = Interrupt::Enable(iTxInterruptId); + + // Modifying a hardware register may not be a zero-delay operation. The member variable iHwGuardTimer could be used to guard a + // continuous poll of the hardware register that checks for the required change in the setting; TimeoutCallback is already + // assigned as the callback function for iHwGaurdTimer, and it modifies member variable iTransactionStatus to indicate a timeout + // - so the two could be used together as follows: + // iTransactionStatus = KErrNone; + // iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); + // while((iTransactionStatus == KErrNone) && + // AsspRegister::Read32(iChannelBase + KRegisterOffset) & KRegisterFlagBitMask); + // if(iTransactionStatus != KErrNone) + // { + // r = KErrGeneral; + // } + // else + // { + // iHwGuardTimer.Cancel(); + // } + + // DoRequest checks the return value so the variable r should be modified in the event of failure with a system-wide error code + // for example, if a register could not be modified, + // r = KErrGeneral; + // __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface failed with error %d\n",r)); + return r; + } + + +// Method to start asynchronous initialisation of the hardware, in accordance with the data provided by the Client +// in the configuration header when capturing the channel. This differs from ConfigureInterface in that it +// merely starts the initialisation, then returns immediately; +// +// The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware +// interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once +// all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is +// complete, the ISR should be invoked, which will then call PIL method ChanCaptureCallback +// +// This method is called from DoRequest and is expected to return a value to indicate success +// or a system wide error code to inform of the failure +// +TInt DSpiSlaveBeagle::AsynchConfigureInterface() + { + __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); + +// TInt r = KErrNone; // A real implementation would use this as the return value to indicate success / failure + + // Precisely what processing is done to 'start' the asynchronous processing is entirely platform-specific; + // it may be the set-up and activation of a long-running operation that completes asynchronously. Regardless of what + // is done, its completion is expected to result in the ISR being run. + // + // Whatever the operation, there must be some means of the ISR recognising that an asynchronous initialisation has + // been performed + // In a real PSL, this may be be checking a bitmask in a status register. For the template PSL, however, + // a dummy class member will be used (iAsyncConfig) + // Since this member will be accessed by the ISR, it should, strictly speaking, be accessed under the guard of a spin lock + TInt intState; + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iAsyncConfig = 1; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + return KErrNone; // A real implementation would return an indication of success / failure + } + +// Method called from DoRequest to start Tx and-or Rx transfer. +// +// The method will initialise the hardware and pointers used to manage transfers, before returning a value to report success +// (KErrNone) or a system-wide error code that indicates the cause of failure. +// +TInt DSpiSlaveBeagle::InitTransfer() + { + __KTRACE_OPT(KIIC, Kern::Printf("DSpiSlaveBeagle::InitTransfer()")); + + TInt r = KErrNone; + + // Local copies of member variables that must be accessed in a synchronised manner + TInt inProgress; + TInt trigger; + + TInt intState; + + // Check if a transfer is already in progress. + // If variable iInProgress is being used, this must be determined in a synchronised manner because the ISR modifies it. + // Bus types that do not rely on chip-select transitions may use an alternative method to indicate if a transfer is in + // progress + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + inProgress = iInProgress; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + if(!inProgress) + { + // If no transfers are in progress, it may be necessary to initialise the hardware to support those that + // are being requested. This may include FIFO and interrupt initialisation, + // + // Initialising the hardware may be achieved with calls similar to the following: + // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask); + // GPIO::SetPinMode(aPinId, GPIO::EEnabled); + } + + // Check the current operations. This must be determined in a synchronised manner because ProcessData + // runs in the context of the Client thread and it modifies the value of iTrigger + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + trigger = iTrigger; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + + if(trigger & ETransmit) + { + // If Tx transfers were not previously active, it may be necessary to initialise the Tx hardware here, e.g. + // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslTxModeBitMask); + + // Initialise the Tx pointers + iTxData = iTxBuf + (iWordSize * iTxOffset); + iTxDataEnd = iTxData + (iWordSize * iNumTxWords); + + __KTRACE_OPT(KIIC, Kern::Printf("Tx Buf: %x", iTxData)); + __KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd)); + + // If using a FIFO, copy the data to it until either the FIFO is full or all the data has been copied + // This could be achieved with something similar to the following lines: + // while(AsspRegister::Read32(iChannelBase + KFifoLevelOffset) <= (KFifoMaxLevel - iWordSize) && + // iTxData != iTxDataEnd) + // For the template port, will just use a dummy variable (dummyFifoLvlChk )in place of the register read + TInt dummyFifoLvlChk = 0; + while((dummyFifoLvlChk) && // Replace this dummy variable with a read of the hardware + (iTxData != iTxDataEnd)) + { + // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted + // but if operating in 16-bit mode, bytes may need arranging for + // endianness + + // Write to the Tx register with something similar to the following: + // AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue); + + iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed + // (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented + // by the number of bytes specified in iWordSize + } + // If a Tx FIFO is not being used, a single Tx value would be written - in which case the above loop would be replaced + + __KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf: %x", iTxData)); + __KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd)); + } + + if(trigger & EReceive) + { + // Initialise the Rx pointers + iRxData = iRxBuf + (iWordSize * iRxOffset); + iRxDataEnd = iRxData + (iWordSize * iNumRxWords); + + __KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer: %x", iRxData)); + __KTRACE_OPT(KIIC, Kern::Printf("Rx Bufend: %x", iRxDataEnd)); + + // If Rx transfers were not previously active, it may be necessary to initialise the Rx hardware here, e.g. + // AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslRxModeBitMask); + } + + // If there is some common configuration required to support Rx, Tx transfers, may do it here + + return r; + } + + +// The gateway function for PSL implementation +// +// This method is called by the PIL to perform one or more operations indicated in the bitmask aOperation, +// which corresponds to members of the TPslOperation enumeration. +// +TInt DSpiSlaveBeagle::DoRequest(TInt aOperation) + { + __KTRACE_OPT(KIIC, Kern::Printf("\nDSpiSlaveBeagle::DoRequest, Operation 0x%x\n", aOperation)); + + TInt r = KErrNone; + TInt intState; + + if (aOperation & EAsyncConfigPwrUp) + { + // The PIL has requested asynchronous operation of CaptureChannel. + // The PSL should start the processing required for a channel to be captured, and then return immediately with + // error code KErrNone (if the processing was started without error), so that the client thread will be unblocked. + // The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware + // interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once + // all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is + // complete, the PSL should call the PIL function ChanCaptureCallback - this will lead to the Client-provided + // callback being executed in the context of the client thread + // + __KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp")); + r = AsynchConfigureInterface(); + if (r != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("AsynchConfigureInterface returned %d\n", r)); + } + return r; + } + + if (aOperation & ESyncConfigPwrUp) + { + // The PIL has requested synchronous operation of CaptureChannel. + // The PSL should perform the processing required for a channel to be captured, and return a system-wide error + // code when this is complete to indicate the status of the capture. + // Capturing a channel is expected to include initialisation of the hardware to enable operation in accordance + // with the configuration specified in the PIL member variable iConfigHeader, which holds the configuration + // specified by the Client. + // + __KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp")); + r = ConfigureInterface(); + if (r != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface returned %d\n", r)); + return r; + } + } + + if (aOperation & ETransmit) + { + // The PIL has requested that a Tx operation be started. + // Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to + // indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other, + // requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes + // to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while + // this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for + // such situations, use a spin lock to guard access to iTrigger. + // When the same check has been performed for Rx, call the InitTransfer function to start the required transfers. + // + __KTRACE_OPT(KIIC, Kern::Printf("ETransmit")); + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger |= ETransmit; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + } + + if (aOperation & EReceive) + { + // The PIL has requested that a Rx operation be started. + // Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to + // indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other, + // requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes + // to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while + // this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for + // such situations, use a spin lock to guard access to iTrigger. + // When the same check has been performed for Tx, call the InitTransfer function to start the required transfers. + // + __KTRACE_OPT(KIIC, Kern::Printf("EReceive")); + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + iTrigger |= EReceive; + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + } + + if (aOperation & (EReceive | ETransmit)) + { + // This code should only be executed once it has been checked whether Rx and Tx operations are required. + r = InitTransfer(); + } + + if (aOperation & EAbort) + { + // The PIL has requested that the current transaction be aborted. + // This is the case if the Client has not responded within an expected time to specify the next steps in + // the transaction processing. The time allowed is specified by calling PIL function SetClientWaitTime, otherwise + // the time defaults to KSlaveDefCWaitTime. + // If the PSL is able to satisfy this request it should, at a minimum, disable interrupts and update the member + // variables that indicate a transaction is in progress. If the PSL is unable to satisfy the request then the same + // behaviour will follow as if this request had not been made, so there is no point in modifying the state variables. + // If both Rx and Tx operations had been requested, and one completes ahead of the other, it is possible that the other + // transfer could complete while this function is running; consequently, it may attempt to access iTrigger and iInProgress, + // and so cause data corruption. To cater for such situations, use a spin lock to guard access to iTrigger. + // The PIL makes no assumptions of whether the PSL can support this request or not, and does not check the return + // value - so there is no need to set one. + // + TUint8 dummyCanAbort = 1; // Dummy variable to represent a check of if it is possible to abort the current transaction + __KTRACE_OPT(KIIC, Kern::Printf("EAbort")); + intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock); + if(dummyCanAbort) + { + // The spin lock has been acquired, so it is safe to modify data and hardware registers that may be accessed as part of + // interrupt processing performed by an ISR - this is assuming that the ISR has been written to acquire the same spin lock. + // Limit the processing to only that which is necessary to be processed under spin lock control, so as to not delay other + // threads of execution that are waiting for the spin lock to be freed. + // Hardware may be configured using code similar to the following: + // AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask); + iInProgress = EFalse; + iTrigger = 0; + } + __SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState); + // Having released the spin lock, now perform any actions that are not affected by pre-emption by an ISR, this may include code + // such as the following + // Interrupt::Disable(iRxInterruptId); + // Interrupt::Disable(iTxInterruptId); + } + + if (aOperation & EPowerDown) + { + // The PIL has requested that the channel be released. + // If this channel is not part of a MasterSlave channel, the next Client will operate in Slave mode. In this case, it may only + // be necessary to disable interrupts, and reset the channel hardware. + // If this channel represents the Slave of a MasterSlave channel, it is possible that some of the hardware is shared between the + // Master and Slave sub-channels. Since it may not be known whether the next Client of the parent channel will require operation + // in either Master or Slave mode, some additional processing may be required to allow for subsequent Master operation (for example. + // unbinding an interrupt). + // + __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown")); + + // Resetting the hardware may be achieved with calls similar to the following: + // AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask); + // GPIO::SetPinMode(aPinId, GPIO::EDisabled); + + // Disable interrupts may be achieved with calls similar to the following: + // Interrupt::Disable(iRxInterruptId); + // Interrupt::Disable(iTxInterruptId); + + // Unbinding an ISR may be achieved with calls similar to the following: + // Interrupt::Unbind(iRxInterruptId); + // Interrupt::Unbind(iTxInterruptId); + + // The PIL checks the return value so the variable r should be modified in the event of failure with a system-wide error code + // for example, if a register could not be modified, + // r = KErrGeneral; + // __KTRACE_OPT(KIIC, Kern::Printf("EPowerDown failed with error %d\n",r)); + + } + return r; + } + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/slave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/slave.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,87 @@ +// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// lukasz.forynski@gmail.com +// +// Description: +// omap3530/omap3530_drivers/spi/slave.h +// + + +#ifndef __OMAP3530_SPI_SLAVE_H__ +#define __OMAP3530_SPI_SLAVE_H__ + +#include + +class DSpiSlaveBeagle: public DIicBusChannelSlave + { + +public: + static DSpiSlaveBeagle* New(TInt aChannelNumber, const TBusType aBusType, + const TChannelDuplex aChanDuplex); + + // Gateway function for PSL implementation + virtual TInt DoRequest(TInt aOperation); + + DSpiSlaveBeagle(TInt aChannelNumber, const TBusType aBusType, + const TChannelDuplex aChanDuplex); + +private: + // Overriders for base class pure-virtual methods + virtual TInt DoCreate(); // second-stage construction, + virtual TInt CheckHdr(TDes8* aHdrBuff); + virtual void ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb); + + // Internal methods.. + TBool TransConfigDiffersFromPrev(); + TInt ConfigureInterface(); + TInt AsynchConfigureInterface(); + TInt InitTransfer(); + + // ISR handler and other static methods.. + static void IicPslIsr(TAny* aPtr); + static void TimeoutCallback(TAny* aPtr); + static inline void NotifyClientEnd(DSpiSlaveBeagle* aPtr); + + // Register base for the Master channel + TUint iSlaveChanBase; + + // Interrupt ID for the Master channel + TInt iSlaveIntId; + + // Bit mask of the transfer triggers managed by the channel + TUint8 iTrigger; + + // Granularity, expressed as the number of bytes in a word + TInt8 iWordSize; + + // Flag indicating transmission activity - optional, may not be required for all bus types + // In the template example it is used to indicate transitions on a chip-select line, such as for SPI. + TInt8 iInProgress; + + // Dummy variable used merely to demonstrate the asynchronous channel capture mechanism + // See method AsynchConfigureInterface + TInt8 iAsyncConfig; + + // Pointers to buffers used for Rx and Tx transfers + TInt8 *iTxData; + TInt8 *iRxData; + TInt8 *iTxDataEnd; + TInt8 *iRxDataEnd; + + // Timer to guard 'while' loops.. + NTimer iHwGuardTimer; + + // status of the transaction + volatile TInt iTransactionStatus; + }; + +#endif /*__OMAP3530_SPI_SLAVE_H__*/ diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/spi.iby --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/spi.iby Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,29 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530/omap3530_drivers/spi/spi.iby +// this file defines components to be included in the rom image in order +// to support IIC SPI implementation. + + + +#ifndef STANDALONE_CHANNEL +extension[VARID]=\epoc32\release\##KMAIN##\##BUILD##\iic.dll \sys\bin\iic.dll +#endif +extension[VARID]=\epoc32\release\##KMAIN##\##BUILD##\_omap3530_spi.dll \sys\bin\spi.dll + +#ifdef INCLUDE_SPI_TEST +device[VARID]=\epoc32\release\##KMAIN##\##BUILD##\_beagle_d_spi_client_m.ldd \sys\bin\d_spi_client_m.ldd +file=\epoc32\release\##KMAIN##\##BUILD##\t_spi_client_m.exe \sys\bin\t_spi_client_m.exe + +#endif + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/spi.mmp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/spi.mmp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,75 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530/omap3530_drivers/spi/spi.mmp +// + +#include +//#include "kernel/kern_ext.mmh" // this is already included from iic_channel.mmh +// ..and probably it shouldn't be + + +// Select the mode to build +// For Master-Slave mode, uncomment both MASTER and SLAVE defines +#define __USE_MASTER_MODE__ +//#define __USE_SLAVE_MODE__ + + +//macro STANDALONE_CHANNEL +//#define STANDALONE_CHANNEL /*Only for iic_channel.mmh to pick up the needed source files*/ + +// PSL source +SOURCEPATH . +SOURCE psl_init.cpp + +#ifdef __USE_MASTER_MODE__ +macro MASTER_MODE +SOURCE master.cpp +#endif + +#ifdef __USE_SLAVE_MODE__ +macro SLAVE_MODE +SOURCE slave.cpp +#endif + + +// PIL source +#include "../../../../../os/kernelhwsrv/kernel/eka/drivers/iic/iic_channel.mmh" +sourcepath ../../../../../os/kernelhwsrv/kernel/eka/drivers/iic/ +source IIC_PIL_SOURCE + + +TARGET AsspTarget(spi,dll) +TARGETTYPE kext +LINKAS spi.dll +//DEFFILE ./def/~/spi.def +NOSTRICTDEF + + +USERINCLUDE . +SYSTEMINCLUDE +\include\assp\omap3530_assp +SYSTEMINCLUDE +\include\drivers +SYMBIAN_BASE_SYSTEMINCLUDE(assp/omap3530_assp) +SYMBIAN_BASE_SYSTEMINCLUDE(drivers) + +LIBRARY AsspTarget(kaomap3530,lib) +LIBRARY AsspTarget(prcm,lib) +LIBRARY AsspTarget(gpio,lib) + +// #ifdef USE_SYMBIAN_PRM? +//library resman.lib + +EPOCALLOWDLLDATA + +CAPABILITY all + +VENDORID 0x70000001 diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/test/d_spi_client_m.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/test/d_spi_client_m.cpp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,869 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530_drivers/spi/test/d_spi_client_m.cpp +// +// This test driver is a simple example IIC SPI client implementation - and a test to SPI implementation. +// It is an LDD but PDD or kernel extension can implement / use the IIC SPI bus exactly the same way. +// + +// Note: IMPORTANT! -If you intend to make changes to the driver! +// Obviously the best test is to use the SPI with the real device (and check if it still works after changes). +// If this is not the case (e.g. as it was when this driver was being created) - to fully test the functionality +// this test can make use of duplex transactions with local loopback. In such case received data should match the +// data sent over the bus. (It might be possible to configure local loopback using pad config(not yet confirmed), +// but until this (and not to complicate the test too much)- here is simple description on how to do it on a +// beagleboard. If McSPI3 is configured with the default pin option, i.e. to route signals to the expansion +// hader (see spi driver for details):s +// following header pins have SPI3 functions: +// 21 - CLK +// 19 - SIMO +// 17 - SOMI +// 11 - CS0 +// local loopback could be simply made by puttint the jumper between pin 17 and pin 19 of the extension header. +// This test will automatically detect this configuration and the jumper - so if you want to test it this way +// it is highly recommended (and it's simple). + +#include +#include +#include +#include "d_spi_client_m.h" + + +_LIT(KTestDriverName,"d_spi_client_m"); + +// (un) comment it out for debugging +//#define LOG_FUNCTION_CALLS + +#ifdef LOG_FUNCTION_CALLS +#define LOG_FUNCTION_ENTRY Kern::Printf("DSpiClientChannel::%s()", __FUNCTION__) +#define LOG_FUNCTION_RETURN Kern::Printf("DSpiClientChannel::%s() return: %d", __FUNCTION__, r) +#else +#define LOG_FUNCTION_ENTRY +#define LOG_FUNCTION_RETURN +#endif + + +// === generic (driver related) stuff === + +// this driver only implements one channel +const TInt KMaxNumChannels = 1; + + + +// entry point +DECLARE_STANDARD_LDD() + { + return new DSpiClientTestFactory; + } + +DSpiClientTestFactory::DSpiClientTestFactory() + { + iParseMask = 0; + iUnitsMask = 0; // No info, no PDD, no Units + iVersion = TVersion(KIntTestMajorVersionNumber, + KIntTestMinorVersionNumber, KIntTestBuildVersionNumber); + } + +DSpiClientTestFactory::~DSpiClientTestFactory() + { + } + +// Install the device driver. +TInt DSpiClientTestFactory::Install() + { + return (SetName(&KLddFileNameRoot)); + } + +void DSpiClientTestFactory::GetCaps(TDes8& aDes) const + { + TPckgBuf b; + b().version = TVersion(KIntTestMajorVersionNumber, KIntTestMinorVersionNumber, KIntTestBuildVersionNumber); + Kern::InfoCopy(aDes, b); + } + +// Create a channel on the device. +TInt DSpiClientTestFactory::Create(DLogicalChannelBase*& aChannel) + { + if (iOpenChannels >= KMaxNumChannels) + return KErrOverflow; + + aChannel = new DSpiClientChannel; + return aChannel ? KErrNone : KErrNoMemory; + } + +DSpiClientChannel::DSpiClientChannel() + { + iClient = &Kern::CurrentThread(); + // Increase the DThread's ref count so that it does not close without us + ((DObject*)iClient)->Open(); + } + +DSpiClientChannel::~DSpiClientChannel() + { + ((TDynamicDfcQue*)iDfcQ)->Destroy(); + // decrement the DThread's reference count + Kern::SafeClose((DObject*&) iClient, NULL); + } + +TInt DSpiClientChannel::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) + { + TDynamicDfcQue *dfcq = NULL; + TInt r = Kern::DynamicDfcQCreate(dfcq, KIntTestThreadPriority, KTestDriverName); + + if(r == KErrNone) + { + SetDfcQ(dfcq); + iMsgQ.Receive(); + } + return r; + } + +void DSpiClientChannel::HandleMsg(TMessageBase* aMsg) + { + TThreadMessage& m = *(TThreadMessage*) aMsg; + TInt id = m.iValue; + + if (id == ECloseMsg) + { + iMsgQ.iMessage->Complete(KErrNone, EFalse); + return; + } + else if (id == KMaxTInt) + { + m.Complete(KErrNone, ETrue); + return; + } + + if (id < 0) + { + TRequestStatus* pS = (TRequestStatus*) m.Ptr0(); + TInt r = DoRequest(~id, pS, m.Ptr1(), m.Ptr2()); + if (r != KErrNone) + { + Kern::RequestComplete(iClient, pS, r); + } + m.Complete(KErrNone, ETrue); + } + else + { + TInt r = DoControl(id, m.Ptr0(), m.Ptr1()); + m.Complete(r, ETrue); + } + } + +// to handle synchronous requests.. +// TODO: There are unimplemented functions - returning KErrNotSupported - treat this as a 'sort of' +// of test-driven development.. Ideally - they should all be implemented.. +TInt DSpiClientChannel::DoControl(TInt aId, TAny* a1, TAny* a2) + { + TInt r = KErrNone; + switch (aId) + { + case RSpiClientTest::EHalfDuplexSingleWrite: + { + r = HalfDuplexSingleWrite(); + break; + } + case RSpiClientTest::EHalfDuplexMultipleWrite: + { + r = HalfDuplexMultipleWrite(); + break; + } + case RSpiClientTest::EHalfDuplexSingleRead: + { + r = HalfDuplexSingleRead(); + break; + } + case RSpiClientTest::EHalfDuplexMultipleRead: + { + r = HalfDuplexMultipleRead(); + break; + } + case RSpiClientTest::EHalfDuplexMultipleWriteRead: + { + r = HalfDuplexMultipleWriteRead(); + break; + } + case RSpiClientTest::EFullDuplexSingle: + { + r = FullDuplexSingle(); + break; + } + case RSpiClientTest::EFullDuplexMultiple: + { + r = FullDuplexMultiple(); + break; + } + case RSpiClientTest::EHalfDuplexExtendable: + { + r = HalfDuplexExtendable(); + break; + } + case RSpiClientTest::EFullDuplexExtendable: + { + r = FullDuplexExtendable(); + break; + } + + default: + { + Kern::Printf("DSpiClientChannel::DoControl():Unrecognized value for aId=0x%x\n", aId); + r = KErrArgument; + break; + } + } + return r; + } + +// to handle asynchronous requests from the client +TInt DSpiClientChannel::DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2) + { + __KTRACE_OPT(KTESTFAST, Kern::Printf("DSpiClientChannel::DoRequest(aId=0x%x, aStatus=0x%x, a1=0x%x, a2=0x%x\n", + aId, aStatus, a1, a2)); + + // TODO: There are unimplemented functions - returning KErrNotSupported - treat this as a 'sort of' + // of test-driven development.. Ideally - they should all be implemented.. + TInt r = KErrNone; + switch (aId) + { + case RSpiClientTest::EAsyncHalfDuplexSingleWrite: + { + r = KErrNotSupported; // AsyncHalfDuplexSingleWrite(aStatus); + break; + } + case RSpiClientTest::EAsyncHalfDuplexMultipleWrite: + { + r = KErrNotSupported; // AsyncHalfDuplexMultipleWrite(aStatus); + break; + } + case RSpiClientTest::EAsyncHalfDuplexSingleRead: + { + r = KErrNotSupported; // AsyncHalfDuplexSingleRead(aStatus); + break; + } + case RSpiClientTest::EAsyncHalfDuplexMultipleRead: + { + r = KErrNotSupported; // AsyncHalfDuplexMultipleRead(aStatus); + break; + } + case RSpiClientTest::EAsyncHalfDuplexMultipleWriteRead: + { + r = KErrNotSupported; // AsyncHalfDuplexMultipleWriteRead(aStatus); + break; + } + case RSpiClientTest::EAsyncFullDuplexSingle: + { + r = KErrNotSupported; // AsyncFullDuplexSingle(aStatus); + break; + } + case RSpiClientTest::EAsyncFullDuplexMultiple: + { + r = KErrNotSupported; // AsyncFullDuplexMultiple(aStatus); + break; + } + case RSpiClientTest::EAsyncHalfDuplexExtendable: + { + r = KErrNotSupported; // AsyncHalfDuplexExtendable(aStatus); + break; + } + case RSpiClientTest::EAsyncFullDuplexExtendable: + { + r = KErrNotSupported; // AsyncFullDuplexExtendable(aStatus); + break; + } + default: + { + Kern::Printf("DSpiClientChannel::DoRequest(): unrecognized value for aId=0x%x\n", aId); + r = KErrArgument; + break; + } + } + + // complete request from here if there was an error creating it.. + if(r != KErrNone) + { + Kern::RequestComplete(iClient, aStatus, r); + } + + return r; + } +// ====== (end of driver related stuff) === + + +// test half duplex write: +// - one transaction with one write transfer +// - synchronous use - all buffers / transfer objects / transactions - on the stack +// (Data on stack - recommended for small transfers). +// This could serve as the simplest example of a single write +TInt DSpiClientChannel::HalfDuplexSingleWrite() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer object + const TInt KBuffLength = 64; + TBuf8 txTransferBuf; // txbuffer.. + + // fill it with some data..(this will also set the length of the buffer) + for (TInt i = 0; i < KBuffLength; ++i) + { + txTransferBuf.Append(i+1); + } + + // create tranfer object + TIicBusTransfer txTransfer(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf); + + // Create a transaction using header and list of transfers.. + TIicBusTransaction transaction(&header, &txTransfer); + + // queue the transaction synchronously + r = IicBus::QueueTransaction(busId, &transaction); + + LOG_FUNCTION_RETURN; + return r; + } + +// test half duplex write: +// - one transaction with more write transfers +// - synchronous use - all buffers / transfer objects / transactions - on the stack +// (Data on stack - recommended for small transfers). +// This could serve as the simplest example of a multiple writes +// Note, that ALWAYS (not only in this example) - each of the transfers is separated +// with SS signals assertion / deassertion +TInt DSpiClientChannel::HalfDuplexMultipleWrite() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer objects + const TInt KBuffLength = 64; + TBuf8 txTransferBuf1; + TBuf8 txTransferBuf2; + + // put some data into these buffers..(this will also set their length) + for (TInt i = 0; i < KBuffLength; ++i) + { + txTransferBuf1.Append(i+1); + txTransferBuf2.Append(63-i); + } + + // create two transfers and link them (transfer2 after transfer1) + TIicBusTransfer txTransfer1(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf1); + TIicBusTransfer txTransfer2(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf2); + txTransfer1.LinkAfter(&txTransfer2); // will link txTransfer2 after txTransfer1.. + + // Create a transaction using header and linked transfers (using first in one) + TIicBusTransaction transaction(&header, &txTransfer1); + + r = IicBus::QueueTransaction(busId, &transaction); + + LOG_FUNCTION_RETURN; + return r; + } + +// test half duplex read: +// - one transaction with one read transfer +// - synchronous use - all buffers / transfer objects / transactions - on the stack +// (Data on stack - recommended for small transfers). +// This could serve as the simplest example of a single read transfer. +// (e.g. for a potential use with a Slave device that is only capable of sending data) +TInt DSpiClientChannel::HalfDuplexSingleRead() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed 3MHz (could use SpiFreqHz(16)) + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer objects + const TInt KBuffLength = 64; + TBuf8 rxTransferBuf; + + // put some data into the buffer..(this will also set its length) + for (TInt i = 0; i < KBuffLength; ++i) + { + rxTransferBuf.Append(i+1); + } + + // create transfer + TIicBusTransfer rxTransfer(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf); + + // And a transaction + TIicBusTransaction transaction(&header, &rxTransfer); + + // queue transaction synchronously + r = IicBus::QueueTransaction(busId, &transaction); + + // now, we has non-zero data in the buffer, but we know, that there was nothing connected + // so we should either receive 0x0 (or 0xff if line was driven high- unlikely)- but we'll check + // if rx buffer has simply different data in it.. + for (int i = 0; i < KBuffLength; i++) + { + if(rxTransferBuf[i] == i+1) + { + r = KErrCorrupt; + break; + } + } + + LOG_FUNCTION_RETURN; + return r; + } + +// test half duplex multiple transfer read: +// - one transaction with two read transfers +// - synchronous use - all buffers / transfer objects / transactions - on the stack +// (Data on stack - recommended for small transfers). +// this is simmilar to write transactions with multiple transfers (e.g. HalfDuplexMultipleWrite) +TInt DSpiClientChannel::HalfDuplexMultipleRead() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 1500000, //iClkSpeed 3MHz (could use SpiFreqHz(32)) + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer objects + const TInt KBuffLength1 = 32; + const TInt KBuffLength2 = 15; + + TBuf8 rxTransferBuf1; + TBuf8 rxTransferBuf2; + + // put some data into these buffers..(this will also set their lengths) + for (TInt i = 0; i < KBuffLength1; ++i) + { + rxTransferBuf1.Append(i+1); + } + + for (TInt i = 0; i < KBuffLength2; ++i) + { + rxTransferBuf2.Append(i+1); + } + + // create two transfers and link them (transfer2 after transfer1) + TIicBusTransfer rxTransfer1(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf1); + TIicBusTransfer rxTransfer2(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf2); + rxTransfer1.LinkAfter(&rxTransfer2); + + // Create a transaction using header and linked transfers (using first in one) + TIicBusTransaction transaction(&header, &rxTransfer1); + + r = IicBus::QueueTransaction(busId, &transaction); + + // now, we has non-zero data in the buffer, but we know, that there was nothing connected + // so we should either receive 0x0 (or 0xff if line was driven high- unlikely)- but we'll check + // if rx buffer has simply different data in it.. + for (int i = 0; i < KBuffLength1; i++) + { + if(rxTransferBuf1[i] == i+1) + { + r = KErrCorrupt; + break; + } + } + + if(r == KErrNone) + { + for (int i = 0; i < KBuffLength2; i++) + { + if(rxTransferBuf2[i] == i+1) + { + r = KErrCorrupt; + break; + } + } + } + LOG_FUNCTION_RETURN; + return r; + } + + +// test half duplex read / write: +// - one transaction with more transfers - intermixed read / write +// - synchronous use - all buffers / transfer objects / transactions - on the stack +// (Data on stack - recommended for small transfers). +// This could serve as a simple example of a common spi usage for peripherals configuration, i.e.: +// write data - e.g. address of register and / or command(s) followed by read of their value(s) +TInt DSpiClientChannel::HalfDuplexMultipleWriteRead() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer objects + // (they don't have to be of the same size-it's just for simplicity here!) + const TInt KBuffLength = 32; + TBuf8 txTransferBuf1; // txbuffer1.. + TBuf8 txTransferBuf2; // txbuffer2.. + TBuf8 rxTransferBuf; // rxbuffer.. + + for (TInt i = 0; i < KBuffLength; ++i) + { + txTransferBuf1.Append(i+1); + txTransferBuf2.Append(63-i); + rxTransferBuf.Append(0xa5); // append some non-zero data-will check, if it's overwritten by read.. + } + // The above will also set size of buffers - and this is checked by the driver! + + // create two transfers and link them (transfer2 after transfer1) + TIicBusTransfer txTransfer1(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf1); + TIicBusTransfer txTransfer2(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf2); + txTransfer1.LinkAfter(&txTransfer2); // will link txTransfer2 after txTransfer1.. + + // create read transfer and link it after above two transfers + TIicBusTransfer rxTransfer(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf); + txTransfer2.LinkAfter(&rxTransfer); // will link rxTransfer after txTransfer2.. + + // Create a transaction using header and first of linked transfers + TIicBusTransaction transaction(&header, &txTransfer1); + + // and queue it synchronously.. + r = IicBus::QueueTransaction(busId, &transaction); + + LOG_FUNCTION_RETURN; + return r; + } + +// test full duplex with single* transfer i,e. simultenous write and read. +// - one half duplex transaction with one write transfer (for first direction-like usually) +// - one read buffer - that will be used to 'setup' a full duplex transaction (for the other direction) +// (Data on stack - recommended for small transfers). +// This could serve as a simple example of simultenous write and read from a bus +TInt DSpiClientChannel::FullDuplexSingle() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create transfer objects (they SHOULD be of the same size!) + const TInt KBuffLength = 64; + TBuf8 txTransferBuf; // txbuffer.. + TBuf8 rxTransferBuf; // rxbuffer.. + + // fill TX buffer with some data to send.. + // and the RX buffer - with some other data - to check if it's overwritten by read + // (this will also set buffers length's) + for (TInt i = 0; i < KBuffLength; ++i) + { + txTransferBuf.Append(i+1); + rxTransferBuf.Append(0x55); + } + + // create transfer objects.. + TIicBusTransfer txTransfer(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf); + TIicBusTransfer rxTransfer(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf); + + // Create transaction using header and list of transfers.. + TIicBusTransaction transaction(&header, &txTransfer); + + // setup a full duplex transaction - using Rx buffer + transaction.SetFullDuplexTrans(&rxTransfer); + + // queue the transaction + r = IicBus::QueueTransaction(busId, &transaction); + + // now confirm the reception.. + // BEAGLE BOARD only- if using local callback (ideally it should be used to fully test it) + // data in rx buffer should match the data in the tx buffer. + // otherwise (with no local loopback) - rx buffer should contain ZERO / (or different) data as it was + // initially filled with. (i.e. as nothing was driving the SOMI line - rx will count that as 0 sent over the bus) + // see top of this file and IsLoobackAvailable() function description for more details. + TBool checkReceivedMatchesSent = IsLoopbackAvailable(); + + if(!checkReceivedMatchesSent) + Kern::Printf("!Warning: %s (%d): not using local-loop for duplex test", __FILE__, __LINE__); + + for (int i = 0; i < KBuffLength; i++) + { + if(checkReceivedMatchesSent) + { + if (rxTransferBuf[i] != txTransferBuf[i]) + { + r = KErrCorrupt; + break; + } + } + else + { + if (rxTransferBuf[i] == 0x55) // this was the value used.. + { + r = KErrCorrupt; + break; + } + } + } + + LOG_FUNCTION_RETURN; + return r; + } + + +// test full duplex with multiple transfers +// - one half duplex transaction with two transfers: write followed by read +// - two buffers to form the other direction: read followed by write. +// (Data on stack - recommended for small transfers only). +// This could serve as a simple example of multi-transfer full duplex transactions. +TInt DSpiClientChannel::FullDuplexMultiple() + { + LOG_FUNCTION_ENTRY; + TInt r = KErrNone; + + TUint32 busId = 0; + SET_BUS_TYPE(busId, DIicBusChannel::ESpi); + SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3 + SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx) + + // create header + const TConfigSpiV01 KHeader = + { + ESpiWordWidth_8, //iWordWidth + 3000000, //iClkSpeed + ESpiPolarityLowRisingEdge, //iClkMode + 500, // iTimeoutPeriod + EBigEndian, // iEndianness + EMsbFirst, //iBitOrder + 0, //iTransactionWaitCycles + ESpiCSPinActiveLow //iCsPinActiveMode + }; + + TPckgBuf header(KHeader); + + // create buffers: + // for first duplex transfer + const TInt KBuffLength1 = 24; + TBuf8 txTransferBuf1; + TBuf8 rxTransferBuf1; + + // for second duplex transfer.. + // Note: Buffers for different transfers can have different size like below. + // the only requirement is - that for each duplex transfer(tx and rx) they need to have the same. + const TInt KBuffLength2 = 32; + TBuf8 rxTransferBuf2; + TBuf8 txTransferBuf2; + + // fill them with data.. + for (TInt i = 0; i < txTransferBuf1.MaxLength(); ++i) + { + txTransferBuf1.Append(i+1); + rxTransferBuf1.Append(0x55); + } + + for (TInt i = 0; i < txTransferBuf2.MaxLength(); ++i) + { + txTransferBuf2.Append(i+1); + rxTransferBuf2.Append(0xaa); + } + + // Now we want to chain transfers in the following way: + // txTransfer1 -> rxTransfer2 + // rxTransfer1 -> txTransfer2 + // note, that there could always be a read and write at the same time (in the same column) + + // first direction + TIicBusTransfer txTransfer1(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf1); + TIicBusTransfer rxTransfer1(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf1); + + // second transfer objects (they will run in parallel after above have finished) + TIicBusTransfer txTransfer2(TIicBusTransfer::EMasterWrite, 8, &txTransferBuf2); + TIicBusTransfer rxTransfer2(TIicBusTransfer::EMasterRead, 8, &rxTransferBuf2); + + // chain them as described above + txTransfer1.LinkAfter(&rxTransfer2); // rxTransfer2 after txTransfer1 + rxTransfer1.LinkAfter(&txTransfer2); // txTransfer2 after rxTransfer1 + + // Create a transaction using header and list of transfers.. + TIicBusTransaction transaction(&header, &txTransfer1); + transaction.SetFullDuplexTrans(&rxTransfer1); + + // and finally queue the transaction + r = IicBus::QueueTransaction(busId, &transaction); + + // now confirm the reception.. + // BEAGLE BOARD only- if using local callback (ideally it should be used to fully test it) + // data in rx buffer should match the data in the tx buffer. + // otherwise (with no local loopback) - rx buffer should contain zero / (or different) data as it was + // initially filled with. See top of this file and IsLoobackAvailable() function description for more details. + TBool checkReceivedMatchesSent = IsLoopbackAvailable(); + + if(!checkReceivedMatchesSent) + Kern::Printf("!Warning: %s (%d): not using local-loop for duplex test", __FILE__, __LINE__); + + // check first transfer + for (int i = 0; i < KBuffLength1; i++) + { + if(checkReceivedMatchesSent) + { + if (rxTransferBuf1[i] != txTransferBuf1[i]) + { + r = KErrCorrupt; + break; + } + } + else + { + if (rxTransferBuf1[i] == 0x55) // this was the value used.. + { + r = KErrCorrupt; + break; + } + } + } + + // check second transfer + for (int i = 0; i < KBuffLength2; i++) + { + if(checkReceivedMatchesSent) + { + if (rxTransferBuf2[i] != txTransferBuf2[i]) + { + r = KErrCorrupt; + break; + } + } + else + { + if (rxTransferBuf2[i] == 0xaa) // this was the value used.. + { + r = KErrCorrupt; + break; + } + } + } + + LOG_FUNCTION_RETURN; + return r; + } + +TInt DSpiClientChannel::HalfDuplexExtendable() + { + return KErrNotSupported; // TODO: not implemented yet.. + } + +TInt DSpiClientChannel::FullDuplexExtendable() + { + return KErrNotSupported; // TODO: not implemented yet.. + } + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/test/d_spi_client_m.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/test/d_spi_client_m.h Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,252 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530_drivers/spi/test/d_spi_client_m.h +// + +#ifndef __D_SPI_CLIENT_MASTER__ +#define __D_SPI_CLIENT_MASTER__ + +#include +#include + + +const TInt KIntTestThreadPriority = 24; + +const TInt KIntTestMajorVersionNumber = 1; +const TInt KIntTestMinorVersionNumber = 0; +const TInt KIntTestBuildVersionNumber = KE32BuildVersionNumber; + +_LIT(KLddFileName, "d_spi_client_m.ldd"); +_LIT(KLddFileNameRoot, "d_spi_client_m"); + +#ifdef __KERNEL_MODE__ +#include + +// For now - the driver has to know what frequencies are supported and has to specify them directly +// in the transaction header. This might be changes in the IIC PIL (kernelhwsrv)-in a way, that the driver +// returns supported frequencies with some 'GetCaps' method. For now - standard frequencies for CLK +// (that support duty cycle 50-50) are 48MHz divisions per power-of-two. +const TInt KSpiClkBaseFreqHz = 48000000; +const TInt KSpiClkMaxDivider = 4096; + +inline TInt SpiFreqHz(TInt aDivider) + { + __ASSERT_DEBUG(aDivider > 0 && aDivider < KSpiClkMaxDivider, Kern::Fault("d_spi_client_master: divider out of range", 13)); + __ASSERT_DEBUG(!(aDivider & (aDivider-1)), Kern::Fault("d_spi_client_master: divider not power of two", 14)); + return KSpiClkBaseFreqHz / aDivider; + } +#endif + +class RSpiClientTest: public RBusLogicalChannel + { +public: + enum TControl + { + EHalfDuplexSingleWrite, + EHalfDuplexMultipleWrite, + EHalfDuplexSingleRead, + EHalfDuplexMultipleRead, + EHalfDuplexMultipleWriteRead, + EFullDuplexSingle, + EFullDuplexMultiple, + EHalfDuplexExtendable, + EFullDuplexExtendable + }; + + enum TRequest + { + EAsyncHalfDuplexSingleWrite, + EAsyncHalfDuplexMultipleWrite, + EAsyncHalfDuplexSingleRead, + EAsyncHalfDuplexMultipleRead, + EAsyncHalfDuplexMultipleWriteRead, + EAsyncFullDuplexSingle, + EAsyncFullDuplexMultiple, + EAsyncHalfDuplexExtendable, + EAsyncFullDuplexExtendable + }; + +#ifndef __KERNEL_MODE__ +public: + TInt Open() + { + return (DoCreate(KLddFileNameRoot, + TVersion(KIntTestMajorVersionNumber,KIntTestMinorVersionNumber,KIntTestBuildVersionNumber), + -1, + NULL, + NULL, + EOwnerThread)); + } + + // Synchronous calls + TInt HalfDuplexSingleWrite() + {return DoControl(EHalfDuplexSingleWrite);} + + TInt HalfDuplexMultipleWrite() + {return DoControl(EHalfDuplexMultipleWrite);} + + TInt HalfDuplexSingleRead() + {return DoControl(EHalfDuplexSingleRead);} + + TInt HalfDuplexMultipleRead() + {return DoControl(EHalfDuplexMultipleRead);} + + TInt HalfDuplexMultipleWriteRead() + {return DoControl(EHalfDuplexMultipleWriteRead);} + + TInt FullDuplexSingle() + {return DoControl(EFullDuplexSingle);} + + TInt FullDuplexMultiple() + {return DoControl(EFullDuplexMultiple);} + + + // Asynchronous calls.. + void HalfDuplexSingleWrite(TRequestStatus& aStatus) + {DoRequest(EAsyncHalfDuplexSingleWrite, aStatus, NULL);} + + void HalfDuplexMultipleWrite(TRequestStatus& aStatus) + {DoRequest(EAsyncHalfDuplexMultipleWrite, aStatus, NULL);} + + void HalfDuplexSingleRead(TRequestStatus& aStatus) + {DoRequest(EAsyncHalfDuplexSingleRead, aStatus, NULL);} + + void HalfDuplexMultipleRead(TRequestStatus& aStatus) + {DoRequest(EAsyncHalfDuplexMultipleRead, aStatus, NULL);} + + void HalfDuplexMultipleWriteRead(TRequestStatus& aStatus) + {DoRequest(EAsyncHalfDuplexMultipleWriteRead, aStatus, NULL);} + + void FullDuplexSingle(TRequestStatus& aStatus) + {DoRequest(EAsyncFullDuplexSingle, aStatus, NULL);} + + void FullDuplexMultiple(TRequestStatus& aStatus) + {DoRequest(EAsyncFullDuplexMultiple, aStatus, NULL);} + +#endif + }; + +#ifdef __KERNEL_MODE__ +struct TCapsProxyClient + { + TVersion version; + }; + +class DSpiClientTestFactory: public DLogicalDevice + { +public: + DSpiClientTestFactory(); + ~DSpiClientTestFactory(); + virtual TInt Install(); + virtual void GetCaps(TDes8 &aDes) const; + virtual TInt Create(DLogicalChannelBase*& aChannel); + }; + +// declaration for the client channel +class DSpiClientChannel: public DLogicalChannel + { +public: + DSpiClientChannel(); + ~DSpiClientChannel(); + virtual TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer); + +protected: + static void Handler(TAny *aParam); + virtual void HandleMsg(TMessageBase* aMsg); // Note: this is a pure virtual in DLogicalChannel + TInt DoControl(TInt aId, TAny* a1, TAny* a2); + TInt DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2); + + // set of example transfers with transactions queued synchronously + TInt HalfDuplexSingleWrite(); + TInt HalfDuplexMultipleWrite(); + TInt HalfDuplexSingleRead(); + TInt HalfDuplexMultipleRead(); + TInt HalfDuplexMultipleWriteRead(); + TInt FullDuplexSingle(); + TInt FullDuplexMultiple(); + TInt HalfDuplexExtendable(); + TInt FullDuplexExtendable(); + + +private: + TRequestStatus iStatus; + DThread* iClient; + }; + +// Below is additional stuff for testing with local loopback +// the IsLoopbackAvailable function checks if McSPI3 is configured to use pins from extension header +// (Beagleboard) and if these pins are physically connected (e.g. with jumper)- in order to determine +// if duplex transfers should receive the same data that was sent to the bus.. +#include +#include +const TInt KSpi3_SIMO_Pin = 131; +const TInt KSpi3_SOMI_Pin = 132; + +inline TBool IsLoopbackAvailable() + { + // first check, if pad is configured to use SPI (this will confirm, if pins are used that way) + // this is their configuration (EMode1) + // CONTROL_PADCONF_MMC2_CLK, SCM::EMsw, SCM::EMode1, // mcspi3_simo + // CONTROL_PADCONF_MMC2_DAT0, SCM::ELsw, SCM::EMode1, // mcspi3_somi + TUint mode_MMC2_CLK = SCM::GetPadConfig(CONTROL_PADCONF_MMC2_CLK, SCM::EMsw); + TUint mode_MMC2_DAT0 = SCM::GetPadConfig(CONTROL_PADCONF_MMC2_DAT0, SCM::ELsw); + + if(!(mode_MMC2_CLK & SCM::EMode1) || + !(mode_MMC2_DAT0 & SCM::EMode1)) + { + return EFalse; // either other pins are used or SPI3 is not configured at all.. + } + + // swap pins to be GPIO (EMode4) + SCM::SetPadConfig(CONTROL_PADCONF_MMC2_CLK, SCM::EMsw, SCM::EMode4 | SCM::EInputEnable); + SCM::SetPadConfig(CONTROL_PADCONF_MMC2_DAT0, SCM::ELsw, SCM::EMode4 | SCM::EInputEnable); + + // set SIMO pin as output + GPIO::SetPinDirection(KSpi3_SIMO_Pin, GPIO::EOutput); + GPIO::SetPinMode(KSpi3_SIMO_Pin, GPIO::EEnabled); + + // and SOMI pin as input + GPIO::SetPinDirection(KSpi3_SOMI_Pin, GPIO::EInput); + GPIO::SetPinMode(KSpi3_SOMI_Pin, GPIO::EEnabled); + + TBool result = ETrue; + GPIO::TGpioState pinState = GPIO::EHigh; + + // test 1: set SIMO to ELow and check if SOMI is the same + GPIO::SetOutputState(KSpi3_SIMO_Pin, GPIO::ELow); + GPIO::GetInputState(KSpi3_SOMI_Pin, pinState); + if(pinState != GPIO::ELow) + { + result = EFalse; + } + else + { + // test 2: set SIMO to EHigh and check if SOMI is the same + GPIO::SetOutputState(KSpi3_SIMO_Pin, GPIO::EHigh); + GPIO::GetInputState(KSpi3_SOMI_Pin, pinState); + if(pinState != GPIO::EHigh) + { + result = EFalse; + } + } + + // restore back oryginal pad configuration for these pins + SCM::SetPadConfig(CONTROL_PADCONF_MMC2_CLK, SCM::EMsw, mode_MMC2_CLK); + SCM::SetPadConfig(CONTROL_PADCONF_MMC2_DAT0, SCM::ELsw, mode_MMC2_DAT0); + + return result; + } + +#endif /* __KERNEL_MODE__ */ + +#endif /* __D_SPI_CLIENT_MASTER__ */ diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/test/d_spi_client_m.mmp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/test/d_spi_client_m.mmp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,49 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530_drivers/spi/test/d_spi_client_m.mmp +// + +#define __USING_ASSP_REGISTER_API__ +#define __USING_ASSP_INTERRUPT_API__ + +#include +#include + +// link against the IIC controller library.. +library iic.lib + +target VariantTarget(d_spi_client_m,ldd) +romtarget d_spi_client_m.ldd + +// for testing only (for local loop-back detection) - link agains GPIO.. +library AsspTarget(gpio,lib) + +targettype ldd +sourcepath ./ +source d_spi_client_m.cpp + +OS_LAYER_SYSTEMINCLUDE +SYSTEMINCLUDE +\include\assp\omap3530_assp +SYSTEMINCLUDE +\include\drivers +SYMBIAN_BASE_SYSTEMINCLUDE(assp/omap3530_assp) +SYMBIAN_BASE_SYSTEMINCLUDE(drivers) +SYMBIAN_BASE_SYSTEMINCLUDE(kernel) + + +uid 0x100000af +vendorid 0x70000001 + +capability all +epocallowdlldata + +SMPSAFE diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/test/t_spi_client_m.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/test/t_spi_client_m.cpp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,112 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530_drivers/spi/test/t_spi_client_master.cpp +// +// user-side application for the simple SPI client (master) test driver. +// + +#include +#include +#include "d_spi_client_m.h" + + +// global data.. +_LIT(testName,"T_SPI_CLIENT_M"); +GLDEF_D RTest test(testName); + +GLDEF_D RSpiClientTest testLdd; // the actual driver.. + +void PrepareDriver() + { + // Load the Test Driver + TInt r = User::LoadLogicalDevice(KLddFileName); + if(r != KErrNone && r != KErrAlreadyExists) + { + // fail.. + test.Printf(_L("\nFailed to load the driver, r=%d"), r); + test(EFalse); + } + + // Open the driver + r = testLdd.Open(); + if(r != KErrNone) + { + test.Printf(_L("Failed to open the driver..\n\r")); + test(EFalse); + } + } + +void ReleaseDriver() + { + // Close the driver + testLdd.Close(); + + // unload the driver + User::FreeLogicalDevice(KLddFileName); + } + +inline void TestError(TInt r) + { + if(r == KErrNotSupported) // this is to warn stuf not yet implemented (TDD) + { + test.Printf(_L("!! Warning: not implemented yet..\n\r")); + } + else + { + test(r == KErrNone); + } + } + +void TestSynchronousOperation() + { + test.Next(_L("TestSynchronousOperation()")); + + test.Next(_L("HalfDuplexSingleWrite()")); + TestError(testLdd.HalfDuplexSingleWrite()); + + test.Next(_L("HalfDuplexMultipleWrite()")); + TestError(testLdd.HalfDuplexMultipleWrite()); + + test.Next(_L("HalfDuplexSingleRead()")); + TestError(testLdd.HalfDuplexSingleRead()); + + test.Next(_L("HalfDuplexMultipleRead()")); + TestError(testLdd.HalfDuplexMultipleRead()); + + test.Next(_L("HalfDuplexMultipleWriteRead()")); + TestError(testLdd.HalfDuplexMultipleWriteRead()); + + test.Next(_L("FullDuplexSingle()")); + TestError(testLdd.FullDuplexSingle()); + + test.Next(_L("FullDuplexMultiple()")); + TestError(testLdd.FullDuplexMultiple()); + } + + +TInt E32Main() + { + test.Title(); + test.Start(_L("Testing SPI..")); + + PrepareDriver(); + + TestSynchronousOperation(); + + ReleaseDriver(); + + test.End(); + + return KErrNone; + } + diff -r 29b14275133a -r e5fd00cbb70a omap3530/omap3530_drivers/spi/test/t_spi_client_m.mmp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omap3530/omap3530_drivers/spi/test/t_spi_client_m.mmp Tue Sep 21 02:30:11 2010 +0100 @@ -0,0 +1,31 @@ +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// lukasz.forynski@gmail.com +// +// Contributors: +// +// +// Description: +// omap3530_drivers/spi/test/t_spi_client_m.mmp +// + +target t_spi_client_m.exe +targettype exe + +sourcepath . +source t_spi_client_m.cpp + +OS_LAYER_SYSTEMINCLUDE_SYMBIAN +SYMBIAN_BASE_SYSTEMINCLUDE(nkern) + +library euser.lib hal.lib + +capability all + + +VENDORID 0x70000001 +