sidk_s5jt200: add to support on-board eeprom
authorIvan <ivan.galkin@samsung.com>
Mon, 3 Apr 2017 13:59:55 +0000 (22:59 +0900)
committerHeesub Shin <heesub.shin@samsung.com>
Tue, 18 Apr 2017 03:02:07 +0000 (12:02 +0900)
On SIDK board there is a MicroWire EEPROM.

In order to use it driver is implemented. Driver is based on bitbang SPI
emulation, since T200 SPI does not support non 8 bit aligned transfers,
used by EEPROM.

To enable driver, in make menuconfig enable next options:

Device Drivers  --->
[*] SPI Driver Support  --->
[*]   SPI bit-bang device
[*]     SPI bit-bang variable width transfers

Board Selection  --->
[*] Support M93C66WMN6T EEPROM

Change-Id: Ia3ecf868f207866b6f19590a0e9a2900d18c1340
Signed-off-by: Ivan <ivan.galkin@samsung.com>
os/arch/arm/src/sidk_s5jt200/Kconfig
os/arch/arm/src/sidk_s5jt200/include/board.h
os/arch/arm/src/sidk_s5jt200/src/Makefile
os/arch/arm/src/sidk_s5jt200/src/s5jt200_boot.c
os/arch/arm/src/sidk_s5jt200/src/s5jt200_eeprom.c [new file with mode: 0644]

index 74801cf..f052a07 100644 (file)
@@ -21,6 +21,15 @@ config SIDK_S5JT200_S8300
                SIDK S5JT200 evaluation board has S8300 linear LED driver IC
                which is driven by PWMs from S5J chipset.
 
+config SIDK_S5JT200_M93C66WMN6T
+       bool "Support M93C66WMN6T EEPROM"
+       default n
+       depends on SPI_BITBANG
+       depends on SPI_BITBANG_VARWIDTH
+       ---help---
+               SIDK S5JT200 evaluation board has an SPI MicroWire serial
+               access EEPROM attached to GPIO pins.
+
 config SIDK_S5JT200_PWM_CHNUM
        int "Number of PWM channel"
        default 6
index 0fb0fb8..522fc06 100644 (file)
 #define SYSCLK_FREQUENCY       32768 /* RTC clock at 32768Hz */
 
 #ifndef __ASSEMBLY__
+
 extern void s5j_boardinitialize(void);
-#endif
 
-#endif                                                 /* __ARCH_ARM_SRC_SIDK_S5JT200_INCLUDE_BOARD_H */
+#ifdef CONFIG_SIDK_S5JT200_EEPROM
+extern struct spi_dev_s *eeprom_dev;
+extern void sidk_s5jt200_eeprom_init(void);
+#endif /* CONFIG_SIDK_S5JT200_EEPROM */
+
+#endif
+#endif /* __ARCH_ARM_SRC_SIDK_S5JT200_INCLUDE_BOARD_H */
index bf950e2..8c13e16 100644 (file)
@@ -83,6 +83,10 @@ ifeq ($(CONFIG_MTD_PROGMEM),y)
 CSRCS += s5jt200_progmem.c
 endif
 
+ifeq ($(CONFIG_SIDK_S5JT200_EEPROM), y)
+CSRCS += s5jt200_eeprom.c
+endif
+
 COBJS = $(CSRCS:.c=$(OBJEXT))
 
 SRCS = $(ASRCS) $(CSRCS)
index 20d76c5..4fce499 100644 (file)
@@ -218,5 +218,9 @@ void board_initialize(void)
 #ifdef CONFIG_SIDK_S5JT200_S8300
        s5j_ledinitialize();
 #endif
+
+#ifdef CONFIG_SIDK_S5JT200_EEPROM
+       sidk_s5jt200_eeprom_init();
+#endif
 }
 #endif /* CONFIG_BOARD_INITIALIZE */
