generic-poky/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-pch-spi.patch

4378 lines
120 KiB
Diff

From: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Subject: OKI Semiconductor PCH SPI driver
This driver implements SPI controls for PCH.
Signed-off-by: Masayuki Ohtake <masa-korg@dsn.okisemi.com>
Acked-by: Wang Qi <qi.wang@intel.com>
---
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.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @defgroup SPI_PCILayerFacilitators
+@ingroup SPI_PCILayer
+@brief This group contains the data structures used by the PCI
+ Layer APIs for their functionalities.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @defgroup SPI_InterfaceLayerFacilitators
+@ingroup SPI_InterfaceLayer
+@brief This group contains the data structures used by the Driver
+ interface APIs for their functionalities.
+<hr>
+*/
+
+/*! @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.
+<hr>
+*/
+
+/*! @defgroup SPI_UtilitiesAPI
+@ingroup SPI_Utilities
+@brief This group contains the APIs(functions) used by other functions
+ in their operations.
+<hr>
+*/
+
+#include <linux/wait.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+
+/*! @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
+
+<hr>
+*/
+#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
+
+<hr>
+*/
+#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
+
+<hr>
+*/
+#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
+
+<hr>
+*/
+#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
+
+<hr>
+*/
+#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
+
+<hr>
+*/
+#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
+<hr>
+*/
+
+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
+<hr>
+*/
+
+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 <linux/io.h>
+#include <linux/interrupt.h>
+#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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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 <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+*/
+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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+/*#include <asm/io.h> modify by checkpatch.pl*/
+#include <linux/io.h>
+#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
+
+<hr>
+
+*/
+#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
+
+<hr>
+
+*/
+#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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+#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
+
+<hr>
+
+*/
+#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
+
+<hr>
+
+*/
+
+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
+
+<hr>
+
+*/
+
+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
+
+<hr>
+
+*/
+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
+
+<hr>
+
+*/
+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 <linux/module.h>
+#include <linux/spi/spidev.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+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");