4378 lines
120 KiB
Diff
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");
|