From: Masayuki Ohtake Subject: OKI Semiconductor PCH SPI driver This driver implements SPI controls for PCH. Signed-off-by: Masayuki Ohtake Acked-by: Wang Qi --- drivers/spi/Kconfig | 19 ++ drivers/spi/Makefile | 3 drivers/spi/pch_common.h | 146 drivers/spi/pch_spi.h | 389 drivers/spi/pch_spi_hal.h | 298 drivers/spi/pch_spi_pci.c | 812 drivers/spi/pch_debug.h | 60 drivers/spi/pch_spi_hal.c | 1208 drivers/spi/pch_spi_main.c | 1323 drivers/spi/pch_spi_platform_devices.c | 50 +++++++++++++++++++++++++++++++ 10 files changed, yyy insertions(+) diff -urN linux-2.6.33-rc3/drivers/spi/Kconfig topcliff-2.6.33-rc3/drivers/spi/Kconfig --- linux-2.6.33-rc3/drivers/spi/Kconfig 2010-01-06 09:02:46.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/Kconfig 2010-03-06 07:48:16.000000000 +0900 @@ -53,6 +53,25 @@ comment "SPI Master Controller Drivers" +config PCH_SPI_PLATFORM_DEVICE + bool "PCH SPI Device" +# depends on PCH_SPI + help + This registers SPI devices for using with PCH SPI controllers. + +config PCH_SPI_PLATFORM_DEVICE_COUNT + int "PCH SPI Bus count" + range 1 2 + depends on PCH_SPI_PLATFORM_DEVICE + help + The number of SPI buses/channels supported by the PCH SPI controller. + +config PCH_SPI + tristate "PCH SPI Controller" + depends on (PCI) && PCH_SPI_PLATFORM_DEVICE + help + This selects a driver for the PCH SPI Controller + config SPI_ATMEL tristate "Atmel SPI Controller" depends on (ARCH_AT91 || AVR32) diff -urN linux-2.6.33-rc3/drivers/spi/Makefile topcliff-2.6.33-rc3/drivers/spi/Makefile --- linux-2.6.33-rc3/drivers/spi/Makefile 2010-01-06 09:02:46.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/Makefile 2010-03-06 01:52:28.000000000 +0900 @@ -59,3 +59,6 @@ # SPI slave drivers (protocol for that link) # ... add above this line ... +obj-$(CONFIG_PCH_SPI) += pch_spi.o +pch_spi-objs := pch_spi_pci.o pch_spi_hal.o pch_spi_main.o +obj-$(CONFIG_PCH_SPI_PLATFORM_DEVICE) +=pch_spi_platform_devices.o diff -urN linux-2.6.33-rc3/drivers/spi/pch_common.h topcliff-2.6.33-rc3/drivers/spi/pch_common.h --- linux-2.6.33-rc3/drivers/spi/pch_common.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_common.h 2010-03-09 05:56:11.000000000 +0900 @@ -0,0 +1,146 @@ +/*! + * @file ioh_common.h + * @brief Provides the macro definitions used by all files. + * @version 1.0.0.0 + * @section + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * History: + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * created: + * WIPRO 03/07/2009 + * modified: + * WIPRO 05/08/2009 + * + */ + +#ifndef __IOH_COMMON_H__ +#define __IOH_COMMON_H__ + +/*! @ingroup Global +@def IOH_WRITE8 +@brief Macro for writing 8 bit data to an io/mem address +*/ +#define IOH_WRITE8(val, addr) iowrite8((val), (void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_LOG +@brief Macro for writing 16 bit data to an io/mem address +*/ +#define IOH_WRITE16(val, addr) iowrite16((val), (void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_LOG +@brief Macro for writing 32 bit data to an io/mem address +*/ +#define IOH_WRITE32(val, addr) iowrite32((val), (void __iomem *)(addr)) + +/*! @ingroup Global +@def IOH_READ8 +@brief Macro for reading 8 bit data from an io/mem address +*/ +#define IOH_READ8(addr) ioread8((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_READ16 +@brief Macro for reading 16 bit data from an io/mem address +*/ +#define IOH_READ16(addr) ioread16((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_READ32 +@brief Macro for reading 32 bit data from an io/mem address +*/ +#define IOH_READ32(addr) ioread32((void __iomem *)(addr)) +/*! @ingroup Global +@def IOH_WRITE32_F +@brief Macro for writing 32 bit data to an io/mem address +*/ +#define IOH_WRITE32_F(val, addr) do \ + { IOH_WRITE32((val), (addr)); (void)IOH_READ32((addr)); } while (0); + +/*! @ingroup Global +@def IOH_WRITE_BYTE +@brief Macro for writing 1 byte data to an io/mem address +*/ +#define IOH_WRITE_BYTE IOH_WRITE8 +/*! @ingroup Global +@def IOH_WRITE_WORD +@brief Macro for writing 1 word data to an io/mem address +*/ +#define IOH_WRITE_WORD IOH_WRITE16 +/*! @ingroup Global +@def IOH_WRITE_LONG +@brief Macro for writing long data to an io/mem address +*/ +#define IOH_WRITE_LONG IOH_WRITE32 + +/*! @ingroup Global +@def IOH_READ_BYTE +@brief Macro for reading 1 byte data from an io/mem address +*/ +#define IOH_READ_BYTE IOH_READ8 +/*! @ingroup Global +@def IOH_READ_WORD +@brief Macro for reading 1 word data from an io/mem address +*/ +#define IOH_READ_WORD IOH_READ16 +/*! @ingroup Global +@def IOH_READ_LONG +@brief Macro for reading long data from an io/mem address +*/ +#define IOH_READ_LONG IOH_READ32 + +/* Bit Manipulation Macros */ + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bit(mask) at the + specified address +*/ +#define IOH_SET_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) |\ + (bitmask)), (addr)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bit(mask) at the specified address +*/ +#define IOH_CLR_ADDR_BIT(addr, bitmask) IOH_WRITE_LONG((IOH_READ_LONG(addr) &\ + ~(bitmask)), (addr)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bitmask for a variable +*/ +#define IOH_SET_BITMSK(var, bitmask) ((var) |= (bitmask)) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bitmask for a variable +*/ +#define IOH_CLR_BITMSK(var, bitmask) ((var) &= (~(bitmask))) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to set a specified bit for a variable +*/ +#define IOH_SET_BIT(var, bit) ((var) |= (1<<(bit))) + +/*! @ingroup Global +@def IOH_READ_LONG +@brief macro to clear a specified bit for a variable +*/ +#define IOH_CLR_BIT(var, bit) ((var) &= ~(1<<(bit))) + +#endif diff -urN linux-2.6.33-rc3/drivers/spi/pch_debug.h topcliff-2.6.33-rc3/drivers/spi/pch_debug.h --- linux-2.6.33-rc3/drivers/spi/pch_debug.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_debug.h 2010-03-09 05:37:47.000000000 +0900 @@ -0,0 +1,60 @@ +/*! + * @file ioh_debug.h + * @brief Provides the macro definitions used for debugging. + * @version 1.0.0.0 + * @section + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * History: + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * created: + * WIPRO 03/07/2009 + * modified: + * WIPRO 05/08/2009 + * + */ + +#ifndef __IOH_DEBUG_H__ +#define __IOH_DEBUG_H__ + +#ifdef MODULE +#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n",\ + THIS_MODULE->name, ##args) +#else +#define IOH_LOG(level, fmt, args...) printk(level "%s:" fmt "\n" ,\ + __FILE__, ##args) +#endif + + +#ifdef DEBUG + #define IOH_DEBUG(fmt, args...) IOH_LOG(KERN_DEBUG, fmt, ##args) +#else + #define IOH_DEBUG(fmt, args...) +#endif + +#ifdef IOH_TRACE_ENABLED + #define IOH_TRACE IOH_DEBUG +#else + #define IOH_TRACE(fmt, args...) +#endif + +#define IOH_TRACE_ENTER IOH_TRACE("Enter %s", __func__) +#define IOH_TRACE_EXIT IOH_TRACE("Exit %s", __func__) + + +#endif diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi.h topcliff-2.6.33-rc3/drivers/spi/pch_spi.h --- linux-2.6.33-rc3/drivers/spi/pch_spi.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi.h 2010-03-06 09:01:42.000000000 +0900 @@ -0,0 +1,389 @@ +#ifndef __IOH_SPI_H__ +#define __IOH_SPI_H__ +/** + * @file ioh_spi.h + * + * @brief This header file contains all macro,structure and function + * declarations + * for IOH SPI driver. + * @version 0.94 + * + * @par + * -- Copyright Notice -- + * + * @par + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * @par + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @par + * -- End of Copyright Notice -- + */ + +/*! @defgroup SPI */ + +/*! @defgroup SPI_Global +@ingroup SPI +@brief This group describes the global entities within + the module. +@remarks This group includes all the global data structures + used within the modules. These are mainly used to + store the device related information, so that it can + be used by other functions of the modules. +
+*/ + +/*! @defgroup SPI_PCILayer +@ingroup SPI +@brief This group describes the PCI layer interface + functionalities. +@remarks This group contains the functions and data structures + that are used to interface the module with PCI Layer + subsystem of the Kernel. +
+*/ + +/*! @defgroup SPI_InterfaceLayer +@ingroup SPI +@brief This group describes the Driver interface functionalities. +@remarks This group contains the data structures and functions used + to interface the module driver with the kernel subsystem. +
+*/ + +/*! @defgroup SPI_HALLayer +@ingroup SPI +@brief This group describes the hardware specific functionalities. +@remarks This group contains the functions and data structures used + by the module to communicate with the hardware. These + functions are device specific and designed according to the + device specifications. +
+*/ + +/*! @defgroup SPI_Utilities +@ingroup SPI +@brief This group describes the utility functionalities. +@remarks This group contains the functions and data structures used + to assist the other functionalities in their operations. +
+*/ + +/*! @defgroup SPI_PCILayerAPI +@ingroup SPI_PCILayer +@brief This group contains the API(functions) used as the PCI + interface between the Kernel subsystem and the module. +
+*/ + +/*! @defgroup SPI_PCILayerFacilitators +@ingroup SPI_PCILayer +@brief This group contains the data structures used by the PCI + Layer APIs for their functionalities. +
+*/ + +/*! @defgroup SPI_InterfaceLayerAPI +@ingroup SPI_InterfaceLayer +@brief This group contains the API(functions) used as the Driver + interface between the Kernel subsystem and the module. +
+*/ + +/*! @defgroup SPI_InterfaceLayerFacilitators +@ingroup SPI_InterfaceLayer +@brief This group contains the data structures used by the Driver + interface APIs for their functionalities. +
+*/ + +/*! @defgroup SPI_HALLayerAPI +@ingroup SPI_HALLayer +@brief This group contains the APIs(functions) used to interact with + the hardware. These APIs act as an interface between the + hardware and the other driver functions. +
+*/ + +/*! @defgroup SPI_UtilitiesAPI +@ingroup SPI_Utilities +@brief This group contains the APIs(functions) used by other functions + in their operations. +
+*/ + +#include +#include +#include +#include +#include + +/*! @ingroup SPI_Global + +@def STATUS_RUNNING + +@brief SPI channel is running + +@note The status of SPI channel is set to STATUS_RUNNING, + once all resources are acquired and initialized from + @ref ioh_spi_get_resources + +@see + - ioh_spi_get_resources + +
+*/ +#define STATUS_RUNNING (1) + +/*! @ingroup SPI_Global + +@def STATUS_EXITING + +@brief SPI device is being removed + +@note The status of SPI channel is set to STATUS_EXITING, + when SPI device is being removed. + +@see + - ioh_spi_process_messages + - ioh_spi_check_request_pending + +
+*/ +#define STATUS_EXITING (2) + +/*! @ingroup SPI_Global + +@def DRIVER_NAME + +@brief Name identifier for IOH SPI driver + +@note This name is used while printing debug logs + +
+*/ +#define DRIVER_NAME "ioh_spi" + +/*! @ingroup SPI_Global + +@def IOH_SPI_SLEEP_TIME + +@brief Sleep time used in @ref ioh_spi_check_request_pending + +@see + - ioh_spi_check_request_pending + +
+*/ +#define IOH_SPI_SLEEP_TIME (10) + +/*! @ingroup SPI_Global + +@def IOH_SPI_MAX_DEV + +@brief Denotes Maximum number of SPI channels + +@note This needs to be edited if number of SPI channels + change. + +@see + - ioh_spi_get_resources + - ioh_spi_free_resources + - ioh_spi_handler + - ioh_spi_check_request_pending + - ioh_spi_probe + - ioh_spi_suspend + - ioh_spi_resume + - ioh_spi_remove + +
+*/ +#ifdef IOH_DEVICE_GE +#define IOH_SPI_MAX_DEV (1) +#else +#define IOH_SPI_MAX_DEV (1) +#endif + +/*! @ingroup SPI_Global + +@def IOH_SPI_ADDRESS_SIZE + +@brief Denotes the address range used by one SPI channel. + +@note The base address of a subsequent SPI channel will be + (base address of the previous SPI channel) + (IOH_SPI_ADDRESS_SIZE) + This needs to be recalculated if any new register is added to a SPI + channel. + +@see + - ioh_spi_get_resources + +
+*/ +#define IOH_SPI_ADDRESS_SIZE (0x20) + +/*structures*/ + +/*! @ingroup SPI_Global +@struct ioh_spi_data +@brief Holds the SPI channel specific details + + This structure holds all the details related to a SPI channel + + The status of SPI data transfer,the base address are all + stored in this structure.The reference to the work queue handler, + the SPI message and transmit and receive indices are also stored + in this structure. + +@see + - ioh_spi_board_data + - ioh_spi_select_chip + - ioh_spi_deselect_chip + - ioh_spi_transfer + - ioh_spi_process_messages +
+*/ + +struct ioh_spi_data { + + u32 IORemapAddress; /**< The remapped PCI base address.*/ + + /**< The SPI master structure that has been registered + with the Kernel.*/ + struct spi_master *pMaster; + + struct work_struct Work; /**< Reference to work queue handler*/ + + /**< Workqueue for carrying out execution of the requests*/ + struct workqueue_struct *pWorkQueue; + + /**< Wait queue for waking up upon receiving an interrupt.*/ + wait_queue_head_t Wait; + + u8 bTransferComplete; /**< Status of SPI Transfer*/ + u8 bCurrent_msg_processing; /**< Status flag for message processing*/ + + spinlock_t Lock; /**< Lock for protecting this structure*/ + + struct list_head Queue; /**< SPI Message queue*/ + u8 Status; /**< Status of the SPI driver.*/ + + u32 lengthInBpw;/**< Length of data to be transferred in bits per word*/ + s8 bTransferActive; /**< Flag showing active transfer*/ + u32 TxIndex;/**< Transmit data count; for bookkeeping during transfer*/ + u32 RxIndex;/**< Receive data count; for bookkeeping during transfer*/ + u16 *pU16TxBuffer; /**< Data to be transmitted*/ + u16 *pU16RxBuffer; /**< Received data*/ + +/**< The chip number that this SPI driver currently operates on*/ + u8 nCurrentChip; + + /**< Reference to the current chip that this SPI driver currently + operates on*/ + struct spi_device *pCurrentChip; + + /**< The current message that this SPI driver is handling*/ + struct spi_message *pCurMsg; + + /**< The current transfer that this SPI driver is handling*/ + struct spi_transfer *pCurTransfer; + + /**< Reference to the SPI device data structure*/ + struct ioh_spi_board_data *pBoardData; +}; + +/*! @ingroup SPI_Global +@struct ioh_spi_board_data +@brief Holds the SPI device specific details + + This structure holds all the details related to a SPI device. + + The reference to the pci_dev structure,status of request_irq, + pci_request_regions and device suspend are all stored in this structure. + + This structure also has an array of pointers to ioh_spi_data structures, + with each pointer holding the details of one spi channel. + +@see + - ioh_spi_data + - ioh_spi_check_request_pending + - ioh_spi_get_resources + - ioh_spi_free_resources + - ioh_spi_remove + - ioh_spi_suspend + - ioh_spi_resume + - ioh_spi_probe + - ioh_spi_handler +
+*/ + +struct ioh_spi_board_data { + + struct pci_dev *pDev; /**< Reference to the PCI device*/ + u8 bIrqRegistered; /**< Status of IRQ registration*/ + u8 bRegionRequested; /**< Status of pci_request_regions*/ + u8 bSuspended; /**< Status of suspend*/ + + /**< Reference to SPI channel data structure*/ + struct ioh_spi_data *pCtrlData[IOH_SPI_MAX_DEV]; +}; + +/*function prototypes*/ + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_callback( struct ioh_spi_data* pCtrlData) +@brief Callback function +*/ +void ioh_spi_callback(struct ioh_spi_data *pCtrlData); + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_free_resources(struct ioh_spi_board_data* pBoardData) +@brief Frees the resources acquired by IOH SPI driver +*/ +void ioh_spi_free_resources(struct ioh_spi_board_data *pBoardData); + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_check_request_pending(struct ioh_spi_board_data* pBoardData) +@brief Checks for any pending SPI transfer request in the queue of pending + transfers +*/ +int ioh_spi_check_request_pending(struct ioh_spi_board_data *pBoardData); + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_get_resources(struct ioh_spi_board_data* pBoardData) +@brief Acquires the resources for IOH SPI driver +*/ +int ioh_spi_get_resources(struct ioh_spi_board_data *pBoardData); + +/*! @ingroup SPI_InterfaceLayerAPI +@fn ioh_spi_setup(struct spi_device* pSpi) +@brief Implements the setup routine for IOH SPI driver +*/ +int ioh_spi_setup(struct spi_device *pSpi); + +/*! @ingroup SPI_InterfaceLayerAPI +@fn ioh_spi_transfer(struct spi_device* pSpi,struct spi_message* pMsg) +@brief Implements the transfer routine for IOH SPI driver +*/ +int ioh_spi_transfer(struct spi_device *pSpi, struct spi_message *pMsg); + +/*! @ingroup SPI_InterfaceLayerAPI +@fn ioh_spi_cleanup(struct spi_device* pSpi) +@brief Implements the cleanup routine for IOH SPI driver +*/ +void ioh_spi_cleanup(struct spi_device *pSpi); + +#endif diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi_hal.c topcliff-2.6.33-rc3/drivers/spi/pch_spi_hal.c --- linux-2.6.33-rc3/drivers/spi/pch_spi_hal.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi_hal.c 2010-03-09 00:41:44.000000000 +0900 @@ -0,0 +1,1208 @@ +/** + * @file ioh_spi_hal.c + * + * @brief This file defines the HAL methods . + * + * @version 0.94 + * + * @par + * -- Copyright Notice -- + * + * @par + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * @par + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @par + * -- End of Copyright Notice -- + */ + +#include +#include +#include "pch_common.h" +#include "pch_debug.h" +#include "pch_spi.h" +#include "pch_spi_hal.h" + +/*bit positions in SPCR*/ + +/*! @ingroup SPI_HALLayer +@def SPCR_SPE_BIT +@brief SPE bit position in SPCR +@see + - ioh_spi_set_enable +*/ +#define SPCR_SPE_BIT (1 << 0) + +/*! @ingroup SPI_HALLayer +@def SPCR_MSTR_BIT +@brief MSTR bit position in SPCR +@see + - ioh_spi_set_master_mode +*/ +#define SPCR_MSTR_BIT (1 << 1) + +/*! @ingroup SPI_HALLayer +@def SPCR_LSBF_BIT +@brief LSBF bit position in SPCR +@see + - ioh_spi_setup_transfer +*/ +#define SPCR_LSBF_BIT (1 << 4) + +/*! @ingroup SPI_HALLayer +@def SPCR_CPHA_BIT +@brief CPHA bit position in SPCR +@see + - ioh_spi_setup_transfer +*/ +#define SPCR_CPHA_BIT (1 << 5) + +/*! @ingroup SPI_HALLayer +@def SPCR_CPOL_BIT +@brief CPOL bit position in SPCR +@see + - ioh_spi_setup_transfer +*/ +#define SPCR_CPOL_BIT (1 << 6) + +/*! @ingroup SPI_HALLayer +@def SPCR_TFIE_BIT +@brief TFIE bit position in SPCR +@see + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts +*/ +#define SPCR_TFIE_BIT (1 << 8) + +/*! @ingroup SPI_HALLayer +@def SPCR_RFIE_BIT +@brief RFIE bit position in SPCR +@see + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts +*/ +#define SPCR_RFIE_BIT (1 << 9) + +/*! @ingroup SPI_HALLayer +@def SPCR_FIE_BIT +@brief FIE bit position in SPCR +@see + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts +*/ +#define SPCR_FIE_BIT (1 << 10) + +/*! @ingroup SPI_HALLayer +@def SPCR_ORIE_BIT +@brief ORIE bit position in SPCR +@see + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts +*/ +#define SPCR_ORIE_BIT (1 << 11) + +/*! @ingroup SPI_HALLayer +@def SPCR_MDFIE_BIT +@brief MDFIE bit position in SPCR +@see + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts +*/ +#define SPCR_MDFIE_BIT (1 << 12) + +/*! @ingroup SPI_HALLayer +@def SPCR_FICLR_BIT +@brief FICLR bit position in SPCR +@see + - ioh_spi_clear_fifo +*/ +#define SPCR_FICLR_BIT (1 << 24) + +/*bit positions in SPSR*/ + +/*! @ingroup SPI_HALLayer +@def SPSR_TFI_BIT +@brief TFI bit position in SPCR +*/ +#define SPSR_TFI_BIT (1 << 0) + +/*! @ingroup SPI_HALLayer +@def SPSR_RFI_BIT +@brief RFI bit position in SPCR +@see + - ioh_spi_handler +*/ +#define SPSR_RFI_BIT (1 << 1) + +/*! @ingroup SPI_HALLayer +@def SPSR_FI_BIT +@brief FI bit position in SPCR +@see + - ioh_spi_handler +*/ +#define SPSR_FI_BIT (1 << 2) + +/*bit positions in SPBRR*/ + +/*! @ingroup SPI_HALLayer +@def SPBRR_SIZE_BIT +@brief SIZE bit position in SPCR +@see + - ioh_spi_set_bits_per_word +*/ +#define SPBRR_SIZE_BIT (1 << 10) + +/*! @ingroup SPI_HALLayer +@def SPCR_RFIC_FIELD +@brief RFIC field in SPCR +@see + - ioh_spi_set_threshold +*/ +#define SPCR_RFIC_FIELD (20) + +/*! @ingroup SPI_HALLayer +@def SPCR_TFIC_FIELD +@brief TFIC field in SPCR +@see + - ioh_spi_set_threshold +*/ +#define SPCR_TFIC_FIELD (16) + +/*! @ingroup SPI_HALLayer +@def SPSR_INT_BITS +@brief Mask for all interrupt bits in SPSR +@see + - ioh_spi_reset +*/ +#define SPSR_INT_BITS (0x1F) + +/*! @ingroup SPI_HALLayer +@def MASK_SPBRR_SPBR_BITS +@brief Mask for clearing SPBR in SPBRR +@see + - ioh_spi_set_baud_rate +*/ +#define MASK_SPBRR_SPBR_BITS (0xFFFFFC00) + +/*! @ingroup SPI_HALLayer +@def MASK_RFIC_SPCR_BITS +@brief Mask for Rx threshold in SPCR +@see + - ioh_spi_set_threshold +*/ +#define MASK_RFIC_SPCR_BITS (0xFF0FFFFF) + +/*! @ingroup SPI_HALLayer +@def MASK_TFIC_SPCR_BITS +@brief Mask for Tx threshold in SPCR +@see + - ioh_spi_set_threshold +*/ +#define MASK_TFIC_SPCR_BITS (0xFFF0FFF) + +/*! @ingroup SPI_HALLayer +@def IOH_CLOCK_HZ +@brief Pclock Freqeuncy +@see + - ioh_spi_set_baud_rate +*/ +#ifndef FPGA + /*LSI*/ +#define IOH_CLOCK_HZ (50000000) +#else + /*FPGA*/ +#define IOH_CLOCK_HZ (62500000) +#endif +/*! @ingroup SPI_HALLayer +@def IOH_SPI_MAX_SPBR +@brief Maximum value possible for SPBR in SPBRR +@see + - ioh_spi_set_baud_rate +*/ +#define IOH_SPI_MAX_SPBR (1023) +/*global*/ +/*! @ingroup SPI_HALLayer + +@var ioh_spi_gcbptr + +@brief SPI_Global function pointer to store reference of + callback function @ref + ioh_spi_callback + +@note The reference of callback function is assigend to this + pointer + from @ref ioh_spi_probe function by invoking + the function @ref ioh_spi_entcb. + This global variable is used by the function + @ref ioh_spi_hanlder + to invoke the callback function. + +@see + - ioh_spi_entcb + - ioh_spi_handler + +
+ +*/ +static void (*ioh_spi_gcbptr) (struct ioh_spi_data *); + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_set_master_mode( struct spi_master *master) + +@remarks Sets the MSTR bit in SPCR + + The main task performed by this method: + - Read the content of SPCR register + - Set the MSTR bit + - Write back the value to SPCR + +@note This function is invoked from @ref ioh_spi_probe to put the IOH SPI + device into master mode. + +@param master [@ref IN] Contains reference to struct spi_master + +@retval None + +@see + - ioh_spi_probe + +
+ +*/ +void ioh_spi_set_master_mode(struct spi_master *master) +{ + u32 reg_spcr_val; + reg_spcr_val = ioh_spi_readreg(master, IOH_SPI_SPCR); + IOH_DEBUG("ioh_spi_set_master_mode SPCR content=%x\n", reg_spcr_val); + + /*sets the second bit of SPCR to 1:master mode */ + IOH_SET_BITMSK(reg_spcr_val, SPCR_MSTR_BIT); + + /*write the value to SPCR register */ + ioh_spi_writereg(master, IOH_SPI_SPCR, reg_spcr_val); + IOH_DEBUG("ioh_spi_set_master_mode SPCR after setting MSTR bit=%x\n", + reg_spcr_val); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_set_enable(const struct spi_device *spi, u8 enable) + +@remarks Sets/Resets the SPE bit in SPCR + + The main tasks performed by this method are: + - Read the content of SPCR. + - If the enable parameter is true , set the SPE bit. + - If the enable paramter is false , clear the SPE bit. + - Write back the value to SPCR. + +@note This function is invoked by @ref ioh_spi_process_messages to enable SPI + transfer before start of SPI data transfer and to disable SPI data + transfer + after completion of SPI data transfer. + +@param spi [@ref IN] Contains reference to struct spi_device + +@param enable [@ref IN] + To enable SPI transfer enable = true + To disable SPI transfer enable = false + +@retval None + +@see + - ioh_spi_process_messages + +
+ +*/ +void ioh_spi_set_enable(const struct spi_device *spi, u8 enable) +{ + u32 reg_spcr_val; + + reg_spcr_val = ioh_spi_readreg(spi->master, IOH_SPI_SPCR); + IOH_DEBUG("ioh_spi_set_enable SPCR content=%x\n", reg_spcr_val); + + if (enable == true) { + IOH_DEBUG("ioh_spi_set_enable enable==true\n"); + IOH_SET_BITMSK(reg_spcr_val, SPCR_SPE_BIT); + } else { + IOH_DEBUG("ioh_spi_set_enable enable==false\n"); + IOH_CLR_BITMSK(reg_spcr_val, SPCR_SPE_BIT); + } + + ioh_spi_writereg(spi->master, IOH_SPI_SPCR, reg_spcr_val); + + IOH_DEBUG("ioh_spi_set_enable SPCR content after modifying SPE=%x\n", + reg_spcr_val); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_handler(int irq, void* dev_id) + +@remarks Interrupt handler + +The main tasks performed by this method are: +- Check if Corresponding interrupt bits are set in SPSR register. +- If no, return IRQ_NONE. +- If yes, read the number of bytes received and write required number of bytes +according to space available. +- Update all bookkeeping variables. +- If bytes/words to be received is less than 16bytes/words,then disable RFI +and set Rx threshold to 16 bytes/words. +- If SPI data transfer is completed, invoke the callback function +@ref ioh_spi_callback to inform the status to @ref ioh_spi_process_messages. +- Repeat for all SPI channels. + +@note +This is the interrupt handler for IOH SPI controller driver.This function is +invoked by the kernel when any interrupt occurs on the interrupt line shared by +IOH SPI device. The SPI data transfer is initiated by @ref ioh_spi_process_ +messages,but is carried on by this function.For optimised operation,the HAL +functions to read and write registers are not used in this function. +Also register +address calculation is done once at the beginning to avoid the calculation each +time while accessing registers. + +@param irq [@ref IN] The interrupt number + +@param dev_id [@ref IN] Contains reference to struct ioh_spi_board_data + +@retval irqreturn_t + - IRQ_NONE The interrupt is not ours + - IRQ_HANDLED The interrupt has been serviced + +@see + - ioh_spi_get_resources + - ioh_spi_free_resources + - ioh_spi_suspend + - ioh_spi_resume + +
+ +*/ +irqreturn_t ioh_spi_handler(int irq, void *dev_id) +{ + /*channel & read/write indices */ + int dev, readcnt; + + /*SPSR content */ + u32 reg_spsr_val, reg_spcr_val; + + /*book keeping variables */ + u32 nReadable, TxIndex, RxIndex, lengthInBpw; + + /*to hold channel data */ + + struct ioh_spi_data *pCtrlData; + + /*buffer to store rx/tx data */ + u16 *pU16RxBuffer, *pU16TxBuffer; + + /*register addresses */ + u32 SPSR, SPDRR, SPDWR; + + /*remapped pci base address */ + u32 IORemapAddress; + + irqreturn_t tRetVal = IRQ_NONE; + + struct ioh_spi_board_data *pBoardData = + (struct ioh_spi_board_data *)dev_id; + + if (pBoardData->bSuspended == true) { + IOH_DEBUG("ioh_spi_handler returning due to suspend\n"); + } else { + for (dev = 0; dev < IOH_SPI_MAX_DEV; dev++) { + pCtrlData = pBoardData->pCtrlData[dev]; + IORemapAddress = pCtrlData->IORemapAddress; + SPSR = IORemapAddress + IOH_SPI_SPSR; + + reg_spsr_val = IOH_READ_LONG(SPSR); + + /*Check if the interrupt is for SPI device */ + + if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) { + IOH_DEBUG("SPSR in ioh_spi_handler=%x\n", + reg_spsr_val); + /*clear interrupt */ + IOH_WRITE_LONG(reg_spsr_val, SPSR); + + if (pCtrlData->bTransferActive == true) { + RxIndex = pCtrlData->RxIndex; + TxIndex = pCtrlData->TxIndex; + lengthInBpw = pCtrlData->lengthInBpw; + pU16RxBuffer = pCtrlData->pU16RxBuffer; + pU16TxBuffer = pCtrlData->pU16TxBuffer; + + SPDRR = IORemapAddress + IOH_SPI_SPDRR; + SPDWR = IORemapAddress + IOH_SPI_SPDWR; + + nReadable = + IOH_SPI_READABLE(reg_spsr_val); + + for (readcnt = 0; (readcnt < nReadable); + readcnt++) { + /*read data */ + pU16RxBuffer[RxIndex++] = + IOH_READ_LONG(SPDRR); + /*write data */ + + if (TxIndex < lengthInBpw) { + IOH_WRITE_LONG + (pU16TxBuffer + [TxIndex++], + SPDWR); + } + } + + /*disable RFI if not needed */ + if ((lengthInBpw - RxIndex) <= + IOH_SPI_MAX_FIFO_DEPTH) { + IOH_DEBUG + ("ioh_spi_handler disabling\ + RFI as data remaining=%d\n", + (lengthInBpw - RxIndex)); + + reg_spcr_val = + IOH_READ_LONG(IORemapAddress + + + IOH_SPI_SPCR); + + /*disable RFI */ + IOH_CLR_BITMSK(reg_spcr_val, + SPCR_RFIE_BIT); + + /*reset rx threshold */ + reg_spcr_val &= + MASK_RFIC_SPCR_BITS; + reg_spcr_val |= + (IOH_SPI_RX_THOLD_MAX << + SPCR_RFIC_FIELD); + + IOH_WRITE_LONG(IOH_CLR_BITMSK + (reg_spcr_val, + SPCR_RFIE_BIT), + (IORemapAddress + + IOH_SPI_SPCR)); + } + + /*update counts */ + pCtrlData->TxIndex = TxIndex; + + pCtrlData->RxIndex = RxIndex; + + IOH_DEBUG + ("ioh_spi_handler RxIndex=%d\n", + RxIndex); + + IOH_DEBUG + ("ioh_spi_handler TxIndex=%d\n", + TxIndex); + + IOH_DEBUG + ("ioh_spi_handler nWritable=%d\n", + (16 - + (IOH_SPI_WRITABLE + (reg_spsr_val)))); + + IOH_DEBUG + ("ioh_spi_handler nReadable=%d\n", + nReadable); + } + + /*if transfer complete interrupt */ + if (reg_spsr_val & SPSR_FI_BIT) { + IOH_DEBUG + ("ioh_spi_handler FI bit in SPSR\ + set\n"); + + /*disable FI & RFI interrupts */ + ioh_spi_disable_interrupts(pCtrlData-> + pMaster, + IOH_SPI_FI | + IOH_SPI_RFI); + + /*transfer is completed;inform + ioh_spi_process_messages */ + + if (ioh_spi_gcbptr != NULL) { + IOH_DEBUG + ("ioh_spi_handler invoking\ + callback\n"); + (*ioh_spi_gcbptr) (pCtrlData); + } + } + + tRetVal = IRQ_HANDLED; + } + } + } + + IOH_DEBUG("ioh_spi_handler EXIT return value=%d\n", tRetVal); + + return tRetVal; +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_entcb (void (*ioh_spi_cb)( struct ioh_spi_data* )) + +@remarks Registers the callback function + + The major tasks performed by this method are: + - Validate ioh_spi_cb + - Assign it to global pointer @ref ioh_spi_gcbptr + +@note This function is invoked from @ref ioh_spi_probe function + This function should always be invoked before the interrupt + handler is registered. + +@param ioh_spi_cb [@ref IN] + Contains reference to callback function pointer + +@retval None + +@see + - ioh_spi_probe + +
+ +*/ +void ioh_spi_entcb(void (*ioh_spi_cb) (struct ioh_spi_data *)) +{ + if (ioh_spi_cb != NULL) { + /*Assign the above value to a global pointer */ + ioh_spi_gcbptr = ioh_spi_cb; + IOH_DEBUG("ioh_spi_entcb ioh_spi_cb ptr not NULL\n"); + IOH_DEBUG + ("ioh_spi_entcb ioh_spi_cb ptr saved in ioh_spi_gcbptr\n"); + } else { + IOH_LOG(KERN_ERR, "ioh_spi_entcb ioh_spi_cb ptr NULL\n"); + } +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_setup_transfer(struct spi_device *spi) + +@remarks Configures the IOH SPI hardware for transfer + + The major tasks performed by this method are: + - Invoke @ref ioh_spi_set_baud_rate to set the baud rate. + - Invoke @ref ioh_spi_set_bits_per_word to set the bits per word. + - Set the bit justfication in SPCR. + - Set the Clock Polarity and Clock Phase in SPCR. + - Clear the Rx and Tx FIFO by toggling FICLR bit in SPCR. + +@note This function configures the IOH SPI hardware according to the + configurations specified by the user. + +@param spi [@ref IN] Contains reference to struct spi_device + +@retval int + @ref IOH_SPI_SUCCESS All hardware configurations have been done + +@see + - ioh_spi_select_chip + +
+ +*/ +s8 ioh_spi_setup_transfer(struct spi_device *spi) +{ + u32 reg_spcr_val; + + IOH_DEBUG("ioh_spi_setup_transfer SPBRR content =%x\n", + ioh_spi_readreg(spi->master, IOH_SPI_SPBRR)); + + /*set baud rate */ + IOH_DEBUG("ioh_spi_setup_transfer :setting baud rate=%d\n", + spi->max_speed_hz); + ioh_spi_set_baud_rate(spi->master, spi->max_speed_hz); + + /*set bits per word */ + IOH_DEBUG("ioh_spi_setup_transfer :setting bits_per_word=%d\n", + spi->bits_per_word); + ioh_spi_set_bits_per_word(spi->master, spi->bits_per_word); + + IOH_DEBUG + ("ioh_spi_setup_transfer SPBRR content after setting baud\ + rate & bits per word=%x\n", + ioh_spi_readreg(spi->master, IOH_SPI_SPBRR)); + + reg_spcr_val = ioh_spi_readreg(spi->master, IOH_SPI_SPCR); + IOH_DEBUG("ioh_spi_setup_transfer SPCR content = %x\n", reg_spcr_val); + + /*set bit justification */ + + if ((spi->mode & SPI_LSB_FIRST) != 0) { + /*LSB first */ + IOH_CLR_BITMSK(reg_spcr_val, SPCR_LSBF_BIT); + IOH_DEBUG("ioh_spi_setup_transfer :setting LSBF bit to 0\n"); + } else { + /*MSB first */ + IOH_SET_BITMSK(reg_spcr_val, SPCR_LSBF_BIT); + IOH_DEBUG("ioh_spi_setup_transfer :setting LSBF bit to 1\n"); + } + + /*set clock polarity */ + if ((spi->mode & SPI_CPOL) != 0) { + IOH_SET_BITMSK(reg_spcr_val, SPCR_CPOL_BIT); + IOH_DEBUG("ioh_spi_setup_transfer clock polarity = 1\n"); + } else { + IOH_CLR_BITMSK(reg_spcr_val, SPCR_CPOL_BIT); + IOH_DEBUG("ioh_spi_setup_transfer clock polarity = 0\n"); + } + + /*set the clock phase */ + if ((spi->mode & SPI_CPHA) != 0) { + IOH_SET_BITMSK(reg_spcr_val, SPCR_CPHA_BIT); + IOH_DEBUG("ioh_spi_setup_transfer clock phase = 1\n"); + } else { + IOH_CLR_BITMSK(reg_spcr_val, SPCR_CPHA_BIT); + IOH_DEBUG("ioh_spi_setup_transfer clock phase = 0\n"); + } + + /*write SPCR SPCR register */ + ioh_spi_writereg(spi->master, IOH_SPI_SPCR, reg_spcr_val); + + IOH_DEBUG + ("ioh_spi_setup_transfer SPCR content after setting LSB/MSB\ + and MODE= %x\n", + reg_spcr_val); + + /*Clear the FIFO by toggling FICLR to 1 and back to 0 */ + ioh_spi_clear_fifo(spi->master); + + IOH_DEBUG("ioh_spi_setup_transfer Return=%d\n", IOH_SPI_SUCCESS); + + return IOH_SPI_SUCCESS; +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_writereg(struct spi_master *master,int idx, u32 val) + +@remarks Performs register writes + + The major tasks performed by this method are: + - Obtain the SPI channel data structure from master. + - Calculate the register address as offset + base address + from SPI channel data structure. + - Write the value specified by val to register the address calculated. + +@note This function is inline. + +@param master [@ref IN] Contains reference to struct spi_master + +@param idx [@ref IN] Contains register offset + +@param val [@ref IN] Contains value to be written to register + +@retval None + +@see + - ioh_spi_setup_transfer + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts + - ioh_spi_set_enable + - ioh_spi_set_master_mode + - ioh_spi_set_baud_rate + - ioh_spi_set_bits_per_word + - ioh_spi_reset + - ioh_spi_set_threshold + - ioh_spi_clear_fifo + - ioh_spi_process_messages + +
+ +*/ +inline void ioh_spi_writereg(struct spi_master *master, int idx, u32 val) +{ + + struct ioh_spi_data *pCtrlData = spi_master_get_devdata(master); + + IOH_WRITE_LONG(val, (pCtrlData->IORemapAddress + idx)); + + IOH_DEBUG("ioh_spi_writereg Offset=%x\n", idx); + IOH_DEBUG("ioh_spi_writereg Value=%x\n", val); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_readreg(struct spi_master *master,int idx) + +@remarks Performs register reads + + The major tasks performed by this method are: + - Obtain the SPI channel data structure from master. + - Calculate the register address as offset + base address + from SPI channel data structure. + - Read the content of the register at the address calculated. + +@note This function is inline + +@param master [@ref IN] Contains reference to struct spi_master + +@param idx [@ref IN] Contains register offset + +@retval u32 + The content of the register at offset idx + +@see + - ioh_spi_setup_transfer + - ioh_spi_enable_interrupts + - ioh_spi_disable_interrupts + - ioh_spi_set_enable + - ioh_spi_set_master_mode + - ioh_spi_set_baud_rate + - ioh_spi_set_bits_per_word + - ioh_spi_set_threshold + - ioh_spi_clear_fifo + +
+*/ +inline u32 ioh_spi_readreg(struct spi_master *master, int idx) +{ + u32 reg_data; + + struct ioh_spi_data *pCtrlData = spi_master_get_devdata(master); + + IOH_DEBUG("ioh_spi_readreg Offset=%x\n", idx); + reg_data = IOH_READ_LONG((pCtrlData->IORemapAddress + idx)); + + IOH_DEBUG("ioh_spi_readreg Content=%x\n", reg_data); + return reg_data; +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_enable_interrupts (struct spi_master *master, u8 interrupt) + +@remarks Enables specified interrupts + + The major tasks performed by this method are: + - Read the content of SPCR. + - Based on interrupt ,set corresponding bits in SPCR content. + - Write the value back to SPCR. + +@note This function is invoked from @ref ioh_spi_process_messages before + starting SPI data transfer.As of now only FI and RFI interrupts are + used. + +@param master [@ref IN] Contains reference to struct spi_master + +@param interrupt [@ref IN] Interrups to be enabled.This parameter + is a u8 value with five least significant bits representing + each of the interrupts FI,RFI,TFI,ORI and MDFI. + +@retval None + +@see + - ioh_spi_process_messages + +
+ +*/ +void ioh_spi_enable_interrupts(struct spi_master *master, u8 interrupt) +{ + u32 reg_val_spcr; + + reg_val_spcr = ioh_spi_readreg(master, IOH_SPI_SPCR); + + IOH_DEBUG("ioh_spi_enable_interrupts SPCR content=%x\n", reg_val_spcr); + + if ((interrupt & IOH_SPI_RFI) != 0) { + /*set RFIE bit in SPCR */ + IOH_DEBUG("setting RFI in ioh_spi_enable_interrupts\n"); + IOH_SET_BITMSK(reg_val_spcr, SPCR_RFIE_BIT); + } + + if ((interrupt & IOH_SPI_TFI) != 0) { + /*set TFIE bit in SPCR */ + IOH_DEBUG("setting TFI in ioh_spi_enable_interrupts\n"); + IOH_SET_BITMSK(reg_val_spcr, SPCR_TFIE_BIT); + } + + if ((interrupt & IOH_SPI_FI) != 0) { + /*set FIE bit in SPCR */ + IOH_DEBUG("setting FI in ioh_spi_enable_interrupts\n"); + IOH_SET_BITMSK(reg_val_spcr, SPCR_FIE_BIT); + } + + if ((interrupt & IOH_SPI_ORI) != 0) { + /*set ORIE bit in SPCR */ + IOH_DEBUG("setting ORI in ioh_spi_enable_interrupts\n"); + IOH_SET_BITMSK(reg_val_spcr, SPCR_ORIE_BIT); + } + + if ((interrupt & IOH_SPI_MDFI) != 0) { + /*set MODFIE bit in SPCR */ + IOH_DEBUG("setting MDFI in ioh_spi_enable_interrupts\n"); + IOH_SET_BITMSK(reg_val_spcr, SPCR_MDFIE_BIT); + } + + ioh_spi_writereg(master, IOH_SPI_SPCR, reg_val_spcr); + + IOH_DEBUG + ("ioh_spi_enable_interrupts SPCR content after enabling interrupt\ + =%x\n", + reg_val_spcr); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_disable_interrupts (struct spi_master *master, u8 interrupt) + +@remarks Disables specified interrupts + + The major tasks performed by this method are: + - Read the content of SPCR. + - Based on interrupt ,clear corresponding bits in SPCR content. + - Write the value back to SPCR. + +@param master [@ref IN] Contains reference to struct spi_master + +@param interrupt [@ref IN] Interrups to be disabled.This parameter + is a u8 value with five least significant bits representing + each of the interrupts FI,RFI,TFI,ORI and MDFI. + +@retval None + +@see + - ioh_spi_process_messages + - ioh_spi_handler + - ioh_spi_suspend + - ioh_spi_free_resources + +
+ +*/ +void ioh_spi_disable_interrupts(struct spi_master *master, u8 interrupt) +{ + u32 reg_val_spcr; + + reg_val_spcr = ioh_spi_readreg(master, IOH_SPI_SPCR); + + IOH_DEBUG("ioh_spi_disable_interrupts SPCR content =%x\n", + reg_val_spcr); + + if ((interrupt & IOH_SPI_RFI) != 0) { + /*clear RFIE bit in SPCR */ + IOH_DEBUG("clearing RFI in ioh_spi_disable_interrupts\n"); + IOH_CLR_BITMSK(reg_val_spcr, SPCR_RFIE_BIT); + } + + if ((interrupt & IOH_SPI_TFI) != 0) { + /*clear TFIE bit in SPCR */ + IOH_DEBUG("clearing TFI in ioh_spi_disable_interrupts\n"); + IOH_CLR_BITMSK(reg_val_spcr, SPCR_TFIE_BIT); + } + + if ((interrupt & IOH_SPI_FI) != 0) { + /*clear FIE bit in SPCR */ + IOH_DEBUG("clearing FI in ioh_spi_disable_interrupts\n"); + IOH_CLR_BITMSK(reg_val_spcr, SPCR_FIE_BIT); + } + + if ((interrupt & IOH_SPI_ORI) != 0) { + /*clear ORIE bit in SPCR */ + IOH_DEBUG("clearing ORI in ioh_spi_disable_interrupts\n"); + IOH_CLR_BITMSK(reg_val_spcr, SPCR_ORIE_BIT); + } + + if ((interrupt & IOH_SPI_MDFI) != 0) { + /*clear MODFIE bit in SPCR */ + IOH_DEBUG("clearing MDFI in ioh_spi_disable_interrupts\n"); + IOH_CLR_BITMSK(reg_val_spcr, SPCR_MDFIE_BIT); + } + + ioh_spi_writereg(master, IOH_SPI_SPCR, reg_val_spcr); + + IOH_DEBUG + ("ioh_spi_disable_interrupts SPCR after disabling interrupts =%x\n", + reg_val_spcr); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_set_threshold(struct spi_device *spi, u32 threshold, u8 dir) + +@remarks Sets Tx/Rx FIFO thresholds + +The major tasks performed by this function are: +- Read the content of SPCR. +- If the dir is @ref IOH_SPI_RX ,set the Rx threshold bits in SPCR content. +- If the dir is @ref IOH_SPI_TX ,set the Tx threshold bits in SPCR content. +- Write back the value to SPCR. + +@note This function is invoked from ioh_spi_process_messages to set the Receive +threshold level.As of now, when the length of data to be transferred is greater +than FIFO depth of 16 bytes/words ,the Receive FIFO threshold is set at + 8 bytes/words. +If the length of data to be transferred is less than FIFO depth,the Receive FIFO +threshold is set at 16 bytes/words. + +@param spi [@ref IN] Contains reference to struct spi_device + +@param threshold [@ref IN] Threshold value to be set + +@param dir [@ref IN] Rx or Tx threshold to be set + - dir = @ref IOH_SPI_RX implies Receive FIFO threshold needs to be set. + - dir = @ref IOH_SPI_TX implies Transmit FIFO threshold needs to be set. + +@retval None + +@see + - ioh_spi_process_messages + +
+*/ +void ioh_spi_set_threshold(struct spi_device *spi, u32 threshold, u8 dir) +{ + u32 reg_val_spcr; + + reg_val_spcr = ioh_spi_readreg(spi->master, IOH_SPI_SPCR); + IOH_DEBUG("ioh_spi_set_threshold SPCR before modifying =%x\n", + reg_val_spcr); + IOH_DEBUG("ioh_spi_set_threshold threshold=%d\n", (threshold + 1)); + + if (dir == IOH_SPI_RX) { + IOH_DEBUG("ioh_spi_set_threshold setting Rx threshold\n"); + reg_val_spcr &= MASK_RFIC_SPCR_BITS; + reg_val_spcr |= (threshold << SPCR_RFIC_FIELD); + } else if (dir == IOH_SPI_TX) { + IOH_DEBUG("ioh_spi_set_threshold setting Tx threshold\n"); + reg_val_spcr &= MASK_TFIC_SPCR_BITS; + reg_val_spcr |= (threshold << SPCR_TFIC_FIELD); + } + + ioh_spi_writereg(spi->master, IOH_SPI_SPCR, reg_val_spcr); + + IOH_DEBUG("ioh_spi_set_threshold SPCR after modifying =%x\n", + reg_val_spcr); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_reset(struct spi_master* master) + +@remarks Clears SPI registers + + The major tasks performed by this method are: + - Clear all R/W bits of SPCR. + - Clear Receive and Transmit FIFOs by invoking @ref ioh_spi_clear_fifo + - Clear all R/W bits of SPBRR. + - Clear all interrupts in SPSR. + - If the device has SRST [reset register],then instead of the + above steps,first 1 is written to SRST to reset SPI and then + 0 is written to SRST to clear reset. + +@note This function is invoked to bring the IOH SPI device to an + initialized state.After this function is invoked all the SPI + registers need to be configured again. + +@param master [@ref IN] Contains reference to struct spi_master + +@retval None + +@see + - ioh_spi_get_resources + - ioh_spi_suspend + - ioh_spi_resume + +
+ +*/ +void ioh_spi_reset(struct spi_master *master) +{ +#ifndef FPGA + /*LSI*/ + /*write 1 to reset SPI */ + ioh_spi_writereg(master, IOH_SPI_SRST, 0x1); + /*clear reset */ + ioh_spi_writereg(master, IOH_SPI_SRST, 0x0); +#else + /*FPGA*/ + /*write 0 to SPCR */ + ioh_spi_writereg(master, IOH_SPI_SPCR, 0x0); + IOH_DEBUG("ioh_spi_reset SPCR content after reset=%x\n", + ioh_spi_readreg(master, IOH_SPI_SPCR)); + /*Clear the FIFO */ + ioh_spi_clear_fifo(master); + + /*write 0 to SPBRR */ + ioh_spi_writereg(master, IOH_SPI_SPBRR, 0x0); + IOH_DEBUG("ioh_spi_reset SPBRR content after reset=%x\n", + ioh_spi_readreg(master, IOH_SPI_SPBRR)); + + /*clear interrupts in SPSR */ + ioh_spi_writereg(master, IOH_SPI_SPSR, SPSR_INT_BITS); + IOH_DEBUG("ioh_spi_reset SPSR content after reset=%x\n", + ioh_spi_readreg(master, IOH_SPI_SPSR)); +#endif +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_set_baud_rate(struct spi_master* master,u32 speed_hz) + +@remarks Sets SPBR field in SPBRR + + The major tasks performed by this method are: + - Read the content of SPBRR register. + - Calculate the value for SPBR field according to the baud rate. + - Set the SPBR field using the calculated value. + - Write the conetnt back to SPBRR. + +@note The SPBR value is calculated from the baud rate using the formula + SPBR = clock frequency / baud rate. + +@param master [@ref IN] Contains reference to struct spi_master + +@param speed_hz [@ref IN] Baud rate to be set + +@retval None + +@see + - ioh_spi_setup_transfer + - ioh_spi_process_messages + +
+ +*/ +void ioh_spi_set_baud_rate(struct spi_master *master, u32 speed_hz) +{ + u32 nSpbr, reg_spbrr_val; + + nSpbr = IOH_CLOCK_HZ / (speed_hz * 2); + + /*if baud rate is less than we can support + limit it */ + + if (nSpbr > IOH_SPI_MAX_SPBR) + nSpbr = IOH_SPI_MAX_SPBR; + + + reg_spbrr_val = ioh_spi_readreg(master, IOH_SPI_SPBRR); + + IOH_DEBUG("ioh_spi_set_baud_rate SPBRR content=%x\n", reg_spbrr_val); + + IOH_DEBUG("ioh_spi_set_baud_rate SPBR in SPBRR=%d\n", nSpbr); + + /*clear SPBRR */ + reg_spbrr_val &= MASK_SPBRR_SPBR_BITS; + + /*set the new value */ + reg_spbrr_val |= nSpbr; + + /*write the new value */ + ioh_spi_writereg(master, IOH_SPI_SPBRR, reg_spbrr_val); + IOH_DEBUG("ioh_spi_set_baud_rate SPBRR content after setting SPBR=%x\n", + reg_spbrr_val); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_set_bits_per_word(struct spi_master* master,u8 bits_per_word) + +@remarks Sets SIZE field in SPBRR + + The major tasks performed by this method are: + - Read the content of SPBRR register. + - Set the SIZE field in SPBRR according to bits per word. + - Write back the value to SPBRR. + +@note The allowed bits per word settings are 8 and 16.The SIZE bit in SPBRR is + 0 denotes bits per word of 8 and SIZE bit 1 denotes bits per word of 16. + +@param master [@ref IN] Contains reference to struct spi_master + +@param bits_per_word [@ref IN] Bits per word for SPI transfer + +@retval None + +@see + - ioh_spi_setup_transfer + - ioh_spi_process_messages + +
+ +*/ +void ioh_spi_set_bits_per_word(struct spi_master *master, u8 bits_per_word) +{ + u32 reg_spbrr_val = ioh_spi_readreg(master, IOH_SPI_SPBRR); + IOH_DEBUG("ioh_spi_set_bits_per_word SPBRR content=%x\n", + reg_spbrr_val); + + if (bits_per_word == IOH_SPI_8_BPW) { + IOH_CLR_BITMSK(reg_spbrr_val, SPBRR_SIZE_BIT); + IOH_DEBUG("ioh_spi_set_bits_per_word 8\n"); + } else { + IOH_SET_BITMSK(reg_spbrr_val, SPBRR_SIZE_BIT); + IOH_DEBUG("ioh_spi_set_bits_per_word 16\n"); + } + + ioh_spi_writereg(master, IOH_SPI_SPBRR, reg_spbrr_val); + + IOH_DEBUG + ("ioh_spi_set_bits_per_word SPBRR after setting bits per word=%x\n", + reg_spbrr_val); +} + +/*! @ingroup SPI_HALLayerAPI + +@fn ioh_spi_clear_fifo(struct spi_master *master) + +@remarks Clears the Transmit and Receive FIFOs + + The major tasks performed by this method are: + - Read the content of SPCR. + - Set FICLR bit to 1. + - Write back the content to SPCR. + - Set the FICLR bit to 0. + - Write back the content to SPCR. + +@param master [@ref IN] Contains reference to struct spi_master + +@retval None + +@see + - ioh_spi_setup_transfer + - ioh_spi_process_messages + +
+ +*/ +void ioh_spi_clear_fifo(struct spi_master *master) +{ + u32 reg_spcr_val = ioh_spi_readreg(master, IOH_SPI_SPCR); + + IOH_SET_BITMSK(reg_spcr_val, SPCR_FICLR_BIT); + ioh_spi_writereg(master, IOH_SPI_SPCR, reg_spcr_val); + IOH_DEBUG("ioh_spi_clear_fifo SPCR content after setting FICLR = %x\n", + reg_spcr_val); + + IOH_CLR_BITMSK(reg_spcr_val, SPCR_FICLR_BIT); + ioh_spi_writereg(master, IOH_SPI_SPCR, reg_spcr_val); + + IOH_DEBUG + ("ioh_spi_clear_fifo SPCR content after resetting FICLR = %x\n", + reg_spcr_val); +} diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi_hal.h topcliff-2.6.33-rc3/drivers/spi/pch_spi_hal.h --- linux-2.6.33-rc3/drivers/spi/pch_spi_hal.h 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi_hal.h 2010-03-06 09:02:20.000000000 +0900 @@ -0,0 +1,298 @@ +/** + * @file ioh_spi_hal.h + * + * @brief This header file contains macro definitions and function declarations + * for HAL layer APIs. + * @version 0.94 + * + * @par + * -- Copyright Notice -- + * + * @par + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * @par + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @par + * -- End of Copyright Notice -- + */ +#ifndef __IOH_SPI_HAL__ +#define __IOH_SPI_HAL__ + +/*Register offsets*/ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPCR +@brief SPCR register offset +*/ +#define IOH_SPI_SPCR (0x00) /*SPI control register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPBRR +@brief SPBRR register offset +*/ +#define IOH_SPI_SPBRR (0x04) /*SPI baud rate register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPSR +@brief SPSR register offset +*/ +#define IOH_SPI_SPSR (0x08) /*SPI status register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPDWR +@brief SPDWR register offset +*/ +#define IOH_SPI_SPDWR (0x0C) /*SPI write data register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPDRR +@brief SPDRR register offset +*/ +#define IOH_SPI_SPDRR (0x10) /*SPI read data register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SSNXCR +@brief SSNXCR register offset +*/ +#define IOH_SPI_SSNXCR (0x18)/* SSN Expand Control Register */ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SRST +@brief SRST register offset +*/ +#define IOH_SPI_SRST (0x1C) /*SPI reset register */ + +/* valid bits per word settings*/ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_8_BPW +@brief Macro to denote 8 Bits per word transfer +*/ +#define IOH_SPI_8_BPW (8) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_16_BPW +@brief Macro to denote 16 Bits per word transfer +*/ +#define IOH_SPI_16_BPW (16) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPSR_TFD +@brief Mask to obtaining TFD bits from SPSR +*/ +#define IOH_SPI_SPSR_TFD (0x000007C0) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_SPSR_RFD +@brief Mask to obtaining RFD bits from SPSR +*/ +#define IOH_SPI_SPSR_RFD (0x0000F800) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_READABLE(x) +@brief Macro to obtain number of bytes received in Rx FIFO +@note x is the content of SPSR register +*/ +#define IOH_SPI_READABLE(x) (((x) & IOH_SPI_SPSR_RFD)>>11) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_WRITABLE(x) +@brief Macro to obtain number of bytes te be transmitted in Tx FIFO +@note x is the content of SPSR register +*/ +#define IOH_SPI_WRITABLE(x) (((x) & IOH_SPI_SPSR_TFD)>>6) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_RX_THOLD +@brief Macro to denote Rx interrupt threshold +@note Currently set to interrupt when 8 bytes are received +*/ +/*set to interrupt when 8 bytes have been received */ +#define IOH_SPI_RX_THOLD (7) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_RX_THOLD_MAX +@brief Macro to denote Rx interrupt threshold when Rx FIFO is full +*/ +/*set to interrupt when 16 bytes have been received */ +#define IOH_SPI_RX_THOLD_MAX (15) + +/*direction for interrupts*/ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_RX +@brief Macro to indicate Receive +*/ +#define IOH_SPI_RX (1) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_TX +@brief Macro to indicate Transmit +*/ +#define IOH_SPI_TX (2) + +/*various interrupts*/ + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_TFI +@brief Transmit interrupt +*/ +#define IOH_SPI_TFI (0x1) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_RFI +@brief Receive interrupt +*/ +#define IOH_SPI_RFI (0x2) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_FI +@brief Transfer complete interrupt +*/ +#define IOH_SPI_FI (0x4) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_ORI +@brief Overflow interrupt +*/ +#define IOH_SPI_ORI (0x8) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_MDFI +@brief Modefault interrupt +*/ +#define IOH_SPI_MDFI (0x10) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_ALL +@brief Macro to denote all interrupts +*/ +#define IOH_SPI_ALL \ + (IOH_SPI_TFI|IOH_SPI_RFI|IOH_SPI_FI|IOH_SPI_ORI|IOH_SPI_MDFI) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_MAX_BAUDRATE +@brief Macro to denote maximum possible baud rate in bits per second +*/ +#define IOH_SPI_MAX_BAUDRATE (5000000) + +/*! @ingroup SPI_HALLayer +@def IOH_SPI_MAX_FIFO_DEPTH +@brief Macro to denote maximum FIFO depth(16) +*/ +#define IOH_SPI_MAX_FIFO_DEPTH (16) + +/*status codes*/ + +/*! @ingroup SPI_Global +@def IOH_SPI_SUCCESS +@brief Success status code +*/ +#define IOH_SPI_SUCCESS (0) + +/*! @ingroup SPI_Global +@def IOH_SPI_FAIL +@brief Failure status code +*/ +#define IOH_SPI_FAIL (-1) + +/* hal function prototypes */ + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_setup_transfer(struct spi_device *spi) +@brief Configures the IOH SPI hardware for SPI transfer +*/ +s8 ioh_spi_setup_transfer(struct spi_device *spi); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_set_enable(const struct spi_device *spi, u8 enable) +@brief Sets/Resets SPE bit in SPCR based on enable parameter +*/ +void ioh_spi_set_enable(const struct spi_device *spi, u8 enable); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_set_master_mode( struct spi_master *master) +@brief Sets MSTR bit in SPCR +*/ +void ioh_spi_set_master_mode(struct spi_master *master); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_writereg(struct spi_master *master,int idx, u32 val) +@brief Performs register writes +*/ +inline void ioh_spi_writereg(struct spi_master *master, int idx, u32 val); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_readreg(struct spi_master *master,int idx) +@brief Performs register reads +*/ +inline u32 ioh_spi_readreg(struct spi_master *master, int idx); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_handler (int irq, void* dev_id) +@brief The interrupt handler +*/ +irqreturn_t ioh_spi_handler(int irq, void *dev_id); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_entcb (void (*ioh_spi_cb)( struct ioh_spi_data* )) +@brief Registers the Callback function +*/ +void ioh_spi_entcb(void (*ioh_spi_cb) (struct ioh_spi_data *)); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_enable_interrupts (struct spi_master *master ,u8 interrupt) +@brief Enables specified interrupts in SPCR +*/ +void ioh_spi_enable_interrupts(struct spi_master *master, u8 interrupt); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_disable_interrupts (struct spi_master *master ,u8 interrupt) +@brief Disables specified interrupts in SPCR +*/ +void ioh_spi_disable_interrupts(struct spi_master *master, u8 interrupt); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_set_threshold(struct spi_device *spi,u32 threshold, u8 dir) +@brief Sets RFIC/TFIC fields in SPCR based on threshold and dir +*/ +void ioh_spi_set_threshold(struct spi_device *spi, u32 threshold, u8 dir); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_reset(struct spi_master *master) +@brief Resets IOH SPI register settings +*/ +void ioh_spi_reset(struct spi_master *master); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_set_baud_rate(struct spi_master *master,u32 speed_hz) +@brief Sets SPBR field in SPBRR +*/ +void ioh_spi_set_baud_rate(struct spi_master *master, u32 speed_hz); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_set_bits_per_word(struct spi_master *master,u8 bits_per_word) +@brief Sets SIZE field in SPBRR +*/ +void ioh_spi_set_bits_per_word(struct spi_master *master, u8 bits_per_word); + +/*! @ingroup SPI_HALLayerAPI +@fn ioh_spi_clear_fifo(struct spi_master *master) +@brief Clears Tx/Rx FIFOs by toggling FICLR bit in SPCR +*/ +void ioh_spi_clear_fifo(struct spi_master *master); +#endif diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi_main.c topcliff-2.6.33-rc3/drivers/spi/pch_spi_main.c --- linux-2.6.33-rc3/drivers/spi/pch_spi_main.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi_main.c 2010-03-09 00:40:52.000000000 +0900 @@ -0,0 +1,1323 @@ +/** + * @file ioh_spi_main.c + * + * @brief This file defines the SPI_InterfaceLayer APIs of the IOH SPI + * controller + * driver. + * + * @version 0.94 + * + * @par + * -- Copyright Notice -- + * + * @par + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * @par + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @par + * -- End of Copyright Notice -- + */ + +#include +#include +#include +#include +#include +#include "pch_debug.h" +#include "pch_spi.h" +#include "pch_spi_hal.h" + +/*! @ingroup SPI_HALLayer +@def SSN_LOW +@brief SSNXCR register value to pull down SSN +*/ +#define SSN_LOW (0x02U) + +/*! @ingroup SPI_HALLayer +@def SSN_NO_CONTROL +@brief SSNXCR register value to relinquish control over SSN +*/ +#define SSN_NO_CONTROL (0x00U) + +/*function prototypes*/ + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_deselect_chip(struct ioh_spi_data* pCtrlData) +@brief Clears the details of the current slave from the SPI channel + data structure +*/ +static inline void ioh_spi_deselect_chip(struct ioh_spi_data *pCtrlData); + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_select_chip(struct ioh_spi_data* pCtrlData,struct spi_device* pSpi) +@brief Update the slave device details in the SPI channel data structure +*/ +static inline void ioh_spi_select_chip(struct ioh_spi_data *pCtrlData, + struct spi_device *pSpi); + +/*! @ingroup SPI_UtilitiesAPI +@fn ioh_spi_process_messages(struct work_struct* pWork) +@brief Work Queue handler to handle SPI data transfers +*/ +static void ioh_spi_process_messages(struct work_struct *pWork); + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_get_resources(struct ioh_spi_board_data* pBoardData) + +@remarks Acquires the resources needed by IOH SPI driver + + The major tasks performed by this method are: + - Initialize the spin lock of all SPI channels. + - Initialize queue to hold pending SPI messages of all SPI channels. + - Initialize wait queue head of all SPI channels. + - Create the work structure for all SPI channels. + - Create the work queues for all SPI channels. + - Allocate PCI regions. + - Get PCI memory mapped address and base addresses for all SPI channels. + - Reset the IOH SPI hardware for all SPI channels. + - Register the interrupt handler. + +@note This function is invoked by ioh_spi_probe to acquire + the various resources needed by IOH SPI driver.If any of the actions + performed by ioh_spi_get_resources fails,@ref ioh_spi_free_resources + is invoked to perform the necessary cleanups. + +@param pBoardData [@ref INOUT] + Contains the reference to struct ioh_spi_board_data + +@retval int +- @ref IOH_SPI_SUCCESS The function terminates normally after all + required resources are acquired. +- -EBUSY create_singlethread_workqueue fails. + pci_request_regions fails. + request_irq fails. +- -EINVAL request_irq fails. +- -ENOSYS request_irq_fails. +- -ENOMEM pci_iomap_fails. + request_irq fails. + +@see + - ioh_spi_probe + +
+*/ +int ioh_spi_get_resources(struct ioh_spi_board_data *pBoardData) +{ + int i; + long IORemapAddress; + s32 iRetVal = IOH_SPI_SUCCESS; + IOH_DEBUG("ioh_spi_get_resources ENTRY\n"); + + /*initialize resources */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + /*iniatize queue of pending messages */ + INIT_LIST_HEAD(&(pBoardData->pCtrlData[i]->Queue)); + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData[i]->Queue initialized using" + "INIT_LIST_HEAD\n"); + + /*initialize spin locks */ + spin_lock_init(&(pBoardData->pCtrlData[i]->Lock)); + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData[i]->Lock initialized using" + "spin_lock_init\n"); + + /*set channel status */ + pBoardData->pCtrlData[i]->Status = STATUS_RUNNING; + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData[i]->Status\ + = STATUS_RUNNING\n"); + + /*initialize work structure */ + INIT_WORK(&(pBoardData->pCtrlData[i]->Work), + ioh_spi_process_messages); + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData[i]->Work initialized\ + using INIT_WORK\n"); + + /*initialize wait queues */ + init_waitqueue_head(&(pBoardData->pCtrlData[i]->Wait)); + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData[i]->Wait initialized\ + using init_waitqueue_head\n"); + } + + do { + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + /*create workqueue */ + pBoardData->pCtrlData[i]->pWorkQueue = + create_singlethread_workqueue(DRIVER_NAME); + + if ((pBoardData->pCtrlData[i]->pWorkQueue) == NULL) { + IOH_LOG(KERN_ERR, + "ioh_spi_get_resources create_singlet\ + hread_workqueue failed\n"); + iRetVal = -EBUSY; + break; + } + } + + if (iRetVal != 0) + break; + + + IOH_DEBUG + ("ioh_spi_get_resources create_singlethread_workqueue\ + success\n"); + iRetVal = pci_request_regions(pBoardData->pDev, DRIVER_NAME); + if (iRetVal != 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_get_resources request_region failed\n"); + break; + } + + IOH_DEBUG("ioh_spi_get_resources request_region returned=%d\n", + iRetVal); + + pBoardData->bRegionRequested = true; + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData->bRegionRequested = true\n"); + + /* Wipro 1/13/2010 Use Mem BAR */ + IORemapAddress = + (unsigned long)pci_iomap(pBoardData->pDev, 1, 0); + + if (IORemapAddress == 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_get_resources pci_iomap failed\n"); + iRetVal = -ENOMEM; + break; + } + + IOH_DEBUG + ("ioh_spi_get_resources pci_iomap success PCI Base\ + address=%x\n", + (IORemapAddress)); + + /*calculate base address for all channels */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + pBoardData->pCtrlData[i]->IORemapAddress = + IORemapAddress + (IOH_SPI_ADDRESS_SIZE * i); + IOH_DEBUG + ("ioh_spi_get_resources Base address for\ + channel %d= %x\n", + i, (pBoardData->pCtrlData[i]->IORemapAddress)); + } + + /*reset IOH SPI h/w */ + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + ioh_spi_reset(pBoardData->pCtrlData[i]->pMaster); + IOH_DEBUG + ("ioh_spi_get_resources ioh_spi_reset invoked\ + successfully \n"); + } + + /*register IRQ */ + iRetVal = request_irq(pBoardData->pDev->irq, ioh_spi_handler, + IRQF_SHARED, DRIVER_NAME, + (void *)pBoardData); + if (iRetVal != 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_get_resources request_irq failed\n"); + break; + } + + IOH_DEBUG("ioh_spi_get_resources request_irq returned=%d\n", + iRetVal); + + pBoardData->bIrqRegistered = true; + IOH_DEBUG + ("ioh_spi_get_resources pCtrlData->bIrqRegistered=true\n"); + } while (0); + + if (iRetVal != IOH_SPI_SUCCESS) { + IOH_LOG(KERN_ERR, + "ioh_spi_get_resources FAIL:invoking\ + ioh_spi_free_resources\n"); + ioh_spi_free_resources(pBoardData); + } + + IOH_DEBUG("ioh_spi_get_resources Return=%d\n", iRetVal); + + return iRetVal; +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_free_resources(struct ioh_spi_board_data* pBoardData) + +@remarks Frees the resources acquired by IOH SPI driver + + The main tasks performed by this method are: + - Destroy the workqueus created for all SPI channels. + - Disables interrupts and unregisters the interrupt handler. + - Unmaps the PCI base address. + - Releases PCI regions. + +@note This function is invoked from ioh_spi_remove when the SPI device is + being removed from the system or when the IOH SPI driver is being + unloaded from the system using "rmmod" command. + +@param pBoardData [@ref INOUT] Contains the reference to struct + ioh_spi_board_data + +@retval None + +@see + - ioh_spi_remove + +
+*/ +void ioh_spi_free_resources(struct ioh_spi_board_data *pBoardData) +{ + int i; + + IOH_DEBUG("ioh_spi_free_resources ENTRY\n"); + + /*free workqueue */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + if (pBoardData->pCtrlData[i]->pWorkQueue != NULL) { + destroy_workqueue(pBoardData->pCtrlData[i]->pWorkQueue); + pBoardData->pCtrlData[i]->pWorkQueue = NULL; + IOH_DEBUG + ("ioh_spi_free_resources destroy_workqueue invoked\ + successfully\n"); + } + } + + /*disable interrupts & free IRQ */ + if (pBoardData->bIrqRegistered == true) { + /* disable interrupts */ + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + ioh_spi_disable_interrupts(pBoardData->pCtrlData[i]-> + pMaster, IOH_SPI_ALL); + IOH_DEBUG + ("ioh_spi_free_resources ioh_spi_disable_interrupts\ + invoked successfully\n"); + } + + /*free IRQ */ + free_irq(pBoardData->pDev->irq, (void *)pBoardData); + + IOH_DEBUG + ("ioh_spi_free_resources free_irq invoked successfully\n"); + + pBoardData->bIrqRegistered = false; + } + + /*unmap PCI base address */ + if ((pBoardData->pCtrlData[0]->IORemapAddress) != 0) { + pci_iounmap(pBoardData->pDev, + (void *)(pBoardData->pCtrlData[0]->IORemapAddress)); + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) + pBoardData->pCtrlData[i]->IORemapAddress = 0; + + + IOH_DEBUG + ("ioh_spi_free_resources pci_iounmap invoked\ + successfully\n"); + } + + /*release PCI region */ + if (pBoardData->bRegionRequested == true) { + pci_release_regions(pBoardData->pDev); + IOH_DEBUG + ("ioh_spi_free_resources pci_release_regions invoked\ + successfully\n"); + pBoardData->bRegionRequested = false; + } +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_process_messages(struct work_struct* pWork) + +@remarks Work Queue handler to handle SPI data transfers + +The main tasks performed by this method are: +- If system is suspended,then flush the queue of pending transfers and return. +- Retrieve the SPI message to be processed from the queue of pending messages. +- Invoke @ref ioh_spi_select_chip to configure the SPI channel. +- Retrieve the 1st or the subsequent transfer structure from SPI message + structure. +- Update baud rate and bits per word,if user has specified new values. +- Allocate memory for Transmit and Receive buffers. +- Copy transmit data from transfer structure to Transmit buffer. +- Pull down SSN by writing 0x2 to SSNXCR register. +- Write transmit data to Transmit FIFO. +- Enable required interrupts. +- Enable SPI transfer by invoking @ref ioh_spi_set_enable. +- Wait till SPI data transfer is completed. +- Relinquish control over SSN by writing 0x0 to SSNXCR register. +- Disable SPI transfer by invoking @ref ioh_spi_set_enable. +- Clear Transmit & Receive FIFOs by invoking @ref ioh_spi_clear_fifo. +- Copy received data from Receive buffer to transfer structure. +- Free memory allocated for Transmit and Receive buffers. +- Update data count in transfer structure. +- If the SPI message has any more transfers , process them same as above. +- If system is suspended,then flush the queue of pending transfers and return. +- Again schedule the work queue haandler to run if there are pending messages in +queue of pending messages. + +@note Work Queue handler is scheduled by @ref ioh_spi_transfer after +the SPI message to be processed is pushed into the queue of pending +transfers.This function will write the first set of data to Tx FIFO and sleeps +till all SPI data transfer is over.The data transfer is handled by +the interrupt handler ioh_spi_handler function. + +@param pWork [@ref IN] contains reference to struct work_struct + +@retval None + +@see + - ioh_spi_transfer + +
+*/ +static void ioh_spi_process_messages(struct work_struct *pWork) +{ + int j; + u32 nWrites; + + struct spi_message *pMsg; + int bMemFail, size; + int bpw; + + struct ioh_spi_data *pCtrlData = + container_of(pWork, struct ioh_spi_data, Work); + IOH_DEBUG("ioh_spi_process_messages pCtrlData initialized\n"); + + spin_lock(&pCtrlData->Lock); + + /*check if suspend has been initiated;if yes flush queue */ + + if ((pCtrlData->pBoardData->bSuspended == true) + || (pCtrlData->Status == STATUS_EXITING)) { + IOH_DEBUG + ("ioh_spi_process_messages suspend/remove initiated,\ + flushing queue\n"); + list_for_each_entry(pMsg, pCtrlData->Queue.next, queue) { + pMsg->status = -EIO; + + if (pMsg->complete != 0) + pMsg->complete(pMsg->context); + + + /*delete from queue */ + list_del_init(&pMsg->queue); + } + + spin_unlock(&pCtrlData->Lock); + } else { + pCtrlData->bCurrent_msg_processing = true; + IOH_DEBUG + ("ioh_spi_process_messages set pCtrlData->\ + bCurrent_msg_processing" + "= true\n"); + + /*Get the message from the queue and delete it from there. */ + pCtrlData->pCurMsg = + list_entry(pCtrlData->Queue.next, struct spi_message, + queue); + IOH_DEBUG + ("ioh_spi_process_messages :Got new message from queue \n"); + list_del_init(&pCtrlData->pCurMsg->queue); + + pCtrlData->pCurMsg->status = 0; + + IOH_DEBUG + ("ioh_spi_process_messages :Invoking ioh_spi_select_chip\n"); + ioh_spi_select_chip(pCtrlData, pCtrlData->pCurMsg->spi); + + spin_unlock(&pCtrlData->Lock); + + do { + /*If we are already processing a message get the next + transfer + structure from the message otherwise retrieve the + 1st transfer + request from the message. */ + spin_lock(&pCtrlData->Lock); + + if (pCtrlData->pCurTransfer == NULL) { + pCtrlData->pCurTransfer = + list_entry(pCtrlData->pCurMsg->transfers. + next, struct spi_transfer, + transfer_list); + IOH_DEBUG + ("ioh_spi_process_messages :Getting 1st\ + transfer structure" + "for this message\n"); + } else { + pCtrlData->pCurTransfer = + list_entry(pCtrlData->pCurTransfer-> + transfer_list.next, + struct spi_transfer, + transfer_list); + IOH_DEBUG + ("ioh_spi_process_messages :Getting next\ + transfer structure" + "for this message\n"); + } + + spin_unlock(&pCtrlData->Lock); + + /*set baud rate if needed */ + + if (pCtrlData->pCurTransfer->speed_hz) { + IOH_DEBUG + ("ioh_spi_process_messages:setting\ + baud rate\n"); + ioh_spi_set_baud_rate(pCtrlData->pMaster, + (pCtrlData->pCurTransfer-> + speed_hz)); + } + + /*set bits per word if needed */ + if ((pCtrlData->pCurTransfer->bits_per_word) && + ((pCtrlData->pCurMsg->spi->bits_per_word) != + (pCtrlData->pCurTransfer->bits_per_word))) { + IOH_DEBUG + ("ioh_spi_process_messages:setting bits\ + per word\n"); + ioh_spi_set_bits_per_word(pCtrlData->pMaster, + (pCtrlData-> + pCurTransfer-> + bits_per_word)); + bpw = pCtrlData->pCurTransfer->bits_per_word; + } else { + bpw = pCtrlData->pCurMsg->spi->bits_per_word; + } + + /*reset Tx/Rx index */ + pCtrlData->TxIndex = 0; + + pCtrlData->RxIndex = 0; + + if (IOH_SPI_8_BPW == bpw) { + /*8 bits per word */ + pCtrlData->lengthInBpw = + pCtrlData->pCurTransfer->len; + } else { + /*16 bits per word */ + pCtrlData->lengthInBpw = + (pCtrlData->pCurTransfer->len) / 2; + } + + bMemFail = false; + + /*find alloc size */ + size = + (pCtrlData->pCurTransfer->len) * + (sizeof(*(pCtrlData->pU16TxBuffer))); + /*allocate memory for pU16TxBuffer & pU16RxBuffer */ + pCtrlData->pU16TxBuffer = +/* (u16 *) kzalloc(size, GFP_KERNEL);*/ + kzalloc(size, GFP_KERNEL); + if (pCtrlData->pU16TxBuffer != NULL) { + pCtrlData->pU16RxBuffer = +/* (u16 *) kzalloc(size, GFP_KERNEL);*/ + kzalloc(size, GFP_KERNEL); + if (pCtrlData->pU16RxBuffer == NULL) { + bMemFail = true; + kfree(pCtrlData->pU16TxBuffer); + } + } else { + bMemFail = true; + } + + if (bMemFail) { + /*flush queue and set status of all transfers + to -ENOMEM */ + IOH_LOG(KERN_ERR, + "Kzalloc fail in\ + ioh_spi_process_messages\n"); + list_for_each_entry(pMsg, pCtrlData->Queue.next, + queue) { + pMsg->status = -ENOMEM; + + if (pMsg->complete != 0) + pMsg->complete(pMsg->context); + + + /*delete from queue */ + list_del_init(&pMsg->queue); + } + + return; + } + + /*copy Tx Data */ + if ((pCtrlData->pCurTransfer->tx_buf) != NULL) { + for (j = 0; j < (pCtrlData->lengthInBpw); j++) { + if (IOH_SPI_8_BPW == bpw) { + pCtrlData->pU16TxBuffer[j] = + (((u8 *) (pCtrlData-> + pCurTransfer-> + tx_buf))[j]); + IOH_DEBUG + ("xmt data in\ + ioh_spi_process_messages=%x\n", + (pCtrlData-> + pU16TxBuffer[j])); + } else { + pCtrlData->pU16TxBuffer[j] = + ((u16 *) (pCtrlData-> + pCurTransfer-> + tx_buf))[j]; + IOH_DEBUG + ("xmt data ioh_spi_pro\ + cess_messages%x\n", + (pCtrlData-> + pU16TxBuffer[j])); + } + } + } + + /*if len greater than IOH_SPI_MAX_FIFO_DEPTH, + write 16,else len bytes */ + if ((pCtrlData->lengthInBpw) > IOH_SPI_MAX_FIFO_DEPTH) + nWrites = IOH_SPI_MAX_FIFO_DEPTH; + else + nWrites = (pCtrlData->lengthInBpw); + + +#ifndef FPGA + /*LSI*/ + IOH_DEBUG + ("\nioh_spi_process_messages:Pulling down SSN low\ + - writing 0x2 to SSNXCR\n"); + ioh_spi_writereg(pCtrlData->pMaster, IOH_SPI_SSNXCR, + SSN_LOW); +#endif + IOH_DEBUG + ("\nioh_spi_process_messages:Writing %u items\n", + nWrites); + + for (j = 0; j < nWrites; j++) { + ioh_spi_writereg(pCtrlData->pMaster, + IOH_SPI_SPDWR, + pCtrlData->pU16TxBuffer[j]); + } + + /*update TxIndex */ + pCtrlData->TxIndex = j; + + IOH_DEBUG + ("ioh_spi_process_messages:enabling interrupts\n"); + + /*reset transfer complete flag */ + pCtrlData->bTransferComplete = false; + + pCtrlData->bTransferActive = true; + + IOH_DEBUG + ("ioh_spi_process_messages set pCtrlData->\ + bTransferActive = true\n"); + + /*enable interrupts */ + if ((pCtrlData->lengthInBpw) > IOH_SPI_MAX_FIFO_DEPTH) { + /*set receive threhold to IOH_SPI_RX_THOLD */ + ioh_spi_set_threshold(pCtrlData->pCurrentChip, + IOH_SPI_RX_THOLD, + IOH_SPI_RX); + /*enable FI and RFI interrupts */ + ioh_spi_enable_interrupts(pCtrlData->pMaster, + IOH_SPI_RFI | + IOH_SPI_FI); + } else { + /*set receive threhold to maximum */ + ioh_spi_set_threshold(pCtrlData->pCurrentChip, + IOH_SPI_RX_THOLD_MAX, + IOH_SPI_RX); + /*enable FI interrupt */ + ioh_spi_enable_interrupts(pCtrlData->pMaster, + IOH_SPI_FI); + } + + IOH_DEBUG + ("ioh_spi_process_messages:invoking\ + ioh_spi_set_enable to enable SPI\n"); + + ioh_spi_set_enable((pCtrlData->pCurrentChip), true); + + /*Wait until the transfer completes; go to sleep + after initiating the transfer. */ + IOH_DEBUG + ("ioh_spi_process_messages:waiting for transfer\ + to get over\n"); + + wait_event_interruptible(pCtrlData->Wait, + false != + pCtrlData->bTransferComplete); +#ifndef FPGA + /*LSI*/ + ioh_spi_writereg(pCtrlData->pMaster, IOH_SPI_SSNXCR, + SSN_NO_CONTROL); + IOH_DEBUG + ("\n ioh_spi_process_messages:no more control over\ + SSN-writing 0x0 to SSNXCR"); +#endif + IOH_DEBUG("ioh_spi_process_messages:transmit over\n"); + + pCtrlData->bTransferActive = false; + IOH_DEBUG + ("ioh_spi_process_messages set pCtrlData->\ + bTransferActive = false\n"); + + /*clear all interrupts */ + ioh_spi_writereg(pCtrlData->pMaster, IOH_SPI_SPSR, + (ioh_spi_readreg + (pCtrlData->pMaster, IOH_SPI_SPSR))); + /*disable interrupts */ + ioh_spi_disable_interrupts(pCtrlData->pMaster, + IOH_SPI_ALL); + + /*Disable SPI transfer */ + IOH_DEBUG + ("ioh_spi_process_messages:invoking\ + ioh_spi_set_enable to disable" + "spi transfer\n"); + ioh_spi_set_enable((pCtrlData->pCurrentChip), false); + + /*clear FIFO */ + IOH_DEBUG + ("ioh_spi_process_messages:invoking\ + ioh_spi_clear_fifo to " + "clear fifo\n"); + ioh_spi_clear_fifo(pCtrlData->pMaster); + + /*copy Rx Data */ + + if ((pCtrlData->pCurTransfer->rx_buf) != NULL) { + for (j = 0; j < (pCtrlData->lengthInBpw); j++) { + if (IOH_SPI_8_BPW == bpw) { + ((u8 *) (pCtrlData-> + pCurTransfer-> + rx_buf))[j] = + (u8) ((pCtrlData-> + pU16RxBuffer[j]) & + 0xFF); + + IOH_DEBUG + ("rcv data in ioh_spi_proc\ + ess_messages=%x\n", + (pCtrlData-> + pU16RxBuffer[j])); + + } else { + ((u16 *) (pCtrlData-> + pCurTransfer-> + rx_buf))[j] = + (u16) (pCtrlData-> + pU16RxBuffer[j]); + IOH_DEBUG + ("rcv data in ioh_spi_proce\ + ss_messages=%x\n", + (pCtrlData-> + pU16RxBuffer[j])); + } + } + } + + /*free memory */ + kfree(pCtrlData->pU16RxBuffer); + pCtrlData->pU16RxBuffer = NULL; + + + kfree(pCtrlData->pU16TxBuffer); + pCtrlData->pU16TxBuffer = NULL; + + + /*increment message count */ + pCtrlData->pCurMsg->actual_length += + pCtrlData->pCurTransfer->len; + + IOH_DEBUG + ("ioh_spi_process_messages:pCtrlData->pCurMsg->\ + actual_length=%d\n", + pCtrlData->pCurMsg->actual_length); + + /*check for delay */ + if (pCtrlData->pCurTransfer->delay_usecs) { + IOH_DEBUG + ("ioh_spi_process_messages:delay in usec=%d\n", + pCtrlData->pCurTransfer->delay_usecs); + udelay(pCtrlData->pCurTransfer->delay_usecs); + } + + spin_lock(&pCtrlData->Lock); + + /*No more transfer in this message. */ + + if ((pCtrlData->pCurTransfer->transfer_list.next) == + &(pCtrlData->pCurMsg->transfers)) { + IOH_DEBUG + ("ioh_spi_process_messages:no more\ + transfers in this message\n"); + /*Invoke complete callback + [To the spi core..indicating + end of transfer] */ + pCtrlData->pCurMsg->status = 0; + + if ((pCtrlData->pCurMsg->complete) != 0) { + IOH_DEBUG + ("ioh_spi_process_messages:Invoking\ + callback of SPI core\n"); + pCtrlData->pCurMsg->complete(pCtrlData-> + pCurMsg-> + context); + } + + /*update status in global variable */ + pCtrlData->bCurrent_msg_processing = false; + + IOH_DEBUG + ("ioh_spi_process_messages:pCtrlData->\ + bCurrent_msg_processing" + "set to false\n"); + + pCtrlData->pCurMsg = NULL; + + pCtrlData->pCurTransfer = NULL; + + /*check if we have items in list and not + suspending */ + /*return 1 if list empty */ + if ((list_empty(&pCtrlData->Queue) == 0) && + (pCtrlData->pBoardData->bSuspended == false) + && (pCtrlData->Status != STATUS_EXITING)) { + /*We have some more work to do + (either there is more transfer + requests in the current message or + there are more messages) */ + IOH_DEBUG + ("ioh_spi_process_messages:we\ + have pending messages" + "-Invoking queue_work\n"); + queue_work(pCtrlData->pWorkQueue, + &pCtrlData->Work); + } + + /*check if suspend has been initiated;if yes + flush queue */ + else if ((pCtrlData->pBoardData->bSuspended == + true) + || (pCtrlData->Status == + STATUS_EXITING)) { + IOH_DEBUG + ("ioh_spi_process_messages\ + suspend/remove initiated," + "flushing queue\n"); + list_for_each_entry(pMsg, + pCtrlData->Queue. + next, queue) { + pMsg->status = -EIO; + + if (pMsg->complete != 0) { + pMsg->complete(pMsg-> + context); + } + + /*delete from queue */ + list_del_init(&pMsg->queue); + } + } + } + + spin_unlock(&pCtrlData->Lock); + + } while ((pCtrlData->pCurTransfer) != NULL); + } +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_select_chip(struct ioh_spi_data* pCtrlData,struct spi_device* pSpi) + +@remarks Update the SPI device details in the SPI channel data structure + +The main tasks performed by this method are: +- Check whether the active SPI device is different from the device to + which the previous data transfer occured. +- If yes invoke @ref ioh_spi_deselect_chip to clear details of old device + from pCtrlData. +- Update the details of the new device in pCtrlData +- Invoke @ref ioh_spi_setup_transfer to configure the SPI channel. + +@note This function is invoked by @ref ioh_spi_process_messages before + processing + each SPI message. + +@param pCtrlData [@ref INOUT] contains reference to struct ioh_spi_data + +@param pSpi [@ref IN] contains reference to struct spi_device + +@retval None + +@see + - ioh_spi_process_messages + +
+*/ +static inline void ioh_spi_select_chip(struct ioh_spi_data *pCtrlData, + struct spi_device *pSpi) +{ + if ((pCtrlData->pCurrentChip) != NULL) { + if ((pSpi->chip_select) != (pCtrlData->nCurrentChip)) { + IOH_DEBUG + ("ioh_spi_select_chip : different slave-Invoking" + "ioh_spi_deselect_chip\n"); + ioh_spi_deselect_chip(pCtrlData); + } + } + + pCtrlData->pCurrentChip = pSpi; + + pCtrlData->nCurrentChip = pCtrlData->pCurrentChip->chip_select; + + IOH_DEBUG("ioh_spi_select_chip :Invoking ioh_spi_setup_transfer\n"); + ioh_spi_setup_transfer(pSpi); +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_deselect_chip(struct ioh_spi_data* pCtrlData) + +@remarks Clear the SPI device details from the SPI channel data structure + + The main tasks performed by this method are: + - Clear the details of SPI device from SPI channel data structure. + +@note This function is invoked from @ref ioh_spi_select_chip + +@param pCtrlData [@ref INOUT] Contains reference to struct ioh_spi_data + +@retval None + +@see + - ioh_spi_select_chip + +
+*/ +static inline void ioh_spi_deselect_chip(struct ioh_spi_data *pCtrlData) +{ + if (pCtrlData->pCurrentChip != NULL) { + IOH_DEBUG + ("ioh_spi_deselect_chip :clearing pCurrentChip data\n"); + pCtrlData->pCurrentChip = NULL; + } +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_check_request_pending(struct ioh_spi_board_data* pBoardData) + +@remarks Checks for any pending SPI transfer request in the queue of + pending transfers + + The main tasks performed by this method are: + - If the message queue is empty return IOH_SPI_SUCCESS. + - Sleep for 100ms and again check if message queue is empty,if yes + return IOH_SPI_SUCCESS. + - Repeat 500 times. + - If queue is still not empty return -EBUSY. + +@note This function is invoked by @ref ioh_spi_remove + +@param pBoardData [@ref INOUT] Contains reference to struct + ioh_spi_board_data + +@retval int + - @ref IOH_SPI_SUCCESS Message queue is empty + - -EBUSY Queue is not empty + +@see + - ioh_spi_remove + +
+*/ +int ioh_spi_check_request_pending(struct ioh_spi_board_data *pBoardData) +{ + int i; + int iStatus = IOH_SPI_SUCCESS; + u16 count; + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + count = 500; + spin_lock(&(pBoardData->pCtrlData[i]->Lock)); + pBoardData->pCtrlData[i]->Status = STATUS_EXITING; + + while ((list_empty(&(pBoardData->pCtrlData[i]->Queue)) == 0) && + (--count)) { + IOH_DEBUG + ("ioh_spi_check_request_pending :Queue not empty\n"); + spin_unlock(&(pBoardData->pCtrlData[i]->Lock)); + msleep(IOH_SPI_SLEEP_TIME); + spin_lock(&(pBoardData->pCtrlData[i]->Lock)); + } + + spin_unlock(&(pBoardData->pCtrlData[i]->Lock)); + + if (count) { + IOH_DEBUG + ("ioh_spi_check_request_pending :Queue empty\n"); + } else { + iStatus = -EBUSY; + } + } + + IOH_DEBUG("ioh_spi_check_request_pending : EXIT=%d\n", iStatus); + + return iStatus; +} + +/*! @ingroup SPI_InterfaceLayerAPI + +@fn int ioh_spi_setup(struct spi_device* pSpi) + +@remarks Validates the SPI device configuration paramters specified by user + +The main tasks performed by this method are: +- Validate the bits per word paramter (should be either 8 or 16). +- Validate the maximum baud rate parameter (should not be greater than 5Mbps). + +@note This function is registered with the SPI core as the setup routine of +IOH SPI controller driver.This function is invoked by the kernel SPI +component when user invokes any of spidev's IOCTLs to configure the +SPI device setting.In this function no hardware settings are modified +as this can affect any ongoing SPI data transfers.So the setting passed +by the user is validated and the function returns.Hardware settings are +updated in the function @ref ioh_spi_setup_transfer, which is invoked from +@ref ioh_spi_process_messages before initiating a SPI data transfer. + +@param pSpi [@ref IN] Contains reference to structure spi_device + +@retval int + - IOH_SPI_SUCCESS All paramters are valid + - -EINVAL Any of the paramter is invalid + +@see + - ioh_spi_probe + +
+*/ +int ioh_spi_setup(struct spi_device *pSpi) +{ + int iRetVal = IOH_SPI_SUCCESS; + + /*check bits per word */ + + if ((pSpi->bits_per_word) == 0) { + pSpi->bits_per_word = IOH_SPI_8_BPW; + IOH_DEBUG("ioh_spi_setup 8 bits per word \n"); + } + + if (((pSpi->bits_per_word) != IOH_SPI_8_BPW) && + ((pSpi->bits_per_word != IOH_SPI_16_BPW))) { + IOH_LOG(KERN_ERR, "ioh_spi_setup Invalid bits per word\n"); + iRetVal = -EINVAL; + } + + /*Check baud rate setting */ + /*if baud rate of chip is greater than + max we can support,return error */ + if ((pSpi->max_speed_hz) > IOH_SPI_MAX_BAUDRATE) { + iRetVal = -EINVAL; + IOH_LOG(KERN_ERR, "ioh_spi_setup Invalid Baud rate\n"); + } + + IOH_DEBUG(KERN_ERR, "ioh_spi_setup MODE = %x\n", + ((pSpi->mode) & (SPI_CPOL | SPI_CPHA))); + + if (((pSpi->mode) & SPI_LSB_FIRST) != 0) + IOH_DEBUG("ioh_spi_setup LSB_FIRST\n"); + else + IOH_DEBUG("ioh_spi_setup MSB_FIRST\n"); + + + IOH_DEBUG("ioh_spi_setup Return=%d\n", iRetVal); + + return iRetVal; +} + +/*! @ingroup SPI_InterfaceLayerAPI + +@fn ioh_spi_transfer(struct spi_device* pSpi,struct spi_message* pMsg) + +@remarks Validates the SPI message and pushes it onto queue of pending + transfers + + The main tasks performed by this method are: + - If the list of transfers is empty return -EINVAL. + - If the maximum baud rate is zero return -EINVAL. + - If Tranmit buffer and Receive buffer both are invalid for + any transfer return -EINVAL. + - If the length of transfer is zero for any transfer return -EINVAL. + - If maximum baud rate and bits per word are invalid return -EINVAL. + - If status of SPI channel is STATUS_EXITING return -ESHUTDOWN. + - If device is suspended return -EINVAL. + - Add the SPI message to queue of pending SPI messages. + - Schedule work queue handler to run. + +@note ioh_spi_transfer is registered by IOH SPI controller driver + with SPI core as + its transfer routine from the function @ref ioh_spi_probe.It + is invoked by the kernel's SPI component when user invokes + read,write or SPI_IOC_MESSAGE ioctl. + +@param pSpi [@ref IN] Contains reference to struct spi_device + +@param pMsg [@ref IN] Contains reference to struct spi_message + +@retval int +- @ref IOH_SPI_SUCCESS The function exists normally after adding the SPI message + to queue of pending SPI messages and schedules work queue + handler to run. +- -EINVAL Any of the paramters are found to be invalid or the system is + suspended. +- -ESHUTDOWN When the status of the SPI channel is STATUS_EXITING + The status STATUS_EXITING is set when ioh_spi_remove + is invoked. + +@see + - ioh_spi_probe + +
+*/ +int ioh_spi_transfer(struct spi_device *pSpi, struct spi_message *pMsg) +{ + + struct spi_transfer *pTransfer; + + struct ioh_spi_data *pCtrlData = spi_master_get_devdata(pSpi->master); + int iRetVal = IOH_SPI_SUCCESS; + + do { + /*validate spi message and baud rate */ + if (unlikely((list_empty(&pMsg->transfers) == 1) || + ((pSpi->max_speed_hz) == 0))) { + if (list_empty(&pMsg->transfers) == 1) { + IOH_LOG(KERN_ERR, + "ioh_spi_transfer list empty\n"); + } + + if ((pSpi->max_speed_hz) == 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_tranfer maxspeed=%d\n", + (pSpi->max_speed_hz)); + } + + IOH_LOG(KERN_ERR, + "ioh_spi_transfer returning EINVAL\n"); + + iRetVal = -EINVAL; + break; + } + + IOH_DEBUG("ioh_spi_transfer Transfer List not empty\n"); + + IOH_DEBUG("ioh_spi_transfer Transfer Speed is set\n"); + + /*validate Tx/Rx buffers and Transfer length */ + list_for_each_entry(pTransfer, &pMsg->transfers, + transfer_list) { + if ((((pTransfer->tx_buf) == NULL) + && ((pTransfer->rx_buf) == NULL)) + || (pTransfer->len == 0)) { + if (((pTransfer->tx_buf) == NULL) + && ((pTransfer->rx_buf) == NULL)) { + IOH_LOG(KERN_ERR, + "ioh_spi_transfer Tx and Rx\ + buffer NULL\n"); + } + + if (pTransfer->len == 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_transfer Transfer\ + length invalid\n"); + } + + IOH_LOG(KERN_ERR, + "ioh_spi_transfer returning EINVAL\n"); + + iRetVal = -EINVAL; + break; + } + + IOH_DEBUG("ioh_spi_transfer Tx/Rx buffer valid\n"); + + IOH_DEBUG("ioh_spi_transfer Transfer length valid\n"); + + /*if baud rate hs been specified validate the same */ + + if (pTransfer->speed_hz) { + if ((pTransfer->speed_hz) > + IOH_SPI_MAX_BAUDRATE) { + iRetVal = -EINVAL; + IOH_LOG(KERN_ERR, + "ioh_spi_transfer Invalid\ + Baud rate\n"); + } + } + + /*if bits per word has been specified validate + the same */ + if (pTransfer->bits_per_word) { + if ((pTransfer->bits_per_word != IOH_SPI_8_BPW) + && (pTransfer->bits_per_word != + IOH_SPI_16_BPW)) { + iRetVal = -EINVAL; + IOH_LOG(KERN_ERR, + "ioh_spi_transfer Invalid bits\ + per word\n"); + break; + } + } + } + + if (iRetVal == -EINVAL) + break; + + + spin_lock(&pCtrlData->Lock); + + /*We won't process any messages if we have been asked + to terminate */ + + if (STATUS_EXITING == (pCtrlData->Status)) { + spin_unlock(&pCtrlData->Lock); + IOH_LOG(KERN_ERR, + "ioh_spi_transfer -pCtrlData->Status\ + = STATUS_EXITING" + "returning ESHUTDOWN\n"); + iRetVal = -ESHUTDOWN; + break; + } + + /*If suspended ,return -EINVAL */ + if (pCtrlData->pBoardData->bSuspended == true) { + IOH_LOG(KERN_ERR, + "ioh_spi_transfer pCtrlData->\ + pBoardData->bSuspending" + "= true returning EINVAL\n"); + spin_unlock(&pCtrlData->Lock); + iRetVal = -EINVAL; + break; + } + + /*set status of message */ + pMsg->actual_length = 0; + + IOH_DEBUG + ("ioh_spi_transfer - setting pMsg->status = -EINPROGRESS\n"); + + pMsg->status = -EINPROGRESS; + + /*add message to queue */ + list_add_tail(&pMsg->queue, &pCtrlData->Queue); + + IOH_DEBUG("ioh_spi_transfer - Invoked list_add_tail\n"); + + /*schedule work queue to run */ + queue_work(pCtrlData->pWorkQueue, &pCtrlData->Work); + + IOH_DEBUG("ioh_spi_transfer - Invoked Queue Work\n"); + + spin_unlock(&pCtrlData->Lock); + + } while (0); + + IOH_DEBUG("ioh_spi_transfer RETURN=%d\n", iRetVal); + + return iRetVal; +} + +/*! @ingroup SPI_InterfaceLayerAPI + +@fn ioh_spi_cleanup(struct spi_device* pSpi) + +@remarks Provides the Cleanup routine for IOH SPI driver + +@note This is a dummy function. + It is not mandatory to have a cleanup function. + If SPI master provides a cleanup function while they register + with the SPI core, then SPI core invokes the cleanup function + when SPI master calls spi_unregister_master function.This + driver invokes spi_unregister_master from ioh_spi_remove + function. Before invoking spi_unregister_master all the resources + used is freed i.e. cleanup activities are handled in the + @ref ioh_spi_remove function itself.This function is registered + as the cleanup routine for this SPI controller driver from + the @ref ioh_spi_probe function. + +@param pSpi [@ref IN] Contains reference to struct spi_device + +@retval None + +@see + - ioh_spi_probe + +
+ +*/ +void ioh_spi_cleanup(struct spi_device *pSpi) +{ + IOH_DEBUG("spi_cleanup\n"); +} + +/*! @ingroup SPI_UtilitiesAPI + +@fn ioh_spi_callback( struct ioh_spi_data* pCtrlData) + +@remarks Informs ioh_spi_process_messages that SPI data transfer is complete + + The main tasks performed by this method are: + - Set transfer status of the SPI channel to completed. + - Inform this to @ref ioh_spi_process_messages. + +@note The reference to this callback function is saved in a global pointer +by the function @ref ioh_spi_entcb invoked from @ref ioh_spi_probe function. +This function is invoked by the interrupt handler ioh_spi_handler +after transfer complete interrupt is received indicating the end of +SPI data transfer.ioh_spi_callback wakes up ioh_spi_process_messages +which blocks till SPI data transfer is completed. + +@param pCtrldata [@ref IN] Contains reference to struct ioh_spi_data + +@retval None + +@see + - ioh_spi_handler + - ioh_spi_probe + +
+*/ +void ioh_spi_callback(struct ioh_spi_data *pCtrlData) +{ + IOH_DEBUG("ioh_ spi _callback waking up process\n"); + spin_lock(&pCtrlData->Lock); + pCtrlData->bTransferComplete = true; + wake_up(&pCtrlData->Wait); + IOH_DEBUG("ioh_ spi _callback invoked wake_up\n"); + spin_unlock(&pCtrlData->Lock); +} diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi_pci.c topcliff-2.6.33-rc3/drivers/spi/pch_spi_pci.c --- linux-2.6.33-rc3/drivers/spi/pch_spi_pci.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi_pci.c 2010-03-17 20:05:19.000000000 +0900 @@ -0,0 +1,811 @@ +/** + * @file ioh_spi_pci.c + * + * @brief This file contains the function definition for the PCI Layer APIs + * + * @version 0.94 + * + * @par + * -- Copyright Notice -- + * + * @par + * Copyright (C) 2008 OKI SEMICONDUCTOR Co., LTD. + * All rights reserved. + * + * @section + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @par + * -- End of Copyright Notice -- + */ + +/*includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +/*#include modify by checkpatch.pl*/ +#include +#include "pch_spi.h" +#include "pch_spi_hal.h" +#include "pch_debug.h" + +/*! @ingroup SPI_PCILayer + +@def IOH_SPI_MAX_CS + +@brief Denotes the maximum chip select number possible. + +@note Currently this is just used to set the number of chip selects in + spi_master structure in @ref ioh_spi_probe function. + +@see ioh_spi_probe + +
+ +*/ +#define IOH_SPI_MAX_CS (0xFF) + +/*pci device ids*/ + +/*! @ingroup SPI_PCILayer + +@brief Denotes the PCI device ID of the supported device. + +@see ioh_spi_pcidev_id + +
+ +*/ +#ifndef FPGA +#define PCI_DEVICE_ID_IOH_SPI (0x8816) /*LSI*/ +#else +#define PCI_DEVICE_ID_IOH_SPI (0x8005) /*FPGA*/ +#endif +/*! @ingroup SPI_PCILayerAPI + +@fn ioh_spi_probe(struct pci_dev *pDev, const struct pci_device_id *id) + +@brief Implements the Probe functionality for IOH SPI driver + +@remarks Implements the Probe functionality for IOH SPI driver + + The major tasks performed by this method are: + - Register the callback function. + - Enable the PCI device. + - Allocate memory for SPI master. + - Initialize members of SPI master structure. + - Register the SPI master. + - Invoke @ref ioh_spi_get_resources to acquire and initialize + other resources needed by the driver. + +@note This function is invoked by the kernel when it detects + a SPI device matching the vendor ID and device ID specified + by this driver. + +@param pDev [@ref INOUT] contains reference to struct pci_dev + +@param id [@ref IN] contains reference to struct pci_device_id + +@retval int +- @ref IOH_SPI_SUCCESS The function exists successfully +- -ENOMEM spi_alloc_master API fails/kmalloc fails +- -EINVAL pci_enable_device fails/spi_register_master fails + /ioh_spi_get_resources fails +- -EIO pci_enable_device fails +- -ENODEV spi_register_master API fails +- -ENOSYS ioh_spi_get_resources fails +- -ENOMEM ioh_spi_get_resources fails + +@see ioh_spi_pcidev + +
+ +*/ +static int ioh_spi_probe(struct pci_dev *pDev, const struct pci_device_id *id) +{ + + struct spi_master *pMaster[IOH_SPI_MAX_DEV]; + + struct ioh_spi_board_data *pBoardData; + int iRetVal, i, j; + + IOH_DEBUG("ioh_spi_probe ENTRY\n"); + /*initialize the call back function */ + ioh_spi_entcb(ioh_spi_callback); + IOH_DEBUG("ioh_spi_probe invoked ioh_spi_entcb\n"); + + do { + /*allocate memory for private data */ + pBoardData = + kmalloc(sizeof(struct ioh_spi_board_data), GFP_KERNEL); + + if (pBoardData == NULL) { + IOH_LOG(KERN_ERR, + " ioh_spi_probe memory allocation for private\ + data failed\n"); + iRetVal = -ENOMEM; + break; + } + + IOH_DEBUG + (" ioh_spi_probe memory allocation for private data\ + success\n"); + + /*enable PCI device */ + iRetVal = pci_enable_device(pDev); + if (iRetVal != 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_probe pci_enable_device FAILED\n"); + + IOH_LOG(KERN_ERR, + "ioh_spi_probe invoked kfree to free memory\ + allocated for pBoardData\n"); + kfree(pBoardData); + break; + } + + IOH_DEBUG("ioh_spi_probe pci_enable_device returned=%d\n", + iRetVal); + + pBoardData->pDev = pDev; + + /*alllocate memory for SPI master */ + i = 0; + + do { + pMaster[i] = + spi_alloc_master(&pDev->dev, + sizeof(struct ioh_spi_data)); + + if (pMaster[i] == NULL) { + iRetVal = -ENOMEM; + + if (i > 0) { + j = 0; + + do { + spi_master_put(pMaster[j]); + j++; + IOH_DEBUG + ("ioh_spi_probe invoked\ + spi_master_put\n"); + } while (j < i); + } + + IOH_LOG(KERN_ERR, + "ioh_spi_probe spi_alloc_master\ + failed\n"); + + break; + } + + i++; + } while (i < IOH_SPI_MAX_DEV); + + IOH_DEBUG("ioh_spi_probe spi_alloc_master returned non NULL\n"); + + if (iRetVal != 0) { + kfree(pBoardData); + IOH_LOG(KERN_ERR, + "ioh_spi_probe invoked kfree to free memory\ + allocated for pBoardData\n"); + pci_disable_device(pDev); + IOH_LOG(KERN_ERR, + "ioh_spi_probe Invoked pci_disable_device\n"); + break; + } + + /*initialize members of SPI master */ + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + pMaster[i]->bus_num = i; + pMaster[i]->num_chipselect = IOH_SPI_MAX_CS; + pMaster[i]->setup = ioh_spi_setup; + IOH_DEBUG + ("ioh_spi_probe setup member of SPI master\ + initialized\n"); + pMaster[i]->transfer = ioh_spi_transfer; + IOH_DEBUG + ("ioh_spi_probe transfer member of SPI master\ + initialized\n"); + pMaster[i]->cleanup = ioh_spi_cleanup; + IOH_DEBUG + ("ioh_spi_probe cleanup member of SPI master\ + initialized\n"); + + pBoardData->pCtrlData[i] = + spi_master_get_devdata(pMaster[i]); + + pBoardData->pCtrlData[i]->pMaster = pMaster[i]; + pBoardData->pCtrlData[i]->nCurrentChip = 255; + pBoardData->pCtrlData[i]->pCurrentChip = NULL; + pBoardData->pCtrlData[i]->bTransferComplete = false; + pBoardData->pCtrlData[i]->pU16TxBuffer = NULL; + pBoardData->pCtrlData[i]->pU16RxBuffer = NULL; + pBoardData->pCtrlData[i]->TxIndex = 0; + pBoardData->pCtrlData[i]->RxIndex = 0; + pBoardData->pCtrlData[i]->bTransferActive = false; + pBoardData->pCtrlData[i]->pBoardData = pBoardData; + + /*Register the controller with the SPI core. */ + iRetVal = spi_register_master(pMaster[i]); + if (iRetVal != 0) { + spi_master_put(pMaster[i]); + IOH_DEBUG + ("ioh_spi_probe invoked spi_master_put\n"); + /*unregister master for any channel that has + registered master */ + + if (i > 0) { +#if 0 + for (j = 0; j < i; j++) { + spi_unregister_master(pMaster + [j]); + IOH_DEBUG + ("ioh_spi_probe invoked\ + spi_unregister_master\n"); + } +#else + spi_unregister_master(pMaster[0]); + IOH_DEBUG + ("ioh_spi_probe invoked\ + spi_unregister_master\n"); +#endif + } + + IOH_LOG(KERN_ERR, + "ioh_spi_probe spi_register_\ + master FAILED\n"); + + break; + } + + IOH_DEBUG + ("ioh_spi_probe spi_register_master\ + returned=%d\n", + iRetVal); + } + + if (iRetVal != 0) { + kfree(pBoardData); + IOH_LOG(KERN_ERR, + "ioh_spi_probe invoked kfree to free memory\ + allocated for pBoardData\n"); + pci_disable_device(pDev); + IOH_DEBUG("ioh_spi_probe invoked pci_disable\n"); + break; + } + + /*allocate resources for IOH SPI */ + iRetVal = ioh_spi_get_resources(pBoardData); + if (iRetVal != IOH_SPI_SUCCESS) { + /* + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + spi_unregister_master(pMaster[i]); + IOH_DEBUG + ("ioh_spi_probe invoked\ + spi_unregister_master\n"); + } + */ + spi_unregister_master(pMaster[0]); + IOH_DEBUG + ("ioh_spi_probe invoked spi_unregister_master\n"); + + + kfree(pBoardData); + + IOH_LOG(KERN_ERR, + "ioh_spi_probe invoked kfree to free memory\ + allocated for pBoardData\n"); + pci_disable_device(pDev); + IOH_DEBUG("ioh_spi_probe invoked pci_disable\n"); + IOH_LOG(KERN_ERR, + "ioh_spi_probe get_resources FAILED\n"); + break; + } + + IOH_DEBUG("ioh_spi_probe ioh_spi_get_resources returned=%d\n", + iRetVal); + + /*save private data in dev */ + pci_set_drvdata(pDev, (void *)pBoardData); + IOH_DEBUG("ioh_spi_probe invoked pci_set_drvdata\n"); + + /*set master mode */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + ioh_spi_set_master_mode(pMaster[i]); + IOH_DEBUG + ("ioh_spi_probe invoked ioh_spi_set_master_mode\n"); + } + + iRetVal = IOH_SPI_SUCCESS; + + } while (false); + + IOH_DEBUG("ioh_spi_probe Return=%d\n", iRetVal); + + return iRetVal; +} + +/*! @ingroup SPI_PCILayerAPI + +@fn ioh_spi_remove(struct pci_dev *pDev) + +@brief Implements the remove routine for IOH SPI driver + +@remarks Implements the remove routine for IOH SPI driver + + The major tasks performed by this method are: + - Invoke @ref ioh_spi_check_request_pending function to find + out if there are any pending requests. + - Free the allocated resources by invoking @ref ioh_spi_free_resources. + - Unregister SPI master. + - Disable PCI device. + +@note This function is invoked when the IOH SPI controller driver module + is removed from the system using "rmmod" command OR when the SPI + device is removed from the system. + +@param pDev [@ref INOUT] contains reference to struct pci_dev + +@retval None + +@see ioh_spi_pcidev + +
+ +*/ +static void ioh_spi_remove(struct pci_dev *pDev) +{ + struct ioh_spi_board_data *pBoardData = pci_get_drvdata(pDev); + + IOH_DEBUG("ioh_spi_remove ENTRY\n"); + + if (pBoardData != NULL) { + IOH_DEBUG("ioh_spi_remove invoked pci_get_drvdata\n"); + + /*check for any pending messages */ + + if ((-EBUSY) == ioh_spi_check_request_pending(pBoardData)) { + IOH_DEBUG + ("ioh_spi_remove ioh_spi_check_request_pending\ + returned EBUSY\n"); + /*no need to take any particular action;proceed with + remove even + though queue is not empty */ + } + + IOH_DEBUG + ("ioh_spi_remove ioh_spi_check_request_pending invoked\n"); + + /*Free resources allocated for IOH SPI */ + ioh_spi_free_resources(pBoardData); + IOH_DEBUG("ioh_spi_remove invoked ioh_spi_free_resources\n"); + + /*Unregister SPI master */ + +#if 0 + int i; + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + spi_unregister_master(pBoardData->pCtrlData[i]-> + pMaster); + IOH_DEBUG + ("ioh_spi_remove invoked spi_unregister_master\n"); + } +#else + spi_unregister_master(pBoardData->pCtrlData[0]->pMaster); + IOH_DEBUG("ioh_spi_remove invoked spi_unregister_master\n"); + +#endif + + /*free memory for private data */ + kfree(pBoardData); + + pci_set_drvdata(pDev, NULL); + + IOH_DEBUG("ioh_spi_remove memory for private data freed\n"); + + /*disable PCI device */ + pci_disable_device(pDev); + + IOH_DEBUG("ioh_spi_remove invoked pci_disable_device\n"); + + } else { + IOH_LOG(KERN_ERR, + "ioh_spi_remove pci_get_drvdata returned NULL\n"); + } +} + +/*! @ingroup SPI_PCILayerAPI + +@fn ioh_spi_suspend(struct pci_dev *pDev,pm_message_t state) + +@brief Implements the suspend routine for IOH SPI driver + +@remarks Implements the suspend routine for IOH SPI driver + + The major tasks performed by this method are: + - Wait till current message is processed. + - Disable interrupts by invoking @ref ioh_spi_disable_interrupts. + - Unregister the interrupt handler. + - Save current state. + - Disable PM notifications. + - Disable PCI device. + - Move the device to D3Hot power state. + +@note This function is invoked by the kernel when the system transitions + to low power state. + +@param pDev [@ref INOUT] contains reference to struct pci_dev + +@param state [@ref IN] contains new PM state to which to transition to. + +@retval int + - @ref IOH_SPI_SUCCESS The function returns successfully + - -ENOMEM pci_save_state fails + +@see ioh_spi_pcidev + +
+ +*/ +#ifdef CONFIG_PM +static int ioh_spi_suspend(struct pci_dev *pDev, pm_message_t state) +{ + int i; + u8 count; + s32 iRetVal = IOH_SPI_SUCCESS; + + struct ioh_spi_board_data *pBoardData = pci_get_drvdata(pDev); + + IOH_DEBUG("ioh_spi_suspend ENTRY\n"); + + if (pBoardData == NULL) { + IOH_LOG(KERN_ERR, + "ioh_spi_suspend pci_get_drvdata returned NULL\n"); + iRetVal = -EFAULT; + } else { + IOH_DEBUG + ("ioh_spi_suspend pci_get_drvdata invoked successfully\n"); + pBoardData->bSuspended = true; + IOH_DEBUG + ("ioh_spi_suspend pBoardData->bSuspending set to true\n"); + + /*check if the current message is processed: + Only after thats done the transfer will be suspended */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + count = 255; + + while ((--count) > 0) { + if (pBoardData->pCtrlData[i]-> + bCurrent_msg_processing == false) { + IOH_DEBUG + ("ioh_spi_suspend pBoardData\ + ->pCtrlData->" + "bCurrent_msg_processing\ + = false\n"); + break; + } else { + IOH_DEBUG + ("ioh_spi_suspend pBoardData\ + ->pCtrlData->" + "bCurrent_msg_processing = true\n"); + } + + msleep(IOH_SPI_SLEEP_TIME); + } + } + + /*Free IRQ */ + if (pBoardData->bIrqRegistered == true) { + /*disable all interrupts */ + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + ioh_spi_disable_interrupts(pBoardData-> + pCtrlData[i]-> + pMaster, + IOH_SPI_ALL); + ioh_spi_reset(pBoardData->pCtrlData[i]-> + pMaster); + IOH_DEBUG + ("ioh_spi_suspend ioh_spi_\ + disable_interrupts invoked" + "successfully\n"); + } + + free_irq(pBoardData->pDev->irq, (void *)pBoardData); + + pBoardData->bIrqRegistered = false; + IOH_DEBUG + ("ioh_spi_suspend free_irq invoked successfully\n"); + IOH_DEBUG + ("ioh_spi_suspend pCtrlData->bIrqRegistered\ + = false\n"); + } + + /*save config space */ + iRetVal = pci_save_state(pDev); + + if (iRetVal == 0) { + IOH_DEBUG + ("ioh_spi_suspend pci_save_state returned=%d\n", + iRetVal); + /*disable PM notifications */ + pci_enable_wake(pDev, PCI_D3hot, 0); + IOH_DEBUG + ("ioh_spi_suspend pci_enable_wake invoked\ + successfully\n"); + /*disable PCI device */ + pci_disable_device(pDev); + IOH_DEBUG + ("ioh_spi_suspend pci_disable_device invoked\ + successfully\n"); + /*move device to D3hot state */ + pci_set_power_state(pDev, PCI_D3hot); + IOH_DEBUG + ("ioh_spi_suspend pci_set_power_state invoked\ + successfully\n"); + } else { + IOH_LOG(KERN_ERR, + "ioh_spi_suspend pci_save_state failed\n"); + } + } + + IOH_DEBUG("ioh_spi_suspend return=%d\n", iRetVal); + + return iRetVal; +} + +#endif +/*! @ingroup SPI_PCILayerAPI + +@fn ioh_spi_resume(struct pci_dev *pDev) + +@brief Implements the resume routine for IOH SPI driver + +@remarks Implements the resume routine for IOH SPI driver + + The major tasks performed by this method are: + - Move the device to D0 power state. + - Restore the saved state. + - Enable the PCI device. + - Disable PM notifications. + - Register interrupt handler. + - Reset IOH SPI hardware. + - Set IOH SPI hardware in master mode. + +@note This function is invoked by the kernel when the system is being + resumed from suspend. + +@param pDev [@ref INOUT] contains reference to struct pci_dev + +@retval int + - @ref IOH_SPI_SUCCESS The function returns successfully + - -EINVAL request_irq fails + - -ENOMEM request_irq fails + - -ENOSYS request_irq fails + - -EBUSY request_irq fails + - -EIO pci_enable_device fails + +@see ioh_spi_pcidev + +
+ +*/ +#ifdef CONFIG_PM +static int ioh_spi_resume(struct pci_dev *pDev) +{ + int i; + s32 iRetVal = IOH_SPI_SUCCESS; + + struct ioh_spi_board_data *pBoardData = pci_get_drvdata(pDev); + IOH_DEBUG("ioh_spi_resume ENTRY\n"); + + if (pBoardData == NULL) { + IOH_LOG(KERN_ERR, + "ioh_spi_resume pci_get_drvdata returned NULL\n"); + iRetVal = -EFAULT; + } else { + /*move device to DO power state */ + pci_set_power_state(pDev, PCI_D0); + IOH_DEBUG + ("ioh_spi_resume pci_set_power_state invoked successfully\n"); + + /*restore state */ + pci_restore_state(pDev); + IOH_DEBUG + ("ioh_spi_resume pci_restore_state invoked successfully\n"); + iRetVal = pci_enable_device(pDev); + if (iRetVal < 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_resume pci_enable_device failed\n"); + } else { + IOH_DEBUG + ("ioh_spi_resume pci_enable_device returned=%d\n", + iRetVal); + + /*disable PM notifications */ + pci_enable_wake(pDev, PCI_D3hot, 0); + IOH_DEBUG + ("ioh_spi_resume pci_enable_wake invoked\ + successfully\n"); + + /*register IRQ handler */ + + if ((pBoardData->bIrqRegistered) != true) { + /*register IRQ */ + iRetVal = request_irq(pBoardData->pDev->irq, + ioh_spi_handler, IRQF_SHARED, + DRIVER_NAME, + pBoardData); + if (iRetVal < 0) { + IOH_LOG(KERN_ERR, + "ioh_spi_resume\ + request_irq failed\n"); + } else { + IOH_DEBUG + ("ioh_spi_resume request_irq\ + returned=%d\n", + iRetVal); + pBoardData->bIrqRegistered = true; + + /*reset IOH SPI h/w */ + + for (i = 0; i < IOH_SPI_MAX_DEV; i++) { + ioh_spi_reset(pBoardData-> + pCtrlData[i]-> + pMaster); + IOH_DEBUG + ("ioh_spi_resume\ + ioh_spi_reset invoked " + "successfully \n"); + ioh_spi_set_master_mode + (pBoardData->pCtrlData[i]-> + pMaster); + IOH_DEBUG + ("ioh_spi_resume\ + ioh_spi_set_master_mode invoked" + "successfully \n"); + } + + /*set suspend status to false */ + pBoardData->bSuspended = false; + + IOH_DEBUG + ("ioh_spi_resume set pBoardData->\ + bSuspending = false\n"); + } + } + } + } + + IOH_DEBUG("ioh_spi_resume returning=%d\n", iRetVal); + + return iRetVal; +} + +#endif +/*! @ingroup SPI_PCILayerFacilitators + +@struct ioh_spi_pcidev_id + +@brief Store information of supported PCI devices + +@see ioh_spi_pcidev + +
+ +*/ + +static struct pci_device_id ioh_spi_pcidev_id[] = { + /*LSI*/ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_IOH_SPI)}, + {0,} + +}; + +/*! @ingroup SPI_PCILayerFacilitators + +@struct ioh_spi_pcidev + +@brief Store the references of PCI driver interfaces to kernel + +@note This structure is registerd with the kernel via the call + pci_register_driver from @ref ioh_spi_init + +@see + - ioh_spi_init + - ioh_spi_exit + +
+ +*/ + +static struct pci_driver ioh_spi_pcidev = { + .name = "ioh_spi", + .id_table = ioh_spi_pcidev_id, + .probe = ioh_spi_probe, + .remove = ioh_spi_remove, +#ifdef CONFIG_PM + .suspend = ioh_spi_suspend, + .resume = ioh_spi_resume, +#endif + +}; + +/*! @ingroup SPI_InterfaceLayerAPI + +@fn ioh_spi_init(void) + +@brief Entry point function for this module. + +@remarks Init function for IOH SPI driver module + +@param None + +@retval int + - 0 Function exits successfully + - -EEXIST pci_register_driver fails + - -EINVAL pci_register_driver fails + - -ENOMEM pci_register_driver fails + +
+ +*/ +static int __init ioh_spi_init(void) +{ + s32 iRetVal; + + iRetVal = pci_register_driver(&ioh_spi_pcidev); + if (iRetVal == 0) { + IOH_DEBUG + ("ioh_spi_init pci_register_driver invoked successfully\n"); + } else { + IOH_LOG(KERN_ERR, "ioh_spi_init pci_register_driver failed\n"); + } + + IOH_DEBUG("ioh_spi_init returning=%d\n", iRetVal); + + return iRetVal; +} + +/*! @ingroup SPI_InterfaceLayerAPI + +@fn ioh_spi_exit(void) + +@brief Exit point function for this module. + +@remarks Function invoked when module is removed + +@param None + +@retval None + +
+ +*/ +static void __exit ioh_spi_exit(void) +{ + IOH_DEBUG("ioh_spi_exit Invoking pci_unregister_driver\n"); + pci_unregister_driver(&ioh_spi_pcidev); +} + +MODULE_DESCRIPTION("IOH SPI PCI Driver"); +MODULE_LICENSE("GPL"); +module_init(ioh_spi_init); +module_exit(ioh_spi_exit); diff -urN linux-2.6.33-rc3/drivers/spi/pch_spi_platform_devices.c topcliff-2.6.33-rc3/drivers/spi/pch_spi_platform_devices.c --- linux-2.6.33-rc3/drivers/spi/pch_spi_platform_devices.c 1970-01-01 09:00:00.000000000 +0900 +++ topcliff-2.6.33-rc3/drivers/spi/pch_spi_platform_devices.c 2010-03-06 07:44:02.000000000 +0900 @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +static struct spi_board_info ioh_spi_slaves[] = { + { + .modalias = "spidev", /* Name of spi_driver for this device*/ + .max_speed_hz = 1000000, /* max spi clock (SCK) speed in HZ*/ + .bus_num = 0, /* Framework bus number*/ + .chip_select = 0, /* Framework chip select.*/ + .platform_data = NULL, + .mode = SPI_MODE_0, + }, +#if (CONFIG_PCH_SPI_PLATFORM_DEVICE_COUNT - 1) + { + .modalias = "spidev", /* Name of spi_driver for this device*/ + .max_speed_hz = 1000000, /* max spi clock (SCK) speed in HZ*/ + .bus_num = 1, /* Framework bus number*/ + .chip_select = 0, /* Framework chip select.*/ + .platform_data = NULL, + .mode = SPI_MODE_0, + }, +#endif +}; + +static __init int Load(void) +{ + int iRetVal = -1; + + printk(KERN_INFO "Registering IOH SPI devices... \n"); + + if (!spi_register_board_info + (ioh_spi_slaves, ARRAY_SIZE(ioh_spi_slaves))) + iRetVal = 0; + else + printk(KERN_ERR "Registering IOH SPI devices failed\n"); + + return iRetVal; +} + +/* + * static __exit void Unload() + * { + * + * } + * */ + +module_init(Load); +MODULE_LICENSE("GPL");