From 02db473e97ba3199ee3be80f835b3db40abaec0b Mon Sep 17 00:00:00 2001 From: Antoine Juckler <6445757+ajuckler@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:16:47 +0900 Subject: [PATCH] drivers/eeprom: Set the bus frequency Add EEPIOC_SETSPEED ioctl acting like the MTDIOC_SETSPEED ioctl. The default frequency is settable in the Kconfig. Add xx25xx SPI delay control configurations. Signed-off-by: Antoine Juckler <6445757+ajuckler@users.noreply.github.com> --- .../components/drivers/character/eeprom.rst | 10 ++- drivers/eeprom/Kconfig | 42 ++++++++- drivers/eeprom/i2c_xx24xx.c | 37 ++++---- drivers/eeprom/spi_xx25xx.c | 88 +++++++++++++------ include/nuttx/eeprom/eeprom.h | 21 +++-- 5 files changed, 142 insertions(+), 56 deletions(-) diff --git a/Documentation/components/drivers/character/eeprom.rst b/Documentation/components/drivers/character/eeprom.rst index 25c2813bb2f..143a3fb3e72 100644 --- a/Documentation/components/drivers/character/eeprom.rst +++ b/Documentation/components/drivers/character/eeprom.rst @@ -155,7 +155,15 @@ IOCTL Commands The full list of ``ioctl()`` commands can be found in ``include/nuttx/eeprom/eeprom.h``. -- ``EEPIOC_GEOMETRY``: Get the EEPROM geometry +- ``EEPIOC_GEOMETRY`` + *Argument:* ``struct eeprom_geometry_s *`` + + Get the EEPROM geometry + +- ``EEPIOC_SETSPEED`` + *Argument:* ``uint32_t`` + + Set the SPI/I2C bus frequency File Systems ============ diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index ac0de6fe317..e61bd6e0557 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -24,12 +24,45 @@ if SPI_EE_25XX config EE25XX_SPIMODE int "SPI mode (0-3)" default 0 - depends on SPI_EE_25XX config EE25XX_FREQUENCY int "SPI EEPROM SCK frequency" default 10000000 - depends on SPI_EE_25XX + ---help--- + Default SPI bus frequency, it can be overwritten at runtime using the + EEPIOC_SETSPEED ioctl. See eeprom/eeprom.h. + +config EE25XX_START_DELAY + int "SPI start delay" + depends on SPI_DELAY_CONTROL + range 0 1000000 + default 5000 + ---help--- + The delay between CS active and first CLK. In ns. + +config EE25XX_STOP_DELAY + int "SPI stop delay" + depends on SPI_DELAY_CONTROL + range 0 1000000 + default 5000 + ---help--- + The delay between last CLK and CS inactive. In ns. + +config EE25XX_CS_DELAY + int "SPI CS delay" + depends on SPI_DELAY_CONTROL + range 0 1000000 + default 5000 + ---help--- + The delay between CS inactive and CS active again. In ns. + +config EE25XX_IFDELAY + int "SPI interface delay" + depends on SPI_DELAY_CONTROL + range 0 1000000 + default 5000 + ---help--- + The delay between frames. In ns. endif # SPI_EE_25XX @@ -46,12 +79,13 @@ if I2C_EE_24XX config EE24XX_FREQUENCY int "I2C EEPROM frequency (100000 or 400000)" default 100000 - depends on I2C_EE_24XX + ---help--- + Default I2C bus frequency, it can be overwritten at runtime using the + EEPIOC_SETSPEED ioctl. See eeprom/eeprom.h. config AT24CS_UUID bool "Device driver support for Atmel AT24CSxx UUID" default n - depends on I2C_EE_24XX ---help--- The Atmel AT24CSxx family have a 128-bit UUID which appears as another I2C slave whose address is offset from the EEPROM by +8. diff --git a/drivers/eeprom/i2c_xx24xx.c b/drivers/eeprom/i2c_xx24xx.c index 92d40dd2a27..b9804c46877 100644 --- a/drivers/eeprom/i2c_xx24xx.c +++ b/drivers/eeprom/i2c_xx24xx.c @@ -96,10 +96,6 @@ * Pre-processor Definitions ****************************************************************************/ -#ifndef CONFIG_EE24XX_FREQUENCY -# define CONFIG_EE24XX_FREQUENCY 100000 -#endif - #define UUID_SIZE 16 /**************************************************************************** @@ -124,23 +120,23 @@ struct ee24xx_dev_s { /* Bus management */ - FAR struct i2c_master_s *i2c; /* I2C device where the EEPROM is attached */ - uint32_t freq; /* I2C bus speed */ - uint8_t addr; /* 7-bit unshifted I2C device address */ + FAR struct i2c_master_s *i2c; /* I2C device where the EEPROM is attached */ + uint32_t freq; /* I2C bus speed */ + uint8_t addr; /* 7-bit unshifted I2C device address */ /* Driver management */ - mutex_t lock; /* file write access serialization */ - uint8_t refs; /* Nr of times the device has been opened */ - bool readonly; /* Flags */ + mutex_t lock; /* file write access serialization */ + uint8_t refs; /* Nr of times the device has been opened */ + bool readonly; /* Flags */ /* Expanded from geometry */ - uint32_t size; /* total bytes in device */ - uint16_t pgsize; /* write block size, in bytes */ - uint16_t addrlen; /* number of bytes in data addresses */ - uint16_t haddrbits; /* Number of bits in high address part */ - uint16_t haddrshift; /* bit-shift of high address part */ + uint32_t size; /* total bytes in device */ + uint16_t pgsize; /* write block size, in bytes */ + uint16_t addrlen; /* number of bytes in data addresses */ + uint16_t haddrbits; /* Number of bits in high address part */ + uint16_t haddrshift; /* bit-shift of high address part */ }; /**************************************************************************** @@ -807,6 +803,17 @@ static int ee24xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) } break; + case EEPIOC_SETSPEED: + { + ret = nxmutex_lock(&eedev->lock); + if (ret == OK) + { + eedev->freq = (uint32_t)arg; + nxmutex_unlock(&eedev->lock); + } + } + break; + default: ret = -ENOTTY; } diff --git a/drivers/eeprom/spi_xx25xx.c b/drivers/eeprom/spi_xx25xx.c index a85ed492db8..52a2e413b7a 100644 --- a/drivers/eeprom/spi_xx25xx.c +++ b/drivers/eeprom/spi_xx25xx.c @@ -120,10 +120,6 @@ * Pre-processor Definitions ****************************************************************************/ -#ifndef CONFIG_EE25XX_SPIMODE -# define CONFIG_EE25XX_SPIMODE 0 -#endif - /* EEPROM commands * High bit of low nibble used for A8 in 25xx040/at25040 products */ @@ -176,15 +172,18 @@ struct ee25xx_geom_s struct ee25xx_dev_s { - struct spi_dev_s *spi; /* SPI device where the EEPROM is attached */ - uint16_t devid; /* SPI device ID to manage CS lines in board */ - uint32_t size; /* in bytes, expanded from geometry */ - uint16_t pgsize; /* write block size, in bytes, expanded from geometry */ - uint32_t secsize; /* write sector size, in bytes, expanded from geometry */ - uint16_t addrlen; /* number of BITS in data addresses */ - mutex_t lock; /* file access serialization */ - uint8_t refs; /* The number of times the device has been opened */ - uint8_t readonly; /* Flags */ + FAR struct spi_dev_s *spi; /* SPI device where the EEPROM is attached */ + uint16_t devid; /* SPI device ID to manage CS lines in board */ + uint32_t freq; /* SPI bus frequency in Hz */ + + uint32_t size; /* in bytes, expanded from geometry */ + uint16_t pgsize; /* write block size, in bytes, expanded from geometry */ + uint32_t secsize; /* write sector size, in bytes, expanded from geometry */ + uint16_t addrlen; /* number of BITS in data addresses */ + + mutex_t lock; /* file access serialization */ + uint8_t refs; /* The number of times the device has been opened */ + uint8_t readonly; /* Flags */ }; /**************************************************************************** @@ -297,9 +296,16 @@ static const struct file_operations g_ee25xx_fops = /**************************************************************************** * Name: ee25xx_lock + * + * Description: + * Lock the SPI bus associated with the driver, set its mode and frequency + * + * Input Parameters + * priv - Device structure + * ****************************************************************************/ -static void ee25xx_lock(FAR struct spi_dev_s *dev) +static void ee25xx_lock(FAR struct ee25xx_dev_s *priv) { /* 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 @@ -310,7 +316,7 @@ static void ee25xx_lock(FAR struct spi_dev_s *dev) * bus is unlocked. */ - SPI_LOCK(dev, true); + SPI_LOCK(priv->spi, 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 @@ -318,19 +324,31 @@ static void ee25xx_lock(FAR struct spi_dev_s *dev) * have been left in an incompatible state. */ - SPI_SETMODE(dev, CONFIG_EE25XX_SPIMODE); - SPI_SETBITS(dev, 8); - SPI_HWFEATURES(dev, 0); - SPI_SETFREQUENCY(dev, CONFIG_EE25XX_FREQUENCY); + SPI_SETMODE(priv->spi, CONFIG_EE25XX_SPIMODE); + SPI_SETBITS(priv->spi, 8); + SPI_HWFEATURES(priv->spi, 0); + SPI_SETFREQUENCY(priv->spi, priv->freq); +#ifdef CONFIG_SPI_DELAY_CONTROL + SPI_SETDELAY(priv->spi, CONFIG_EE25XX_START_DELAY, + CONFIG_EE25XX_STOP_DELAY, CONFIG_EE25XX_CS_DELAY, + CONFIG_EE25XX_IFDELAY); +#endif } /**************************************************************************** * Name: ee25xx_unlock + * + * Description: + * Unlock the SPI bus associated with the driver + * + * Input Parameters: + * priv - Device structure + * ****************************************************************************/ -static inline void ee25xx_unlock(FAR struct spi_dev_s *dev) +static inline void ee25xx_unlock(FAR struct ee25xx_dev_s *priv) { - SPI_LOCK(dev, false); + SPI_LOCK(priv->spi, false); } /**************************************************************************** @@ -390,7 +408,7 @@ static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) { /* Select this FLASH part */ - ee25xx_lock(priv->spi); + ee25xx_lock(priv); SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); /* Send "Read Status Register (RDSR)" command */ @@ -406,7 +424,7 @@ static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) /* Deselect the FLASH */ SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - ee25xx_unlock(priv->spi); + ee25xx_unlock(priv); /* Given that writing could take up to a few milliseconds, * the following short delay in the "busy" case will allow @@ -432,13 +450,13 @@ static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) static void ee25xx_writeenable(FAR struct ee25xx_dev_s *eedev, int enable) { - ee25xx_lock(eedev->spi); + ee25xx_lock(eedev); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), true); SPI_SEND(eedev->spi, enable ? EE25XX_CMD_WREN : EE25XX_CMD_WRDIS); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), false); - ee25xx_unlock(eedev->spi); + ee25xx_unlock(eedev); } /**************************************************************************** @@ -453,14 +471,14 @@ static void ee25xx_writepage(FAR struct ee25xx_dev_s *eedev, FAR const char *data, size_t len) { - ee25xx_lock(eedev->spi); + ee25xx_lock(eedev); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), true); ee25xx_sendcmd(eedev->spi, EE25XX_CMD_WRITE, eedev->addrlen, devaddr); SPI_SNDBLOCK(eedev->spi, data, len); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), false); - ee25xx_unlock(eedev->spi); + ee25xx_unlock(eedev); } /**************************************************************************** @@ -645,7 +663,7 @@ static ssize_t ee25xx_read(FAR struct file *filep, FAR char *buffer, len = eedev->size - filep->f_pos; } - ee25xx_lock(eedev->spi); + ee25xx_lock(eedev); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), true); /* STM32F4Disco: There is a 25 us delay here */ @@ -659,7 +677,7 @@ static ssize_t ee25xx_read(FAR struct file *filep, FAR char *buffer, /* STM32F4Disco: There is a 20 us delay here */ SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), false); - ee25xx_unlock(eedev->spi); + ee25xx_unlock(eedev); /* Update the file position */ @@ -803,6 +821,17 @@ static int ee25xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) } break; + case EEPIOC_SETSPEED: + { + ret = nxmutex_lock(&eedev->lock); + if (ret == OK) + { + eedev->freq = (uint32_t)arg; + nxmutex_unlock(&eedev->lock); + } + } + break; + default: ret = -ENOTTY; } @@ -858,6 +887,7 @@ int ee25xx_initialize(FAR struct spi_dev_s *dev, uint16_t spi_devid, eedev->spi = dev; eedev->devid = spi_devid; + eedev->freq = CONFIG_EE25XX_FREQUENCY; eedev->size = 128 << g_ee25xx_devices[devtype].bytes; eedev->pgsize = 8 << g_ee25xx_devices[devtype].pagesize; eedev->secsize = eedev->pgsize << g_ee25xx_devices[devtype].secsize; diff --git a/include/nuttx/eeprom/eeprom.h b/include/nuttx/eeprom/eeprom.h index b64a93aa776..018d50761db 100644 --- a/include/nuttx/eeprom/eeprom.h +++ b/include/nuttx/eeprom/eeprom.h @@ -47,13 +47,20 @@ /* EEPROM IOCTL Commands ************************************************************/ #define EEPIOC_GEOMETRY _EEPIOC(0x000) /* Similar to BIOC_GEOMETRY: - * Return the geometry of the EEPROM - * device. - * IN: Pointer to writable instance of - * struct eeprom_geometry_s in which - * to return the geometry. - * OUT: Data return in user-provided - * buffer. */ + * Return the geometry of the + * EEPROM device. + * IN: Pointer to writable + * instance of struct + * eeprom_geometry_s to be + * populated + * OUT: Data return in user- + * provided buffer. */ + +#define EEPIOC_SETSPEED _EEPIOC(0x001) /* Overwrite the SPI/I2C bus speed + * IN: Bus speed in Hz + * OUT: None (ioctl return value + * provides success/failure + * indication). */ /************************************************************************************ * Type Definitions