From: Ivan Date: Mon, 3 Apr 2017 13:59:55 +0000 (+0900) Subject: sidk_s5jt200: add to support on-board eeprom X-Git-Tag: 1.1_Public_Release~614^2~146 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=23fd8a8b715201a950b8f22d9114c08ee3e6dd7b;p=rtos%2Ftinyara.git sidk_s5jt200: add to support on-board eeprom 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 --- diff --git a/os/arch/arm/src/sidk_s5jt200/Kconfig b/os/arch/arm/src/sidk_s5jt200/Kconfig index 74801cf..f052a07 100644 --- a/os/arch/arm/src/sidk_s5jt200/Kconfig +++ b/os/arch/arm/src/sidk_s5jt200/Kconfig @@ -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 diff --git a/os/arch/arm/src/sidk_s5jt200/include/board.h b/os/arch/arm/src/sidk_s5jt200/include/board.h index 0fb0fb8..522fc06 100644 --- a/os/arch/arm/src/sidk_s5jt200/include/board.h +++ b/os/arch/arm/src/sidk_s5jt200/include/board.h @@ -61,7 +61,13 @@ #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 */ diff --git a/os/arch/arm/src/sidk_s5jt200/src/Makefile b/os/arch/arm/src/sidk_s5jt200/src/Makefile index bf950e2..8c13e16 100644 --- a/os/arch/arm/src/sidk_s5jt200/src/Makefile +++ b/os/arch/arm/src/sidk_s5jt200/src/Makefile @@ -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) diff --git a/os/arch/arm/src/sidk_s5jt200/src/s5jt200_boot.c b/os/arch/arm/src/sidk_s5jt200/src/s5jt200_boot.c index 20d76c5..4fce499 100644 --- a/os/arch/arm/src/sidk_s5jt200/src/s5jt200_boot.c +++ b/os/arch/arm/src/sidk_s5jt200/src/s5jt200_boot.c @@ -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 index 0000000..f72b7c2 --- /dev/null +++ b/os/arch/arm/src/sidk_s5jt200/src/s5jt200_eeprom.c @@ -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 + * + * 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 +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "sidk_s5jt200.h" + +#include + +#include + +#include + +/**************************************************************************** + * 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 + +/**************************************************************************** +* 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); +}