diff --git a/os/arch/arm/src/sidk_s5jt200/src/s5jt200_eeprom.c b/os/arch/arm/src/sidk_s5jt200/src/s5jt200_eeprom.c
new file mode 100644 (file)
index 0000000..f72b7c2
--- /dev/null
@@ -0,0 +1,842 @@
+/****************************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * arch/arm/src/sidk_s5jt200/src/s5jt200_eeprom.c
+ *
+ *   Copyright (C) 2014-2015 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+#include <tinyara/spi/spi.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <tinyara/board.h>
+#include <arch/board/board.h>
+
+#include <tinyara/config.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <tinyara/configdata.h>
+
+#include "sidk_s5jt200.h"
+
+#include <chip.h>
+
+#include <errno.h>
+
+#include <tinyara/kmalloc.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/*
+ * To use this logic, you should provide a C file that does the following:
+ *
+ * - Defines SPI_SETSCK and SPI_CLRSCK to set and clear the SCK signal
+ * - Defines SPI_SETMOSI and SPI_CLRMOSI to set and clear the MISO signal
+ * - Defines SPI_GETMISO to sample the MISO state
+ * - Defines SPI_PERBIT_NSEC which is the minimum time to transfer one bit.
+ *   This determines the maximum frequency.
+ * - Other configuration options:
+ *   SPI_BITBAND_LOOPSPERMSEC - Delay loop calibration
+ *   SPI_BITBANG_DISABLEMODE0 - Define to disable Mode 0 support
+ *   SPI_BITBANG_DISABLEMODE1 - Define to disable Mode 1 support
+ *   SPI_BITBANG_DISABLEMODE2 - Define to disable Mode 2 support
+ *   SPI_BITBANG_DISABLEMODE3 - Define to disable Mode 3 support
+ * - Provide implementations of spi_select(), spi_status(), and spi_cmddata().
+ * - Then include this file
+ * - Provide an initialization function that initializes the GPIO pins used
+ *   in the bit bang interface and calls spi_create_bitbang().
+ */
+
+#define SPI_SETSCK  gpio_set_value(gpio_clk, 1)
+#define SPI_CLRSCK  gpio_set_value(gpio_clk, 0)
+
+#define SPI_SETMOSI  gpio_set_value(gpio_mosi, 1)
+#define SPI_CLRMOSI  gpio_set_value(gpio_mosi, 0)
+
+#define SPI_GETMISO  gpio_get_value(gpio_miso)
+
+#define SPI_PERBIT_NSEC 1000000
+
+#define SPI_BITBAND_LOOPSPERMSEC 30000000
+
+#define SPI_BITBANG_DISABLEMODE1 1
+#define SPI_BITBANG_DISABLEMODE2 1
+#define SPI_BITBANG_DISABLEMODE3 1
+
+/* EEPROM commands */
+#define EE_CMD_WRITE 0x5
+#define EE_CMD_READ  0x6
+#define EE_CMD_WRDIS 0x4
+#define EE_CMD_WREN  0x4
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* Supported device geometries */
+
+typedef enum {
+       M93C46_1B,
+       M93C56_1B,
+       M93C66_1B,
+       M93C76_1B,
+       M93C86_1B,
+       M93C46_2B,
+       M93C56_2B,
+       M93C66_2B,
+       M93C76_2B,
+       M93C86_2B
+} ee_dev_type;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+static s32 gpio_clk, gpio_ss, gpio_miso, gpio_mosi;
+
+/* Device geometry description, compact form (2 bytes per entry) */
+
+struct ee_geom_s {
+       uint8_t bytes: 4;       /* Power of two of 128 bytes (0:128 1:256 2:512 etc) */
+       uint8_t org: 4;         /* Organization, bytes */
+       uint8_t addrlen: 4;     /* Number of bytes in command address field */
+       uint8_t flags: 4;       /* Special Features ??? */
+};
+
+static const struct ee_geom_s g_ee_devices[] = {
+       {0, 0, 7, 0},           /* M93C46     128  1byte org */
+       {1, 0, 9, 0},           /* M93C56     256  1byte org */
+       {2, 0, 9, 0},           /* M93C66     512  1byte org */
+       {3, 0, 11, 0},          /* M93C76    1024  1byte org */
+       {4, 0, 11, 0},          /* M93C86    2048  1byte org */
+
+       {0, 1, 6, 0},           /* M93C46     128  2bytes org */
+       {1, 1, 8, 0},           /* M93C56     256  2bytes org */
+       {2, 1, 8, 0},           /* M93C66     512  2bytes org */
+       {3, 1, 10, 0},          /* M93C76    1024  2bytes org */
+       {4, 1, 10, 0},          /* M93C86    2048  2bytes org */
+};
+
+/* Private data attached to the inode */
+
+struct ee_dev_s {
+       struct spi_dev_s *spi;  /* SPI device where the EEPROM is attached */
+       uint32_t size;          /* in bytes, expanded from geometry */
+       uint16_t org_size;      /* access block size, power of 2, expanded from geometry */
+       uint16_t addrlen;       /* number of BITS in data addresses */
+       sem_t sem;              /* file access serialization */
+       uint8_t refs;           /* The number of times the device has been opened */
+       uint8_t readonly;       /* Flags */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#include <tinyara/spi/spi_bitbang.c>
+
+/****************************************************************************
+* Name: spi_select
+*
+* Description:
+*   This function is used to selectdesired Slave on SPI bus.
+*
+* Input Parameters:
+*   priv - Private structure
+*   devid - Device ID.
+*   selected - Select/Deselect argument
+*
+*
+* Returned Value:
+*   None
+*
+****************************************************************************/
+static void spi_select(FAR struct spi_bitbang_s *priv, enum spi_dev_e devid, bool selected)
+{
+       gpio_set_value(gpio_ss, selected);
+}
+
+/****************************************************************************
+* Name: spi_ststus
+*
+* Description:
+*   This function is used to determine, if  WRITE/ERASE procedure is over.
+*   Within hardcoded time interval (5uS), write procedure should be over.
+*   If after 5uS Status is not BUSY, function returns 1.
+*
+* Input Parameters:
+*   priv - Private structure
+*   devid - Device ID.
+*
+*
+* Returned Value:
+*   0 - BUSY
+*   1 - FREE
+*
+****************************************************************************/
+static uint8_t spi_status(FAR struct spi_bitbang_s *priv, enum spi_dev_e devid)
+{
+       gpio_set_value(gpio_ss, true);
+
+       usleep(5000);
+       if (gpio_get_value(gpio_miso) != 0) {
+               goto OK_Exit;
+       }
+
+       usleep(5000);
+       if (gpio_get_value(gpio_miso) != 0) {
+               goto OK_Exit;
+       }
+
+       gpio_set_value(gpio_ss, false);
+       return 0;
+
+OK_Exit:
+       gpio_set_value(gpio_ss, false);
+       return 1;
+}
+
+/****************************************************************************
+* Name: spi_cmddata
+*
+* Description:
+*   This is placeholder function. Empty.
+*
+* Input Parameters:
+*   priv - Private structure
+*   devid - Device ID.
+*   bool - command
+*
+*
+* Returned Value:
+*   0
+*
+****************************************************************************/
+static int spi_cmddata(FAR struct spi_bitbang_s *priv, enum spi_dev_e devid, bool cmd)
+{
+       return 0;
+}
+
+/****************************************************************************
+ * Private Stuff
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int ee_fopen(FAR struct file *filep);
+static int ee_fclose(FAR struct file *filep);
+static off_t ee_fseek(FAR struct file *filep, off_t offset, int whence);
+static ssize_t ee_fread(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t ee_fwrite(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int ee_fioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/* Driver operations */
+
+static const struct file_operations ee_fops = {
+       ee_fopen,       /* open */
+       ee_fclose,      /* close */
+       ee_fread,       /* read */
+       ee_fwrite,      /* write */
+       ee_fseek,       /* seek */
+       ee_fioctl,      /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+       0,              /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Name: ee_lock
+ ****************************************************************************/
+
+static void ee_lock(FAR struct spi_dev_s *dev)
+{
+       /*
+        * On SPI buses where there are multiple devices, it will be necessary
+        * to lock SPI to have exclusive access to the buses for a sequence of
+        * transfers.  The bus should be locked before the chip is selected.
+        *
+        * This is a blocking call and will not return until we have exclusive
+        * access to the SPI bus.  We will retain that exclusive access until
+        * the bus is unlocked.
+        */
+
+       (void)SPI_LOCK(dev, true);
+
+       /* After locking the SPI bus, the we also need call the setfrequency,
+        * setbits, and setmode methods to make sure that the SPI is properly
+        * configured for the device.  If the SPI bus is being shared, then
+        * it may have been left in an incompatible state.
+        */
+}
+
+/****************************************************************************
+ * Name: ee25xx_unlock
+ ****************************************************************************/
+
+static inline void ee_unlock(FAR struct spi_dev_s *dev)
+{
+       (void)SPI_LOCK(dev, false);
+}
+
+/****************************************************************************
+ * Name: ee_writeenable
+ *
+ * Description: Enable or disable write operations.
+ * This is required before any write, since a lot of operations automatically
+ * disables the write latch.
+ *
+ ****************************************************************************/
+
+static void ee_writeenable(FAR struct ee_dev_s *eedev, int enable)
+{
+       unsigned int read_cmd;
+
+       SPI_SELECT(eedev->spi, SPIDEV_EEPROM, true);
+       SPI_SETBITS(eedev->spi, 11);
+       read_cmd = (EE_CMD_WREN << 8) | 0xC0;
+       SPI_SNDBLOCK(eedev->spi, &read_cmd, 1);
+       SPI_SELECT(eedev->spi, SPIDEV_EEPROM, false);
+}
+
+/****************************************************************************
+ * Name: ee_write
+ *
+ * Description: Write data to the EEPROM.
+ *
+ ****************************************************************************/
+
+static ssize_t ee_write(FAR struct ee_dev_s *eedev, uint32_t devaddr, FAR const char *data, size_t len)
+{
+       unsigned int write_cmd;
+       ssize_t rc = 0;
+
+       uint16_t org_size = 1 << eedev->org_size; /* access block size, in bytes, expanded from geometry */
+       uint16_t addrlen = eedev->addrlen;        /* number of BITS in data addresses */
+       uint16_t data_bits = 8 * org_size;
+       uint16_t cmd_bits = 3 + addrlen;
+
+       devaddr >>= eedev->org_size;
+
+       while (len > (org_size - 1)) {
+               SPI_SELECT(eedev->spi, SPIDEV_EEPROM, true);
+
+               SPI_SETBITS(eedev->spi, cmd_bits);
+               write_cmd = (EE_CMD_WRITE << addrlen) | devaddr;
+               SPI_SNDBLOCK(eedev->spi, &write_cmd, 1);
+
+               SPI_SETBITS(eedev->spi, data_bits);
+               SPI_SNDBLOCK(eedev->spi, data, 1);
+
+               SPI_SELECT(eedev->spi, SPIDEV_EEPROM, false);
+
+               if (SPI_STATUS(eedev->spi, SPIDEV_EEPROM) == 0) {
+                       printf("\nEEPROM_Write Failed\n");
+               }
+
+               len -= org_size;
+               data += org_size;
+               rc += org_size;
+               devaddr++;
+       }
+
+       return rc;
+}
+
+/****************************************************************************
+ * Name: ee_read
+ *
+ * Description: Read data from EEPROM.
+ *
+ ****************************************************************************/
+static ssize_t ee_read(FAR struct ee_dev_s *eedev, uint32_t devaddr, FAR char *data, size_t len)
+{
+       unsigned int read_cmd;
+       size_t rc = 0;
+
+       uint16_t org_size = 1 << eedev->org_size; /* access block size, in bytes, expanded from geometry */
+       uint16_t addrlen = eedev->addrlen;        /* number of BITS in data addresses */
+       uint16_t data_bits = 8 * org_size;
+       uint16_t cmd_bits = 3 + addrlen;
+
+       devaddr >>= eedev->org_size;
+       while (len > (org_size - 1)) {
+               SPI_SELECT(eedev->spi, SPIDEV_EEPROM, true);
+
+               SPI_SETBITS(eedev->spi, cmd_bits);
+               read_cmd = (EE_CMD_READ << addrlen) | devaddr;
+               SPI_SNDBLOCK(eedev->spi, &read_cmd, 1);
+
+               SPI_SETBITS(eedev->spi, data_bits);
+               SPI_RECVBLOCK(eedev->spi, data, 1);
+               SPI_SELECT(eedev->spi, SPIDEV_EEPROM, false);
+
+               len -= org_size;
+               data += org_size;
+               rc += org_size;
+               devaddr++;
+       }
+
+       return rc;
+}
+
+/****************************************************************************
+ * Name: ee_semtake
+ *
+ * Acquire a resource to access the device.
+ * The purpose of the semaphore is to block tasks that try to access the
+ * EEPROM while another task is actively using it.
+ *
+ ****************************************************************************/
+
+static void ee_semtake(FAR struct ee_dev_s *eedev)
+{
+       /* Take the semaphore (perhaps waiting) */
+
+       while (sem_wait(&eedev->sem) != 0) {
+               /*
+                * The only case that an error should occur here is if
+                * the wait was awakened by a signal.
+                */
+
+               ASSERT(errno == EINTR);
+       }
+}
+
+/****************************************************************************
+ * Name: ee25xx_semgive
+ *
+ * Release a resource to access the device.
+ *
+ ****************************************************************************/
+
+static inline void ee_semgive(FAR struct ee_dev_s *eedev)
+{
+       sem_post(&eedev->sem);
+}
+
+/****************************************************************************
+ * Driver Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ee_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int ee_fopen(FAR struct file *filep)
+{
+       FAR struct inode *inode = filep->f_inode;
+       FAR struct ee_dev_s *eedev;
+       int ret = OK;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+       ee_semtake(eedev);
+
+       /* Increment the reference count */
+
+       if ((eedev->refs + 1) == 0) {
+               ret = -EMFILE;
+       } else {
+               eedev->refs += 1;
+       }
+
+       ee_semgive(eedev);
+       return ret;
+}
+
+/****************************************************************************
+ * Name: ee_close
+ *
+ * Description: Close the block device
+ *
+ ****************************************************************************/
+
+static int ee_fclose(FAR struct file *filep)
+{
+       FAR struct inode *inode = filep->f_inode;
+       FAR struct ee_dev_s *eedev;
+       int ret = OK;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+       ee_semtake(eedev);
+
+       /*
+        * Decrement the reference count. I want the entire close operation
+        * to be atomic wrt other driver operations.
+        */
+
+       if (eedev->refs == 0) {
+               ret = -EIO;
+       } else {
+               eedev->refs -= 1;
+       }
+
+       ee_semgive(eedev);
+       return ret;
+}
+
+/****************************************************************************
+ * Name: ee_seek
+ *
+ * Remark: Copied from bchlib
+ *
+ ****************************************************************************/
+
+static off_t ee_fseek(FAR struct file *filep, off_t offset, int whence)
+{
+       FAR struct ee_dev_s *eedev;
+       off_t newpos;
+       int ret;
+       FAR struct inode *inode = filep->f_inode;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+       ee_semtake(eedev);
+
+       /* Determine the new, requested file position */
+
+       switch (whence) {
+       case SEEK_CUR:
+               newpos = filep->f_pos + offset;
+               break;
+
+       case SEEK_SET:
+               newpos = offset;
+               break;
+
+       case SEEK_END:
+               newpos = eedev->size + offset;
+               break;
+
+       default:
+               /* Return EINVAL if the whence argument is invalid */
+
+               ee_semgive(eedev);
+               return -EINVAL;
+       }
+
+       /*
+        * Opengroup.org:
+        *
+        *  "The lseek() function shall allow the file offset to be set
+        *  beyond the end of the existing data in the file. If data is later
+        *  written at this point, subsequent reads of data in the gap shall
+        *  return bytes with the value 0 until data is actually written into
+        *  the gap."
+        *
+        * We can conform to the first part, but not the second. But return
+        * EINVAL if
+        *
+        *  "...the resulting file offset would be negative for a regular
+        *  file, block special file, or directory."
+        */
+
+       if (newpos >= 0) {
+               filep->f_pos = newpos;
+               ret = newpos;
+       } else {
+               ret = -EINVAL;
+       }
+
+       ee_semgive(eedev);
+       return ret;
+}
+
+/****************************************************************************
+ * Name: ee_read
+ ****************************************************************************/
+
+static ssize_t ee_fread(FAR struct file *filep, FAR char *buffer, size_t len)
+{
+       FAR struct ee_dev_s *eedev;
+       FAR struct inode *inode = filep->f_inode;
+       size_t rc_len = 0;
+       size_t rd_len;
+       uint16_t buff_2;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+
+       if (filep->f_pos >= eedev->size) {
+               return EOF;
+       }
+
+       ee_semtake(eedev);
+
+       /* trim len if read would go beyond end of device */
+
+       if ((filep->f_pos + len) > eedev->size) {
+               len = eedev->size - filep->f_pos;
+       }
+
+       ee_lock(eedev->spi);
+       if (eedev->org_size == 1) {
+               if (filep->f_pos & 1) {
+                       rd_len = ee_read(eedev, filep->f_pos & ~1, (char *)&buff_2, 2);
+                       buffer[0] = buff_2 >> 8;
+
+                       buffer += 1;
+                       filep->f_pos += 1;
+                       rc_len += 1;
+                       len = len - 1;
+               }
+
+               if (len > 1) {
+                       rd_len = ee_read(eedev, filep->f_pos, buffer, len);
+
+                       buffer += rd_len;
+                       filep->f_pos += rd_len;
+                       rc_len += rd_len;
+                       len = len - rd_len;
+               }
+
+               if (len == 1) {
+                       rd_len = ee_read(eedev, filep->f_pos, (char *)&buff_2, 2);
+                       buffer[0] = (char)buff_2;
+
+                       buffer += 1;
+                       filep->f_pos += 1;
+                       rc_len += 1;
+               }
+       } else {
+               rc_len = ee_read(eedev, filep->f_pos, buffer, len);
+
+               filep->f_pos += rc_len;
+       }
+
+       ee_unlock(eedev->spi);
+
+       /* Update the file position */
+       ee_semgive(eedev);
+       return rc_len;
+}
+
+/****************************************************************************
+ * Name: ee_write
+ ****************************************************************************/
+
+static ssize_t ee_fwrite(FAR struct file *filep, FAR const char *buffer, size_t len)
+{
+       FAR struct ee_dev_s *eedev;
+       FAR struct inode *inode = filep->f_inode;
+       int ret = -EACCES;
+       uint16_t buff_2;
+       size_t wr_len;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+
+       if (eedev->readonly) {
+               return ret;
+       }
+
+       /* Forbid writes past the end of the device */
+
+       if (filep->f_pos >= eedev->size) {
+               return -EFBIG;
+       }
+
+       /* Clamp len to avoid crossing the end of the memory */
+
+       if ((len + filep->f_pos) > eedev->size) {
+               len = eedev->size - filep->f_pos;
+       }
+
+       /*
+        * From this point no failure cannot be detected anymore.
+        * The user should verify the write by rereading memory.
+        */
+
+       ret = len; /* save number of bytes written */
+
+       ee_semtake(eedev);
+
+       /* First, write some page-unaligned data */
+       ee_lock(eedev->spi);
+
+       ee_writeenable(eedev, true);
+
+       if (eedev->org_size == 1) {
+               if (filep->f_pos & 1) {
+                       ee_read(eedev, filep->f_pos & ~1, (char *)&buff_2, 2);
+                       buff_2 = (buff_2 & 0x00FF) | ((((uint16_t) *buffer) << 8) & 0xFF00);
+                       ee_write(eedev, filep->f_pos & ~1, (char *)&buff_2, 2);
+
+                       len--;
+                       buffer++;
+                       filep->f_pos++;
+               }
+
+               if (len > 1) {
+                       wr_len = ee_write(eedev, filep->f_pos, buffer, len);
+
+                       len -= wr_len;
+                       buffer += wr_len;
+                       filep->f_pos += wr_len;
+               }
+
+               if (len == 1) {
+                       ee_read(eedev, filep->f_pos, (char *)&buff_2, 2);
+                       buff_2 = (buff_2 & 0xFF00) | (((uint16_t) *buffer) & 0x00FF);
+                       ee_write(eedev, filep->f_pos, (char *)&buff_2, 2);
+
+                       len--;
+                       buffer++;
+                       filep->f_pos++;
+               }
+       } else {
+               wr_len = ee_write(eedev, filep->f_pos, buffer, len);
+               filep->f_pos += wr_len;
+       }
+       ee_writeenable(eedev, false);
+       ee_unlock(eedev->spi);
+
+       /* Then, write remaining bytes at page-aligned addresses */
+
+       ee_semgive(eedev);
+
+       return ret;
+}
+
+/****************************************************************************
+ * Name: ee25xx_ioctl
+ *
+ * Description: TODO: Erase a sector/page/device or read device ID.
+ * This is completely optional and only applies to bigger devices.
+ *
+ ****************************************************************************/
+
+static int ee_fioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+       FAR struct ee_dev_s *eedev;
+       FAR struct inode *inode = filep->f_inode;
+       int ret = 0;
+
+       DEBUGASSERT(inode && inode->i_private);
+       eedev = (FAR struct ee_dev_s *)inode->i_private;
+
+       switch (cmd) {
+       default:
+               (void)eedev;
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+static struct spi_dev_s *eeprom_dev;
+
+/****************************************************************************
+ * Name: ee_initialize
+ *
+ * Description: Bind a EEPROM driver to an SPI bus. The user MUST provide
+ * a description of the device geometry, since it is not possible to read
+ * this information from the device (contrary to the SPI flash devices).
+ *
+ ****************************************************************************/
+int ee_initialize(FAR struct spi_dev_s *dev, FAR char *devname, ee_dev_type devtype, int readonly)
+{
+       FAR struct ee_dev_s *eedev;
+
+       /* Check device type early */
+
+       eedev = (struct ee_dev_s *)kmm_zalloc(sizeof(struct ee_dev_s));
+
+       if (!eedev) {
+               return -ENOMEM;
+       }
+
+       sem_init(&eedev->sem, 0, 1);
+
+       eedev->spi = dev;
+       eedev->size = 128 << g_ee_devices[devtype].bytes;
+       eedev->org_size = g_ee_devices[devtype].org;
+       eedev->addrlen = g_ee_devices[devtype].addrlen;
+       eedev->readonly = ! !readonly;
+
+       return register_driver(devname, &ee_fops, 0666, eedev);
+}
+
+void sidk_s5jt200_eeprom_init(void)
+{
+       gpio_clk = s5j_gpio(GPP4, 0);
+       gpio_ss = s5j_gpio(GPP4, 1);
+       gpio_miso = s5j_gpio(GPP4, 2);
+       gpio_mosi = s5j_gpio(GPP4, 3);
+
+       gpio_cfg_pin(gpio_clk, GPIO_FUNC(1));
+       gpio_set_pull(gpio_clk, GPIO_PULL_NONE);
+       gpio_cfg_pin(gpio_ss, GPIO_FUNC(1));
+       gpio_set_pull(gpio_ss, GPIO_PULL_NONE);
+       gpio_cfg_pin(gpio_miso, GPIO_FUNC(0));
+       gpio_set_pull(gpio_miso, GPIO_PULL_NONE);
+       gpio_cfg_pin(gpio_mosi, GPIO_FUNC(1));
+       gpio_set_pull(gpio_mosi, GPIO_PULL_NONE);
+
+       eeprom_dev = spi_create_bitbang(&g_spiops);
+       ee_initialize(eeprom_dev, "/dev/eeprom", M93C66_2B, false);
+}