diff --git a/arch/arm/src/samd5e5/Kconfig b/arch/arm/src/samd5e5/Kconfig index f2c45b18ba..63363cc793 100644 --- a/arch/arm/src/samd5e5/Kconfig +++ b/arch/arm/src/samd5e5/Kconfig @@ -549,6 +549,26 @@ config SAMD5E5_WDT bool "Watchdog Timer" default n +menuconfig SAMD5E5_PROGMEM + bool "FLASH program memory" + default n + select ARCH_HAVE_PROGMEM + ---help--- + Enable support FLASH interfaces as defined in include/nuttx/progmem.h + +if SAMD5E5_PROGMEM + +config SAMD5E5_PROGMEM_NSECTORS + int "Number of 32Kbytes sectors" + default 2 + range 1 32 + ---help--- + This is the number of 32Kbytes FLASH sectors at the end of the program + flash memory that will be reserved for use with by the interfaces + prototyped in include/nuttx/progmem.h + +endif # SAMD5E5_PROGMEM + endmenu # SAMD5x/E5x Peripheral Support config SAMD5E5_DMAC_NDESC diff --git a/arch/arm/src/samd5e5/Make.defs b/arch/arm/src/samd5e5/Make.defs index 4a0b720275..3b90c1b98e 100644 --- a/arch/arm/src/samd5e5/Make.defs +++ b/arch/arm/src/samd5e5/Make.defs @@ -177,3 +177,7 @@ endif ifeq ($(CONFIG_SAMD5E5_RTC),y) CHIP_CSRCS += sam_rtc.c endif + +ifeq ($(CONFIG_SAMD5E5_PROGMEM),y) +CHIP_CSRCS += sam_progmem.c +endif diff --git a/arch/arm/src/samd5e5/hardware/sam_nvmctrl.h b/arch/arm/src/samd5e5/hardware/sam_nvmctrl.h index 1d3ab602d4..c5ed643742 100644 --- a/arch/arm/src/samd5e5/hardware/sam_nvmctrl.h +++ b/arch/arm/src/samd5e5/hardware/sam_nvmctrl.h @@ -66,11 +66,11 @@ #define SAM_NVMCTRL_SEECFG_OFFSET 0x002a /* SmartEEPROM configuration register */ #define SAM_NVMCTRL_SEESTAT_OFFSET 0x002c /* SmartEEPROM status register */ - /* NVMCTRL register addresses ***************************************************************/ #define SAM_NVMCTRL_CTRLA (SAM_NVMCTRL_BASE + SAM_NVMCTRL_CTRLA_OFFSET) #define SAM_NVMCTRL_CTRLB (SAM_NVMCTRL_BASE + SAM_NVMCTRL_CTRLB_OFFSET) +#define SAM_NVMCTRL_PARAM (SAM_NVMCTRL_BASE + SAM_NVMCTRL_PARAM_OFFSET) #define SAM_NVMCTRL_INTENCLR (SAM_NVMCTRL_BASE + SAM_NVMCTRL_INTENCLR_OFFSET) #define SAM_NVMCTRL_INTENSET (SAM_NVMCTRL_BASE + SAM_NVMCTRL_INTENSET_OFFSET) #define SAM_NVMCTRL_INTFLAG (SAM_NVMCTRL_BASE + SAM_NVMCTRL_INTFLAG_OFFSET) @@ -95,15 +95,15 @@ # define NVMCTRL_CTRLA_WMODE_MAN (0 << NVMCTRL_CTRLA_WMODE_SHIFT) /* Manual Write */ # define NVMCTRL_CTRLA_WMODE_ADW (1 << NVMCTRL_CTRLA_WMODE_SHIFT) /* Automatic Double Word Write */ # define NVMCTRL_CTRLA_WMODE_AQW (2 << NVMCTRL_CTRLA_WMODE_SHIFT) /* Automatic Quad Word */ -# define NVMCTRL_CTRLA_WMODE_AP (2 << NVMCTRL_CTRLA_WMODE_SHIFT) /* Automatic Page Write */ -#define NVMCTRL_CTRLA_PRM_SHIFT (8) /* Bits 8-9: Power Reduction Mode during Sleep */ +# define NVMCTRL_CTRLA_WMODE_AP (3 << NVMCTRL_CTRLA_WMODE_SHIFT) /* Automatic Page Write */ +#define NVMCTRL_CTRLA_PRM_SHIFT (6) /* Bits 8-9: Power Reduction Mode during Sleep */ #define NVMCTRL_CTRLA_PRM_MASK (3 << NVMCTRL_CTRLA_PRM_SHIFT) # define NVMCTRL_CTRLA_PRM_SEMIAUTO (0 << NVMCTRL_CTRLA_PRM_SHIFT) /* Enter low power on STANDBY/SPRM cmd; * Exit on first access */ # define NVMCTRL_CTRLA_PRM_FULLAUTO (1 << NVMCTRL_CTRLA_PRM_SHIFT) /* Enter/Exit low power on STANDBY */ # define NVMCTRL_CTRLA_PRM_MANUAL (3 << NVMCTRL_CTRLA_PRM_SHIFT) /* Enter low power only on SPRM cmd; * Exit on first access */ -#define NVMCTRL_CTRLA_RWS_SHIFT (8) /* Bits 8-11: NVM Read Wait States */ +#define NVMCTRL_CTRLA_RWS_SHIFT (8) /* Bits 8-11: NVM Read Wait States */ #define NVMCTRL_CTRLA_RWS_MASK (15 << NVMCTRL_CTRLA_RWS_SHIFT) # define NVMCTRL_CTRLA_RWS(n) ((uint32_t)(n) << NVMCTRL_CTRLA_RWS_SHIFT) #define NVMCTRL_CTRLA_AHBNS0 (1 << 12) /* Bit 12: Force AHB0 access to Non-Sequential */ @@ -139,7 +139,7 @@ # define NVMCTRL_CTRLB_CMD_USEE (0x35 << NVMCTRL_CTRLB_CMD_SHIFT) /* Unlock SmartEEPROM data*/ # define NVMCTRL_CTRLB_CMD_LSEER (0x36 << NVMCTRL_CTRLB_CMD_SHIFT) /* Lock SmartEEPROM registers */ # define NVMCTRL_CTRLB_CMD_USEER (0x37 << NVMCTRL_CTRLB_CMD_SHIFT) /* Unlock SmartEEPROM registers */ -#define NVMCTRL_CTRLB_CMDEX_SHIFT (8) /* Bits 8-15: Command Execution */ +#define NVMCTRL_CTRLB_CMDEX_SHIFT (8) /* Bits 8-15: Command Execution */ #define NVMCTRL_CTRLB_CMDEX_MASK (0xff << NVMCTRL_CTRLB_CMDEX_SHIFT) # define NVMCTRL_CTRLB_CMDEX_KEY (0xa5 << NVMCTRL_CTRLB_CMDEX_SHIFT) @@ -158,11 +158,12 @@ # define NVMCTRL_PARAM_PSZ_256B (5 << NVMCTRL_PARAM_PSZ_SHIFT) /* 256 bytes */ # define NVMCTRL_PARAM_PSZ_512B (6 << NVMCTRL_PARAM_PSZ_SHIFT) /* 512 bytes */ # define NVMCTRL_PARAM_PSZ_1KB (7 << NVMCTRL_PARAM_PSZ_SHIFT) /* 1024 bytes */ -#define NVMCTRL_PARAM_SEE_ (1 << 31) /* Bit 31: SmartEEPROM Supported */ +#define NVMCTRL_PARAM_SEE_ (1 << 31) /* Bit 31: SmartEEPROM Supported */ -/* Interrupt clear register */ -/* Interrupt set register */ -/* Interface flags status and clear register */ +/* Interrupt clear register + * Interrupt set register + * Interface flags status and clear register + */ #define NVMCTRL_INT_DONE (1 << 0) /* Bit 0: Command done interrupt */ #define NVMCTRL_INT_ADDRE (1 << 1) /* Bit 1: Address error interrupt */ @@ -208,7 +209,7 @@ # define NVMCTRL_ECCERR_TYPEL_NONE (0 << NVMCTRL_ECCERR_TYPEL_SHIFT) /* No error detected */ # define NVMCTRL_ECCERR_TYPEL_SINGLE (1 << NVMCTRL_ECCERR_TYPEL_SHIFT) /* Single error(s) detected */ # define NVMCTRL_ECCERR_TYPEL_DUAL (2 << NVMCTRL_ECCERR_TYPEL_SHIFT) /* Dual error(s) detected */ -#define NVMCTRL_ECCERR_TYPEH_SHIFT (30) /* Bits 30-31: High double-word error type */ +#define NVMCTRL_ECCERR_TYPEH_SHIFT (30) /* Bits 30-31: High double-word error type */ #define NVMCTRL_ECCERR_TYPEH_MASK (3 << NVMCTRL_ECCERR_TYPEH_SHIFT) # define NVMCTRL_ECCERR_TYPEH_NONE (0 << NVMCTRL_ECCERR_TYPEH_SHIFT) /* No error detected */ # define NVMCTRL_ECCERR_TYPEH_SINGLE (1 << NVMCTRL_ECCERR_TYPEH_SHIFT) /* Single error(s) detected */ @@ -247,7 +248,7 @@ ********************************************************************************************/ /******************************************************************************************** - * Public Functions + * Public Functions Prototypes ********************************************************************************************/ #endif /* __ARCH_ARM_SRC_SAMD5E5_HARDWARE_SAM_NVMCTRL_H */ diff --git a/arch/arm/src/samd5e5/sam_progmem.c b/arch/arm/src/samd5e5/sam_progmem.c new file mode 100644 index 0000000000..0d17ee230b --- /dev/null +++ b/arch/arm/src/samd5e5/sam_progmem.c @@ -0,0 +1,1011 @@ +/**************************************************************************** + * arch/arm/src/samd5e5/sam_progmem.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "arm_arch.h" + +#include "hardware/sam_memorymap.h" +#include "hardware/sam_nvmctrl.h" + +#include "sam_progmem.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_SAMD5E5_PROGMEM_NSECTORS +# error CONFIG_SAMD5E5_PROGMEM_NSECTORS is not defined +#endif + +#define USE_WRITE_WQW +#define USE_UNLOCK +#define USE_LOCK + +/* Chip dependencies */ + +#if defined(CONFIG_ARCH_CHIP_SAMD5X) +/* The page size is 512 bytes. However, the smallest thing that can be + * erased is 16 pages. We will refer to this as a "cluster". + */ + +# define SAMD5E5_SECTOR_SHIFT (15) /* 2**15 = 32KB SAMD51J20 */ +# define SAMD5E5_PAGE_SHIFT (9) /* 2**9 = 512B */ +# define SAMD5E5_CLUSTER_SHIFT (13) /* 2**13 = 8*KB = 16 pages */ +# define SAMD5E5_LOCK_REGION_SHIFT (15) /* 2**15 = 32KB SAMD51J20 */ +#else +# error FLASH geometry for this SAMD5E5 chip not known +#endif + +/* Sizes and masks */ + +#define SAMD5E5_SECTOR_SIZE (1 << SAMD5E5_SECTOR_SHIFT) +#define SAMD5E5_SECTOR_MASK (SAMD5E5_SECTOR_SIZE - 1) + +#define SAMD5E5_PAGE_SIZE (1 << SAMD5E5_PAGE_SHIFT) +#define SAMD5E5_PAGE_WORDS (1 << (SAMD5E5_PAGE_SHIFT - 2)) +#define SAMD5E5_PAGE_MASK (SAMD5E5_PAGE_SIZE - 1) + +#define SAMD5E5_CLUSTER_SIZE (1 << SAMD5E5_CLUSTER_SHIFT) +#define SAMD5E5_CLUSTER_MASK (SAMD5E5_CLUSTER_SHIFT - 1) + +/* Relationships */ + +#define SAMD5E5_PAGE2SEC_SHIFT (SAMD5E5_SECTOR_SHIFT - SAMD5E5_PAGE_SHIFT) +#define SAMD5E5_PAGE_PER_SEC (1 << SAMD5E5_PAGE2SEC_SHIFT) + +#define SAMD5E5_PAGE2CLUST_SHIFT (SAMD5E5_CLUSTER_SHIFT - SAMD5E5_PAGE_SHIFT) +#define SAMD5E5_PAGE_PER_CLUSTER (1 << SAMD5E5_PAGE2CLUST_SHIFT) + +#define SAMD5E5_CLUST2SECT_SHIFT (SAMD5E5_SECTOR_SHIFT - SAMD5E5_CLUSTER_SHIFT) +#define SAMD5E5_CLUSTER_PER_SEC (1 << SAMD5E5_CLUST2SECT_SHIFT) + +/* Conversions */ + +#define SAMD5E5_BYTE2PAGE(o) ((o) >> SAMD5E5_PAGE_SHIFT) +#define SAMD5E5_BYTE2CLUST(o) ((o) >> SAMD5E5_CLUSTER_SHIFT) +#define SAMD5E5_BYTE2SECT(o) ((o) >> SAMD5E5_SECTOR_SHIFT) + +#define SAMD5E5_PAGE2BYTE(p) ((p) << SAMD5E5_PAGE_SHIFT) +#define SAMD5E5_PAGE2CLUST(p) ((p) >> SAMD5E5_PAGE2CLUST_SHIFT) +#define SAMD5E5_PAGE2SEC(p) ((p) >> SAMD5E5_PAGE2SEC_SHIFT) + +#define SAMD5E5_CLUST2BYTE(c) ((c) << SAMD5E5_CLUSTER_SHIFT) +#define SAMD5E5_CLUST2PAGE(c) ((c) << SAMD5E5_PAGE2CLUST_SHIFT) +#define SAMD5E5_CLUST2SEC(c) ((c) >> SAMD5E5_CLUST2SECT_SHIFT) + +#define SAMD5E5_SEC2BYTE(s) ((s) << SAMD5E5_SECTOR_SHIFT) +#define SAMD5E5_SEC2PAGE(s) ((s) << SAMD5E5_PAGE2SEC_SHIFT) +#define SAMD5E5_SEC2CLUST(s) ((s) << SAMD5E5_CLUST2SECT_SHIFT) + +/* Lock region */ + +#define SAMD5E5_LOCK_REGION_SIZE (1 << SAMD5E5_LOCK_REGION_SHIFT) +#define SAMD5E5_LOCK_REGION_MASK (SAMD5E5_LOCK_REGION_SIZE - 1) + +/* Total FLASH sizes */ + +#define SAMD5E5_TOTAL_NSECTORS (SAMD5E5_FLASH_SIZE >> SAMD5E5_SECTOR_SHIFT) +#define SAMD5E5_TOTAL_NPAGES SAMD5E5_SEC2PAGE(SAMD5E5_TOTAL_NSECTORS) +#define SAMD5E5_TOTAL_NCLUSTERS SAMD5E5_SEC2CLUST(SAMD5E5_TOTAL_NSECTORS) + +/* Start and size of the programmable region */ + +#define SAMD5E5_PROGMEM_NBYTES (CONFIG_SAMD5E5_PROGMEM_NSECTORS << SAMD5E5_SECTOR_SHIFT) +#define SAMD5E5_PROGMEM_END (SAM_FLASH_BASE + SAMD5E5_FLASH_SIZE) +#define SAMD5E5_PROGMEM_START (SAMD5E5_PROGMEM_END - SAMD5E5_PROGMEM_NBYTES) + +#define SAMD5E5_PROGMEM_NPAGES SAMD5E5_SEC2PAGE(CONFIG_SAMD5E5_PROGMEM_NSECTORS) +#define SAMD5E5_PROGMEM_ENDPAGE (SAMD5E5_TOTAL_NPAGES) +#define SAMD5E5_PROGMEM_STARTPAGE (SAMD5E5_PROGMEM_ENDPAGE - SAMD5E5_PROGMEM_NPAGES) + +#define SAMD5E5_PROGMEM_NCLUSTERS SAMD5E5_SEC2CLUST(CONFIG_SAMD5E5_PROGMEM_NSECTORS) +#define SAMD5E5_PROGMEM_ENDCLUST (SAMD5E5_TOTAL_NCLUSTERS) +#define SAMD5E5_PROGMEM_STARTCLUST (SAMD5E5_PROGMEM_ENDCLUST - SAMD5E5_PROGMEM_NCLUSTERS) + +#define SAMD5E5_PROGMEM_NSECTORS (CONFIG_SAMD5E5_PROGMEM_NSECTORS) +#define SAMD5E5_PROGMEM_ENDSEC (SAMD5E5_TOTAL_NSECTORS) +#define SAMD5E5_PROGMEM_STARTSEC (SAMD5E5_PROGMEM_ENDSEC - CONFIG_SAMD5E5_PROGMEM_NSECTORS) + +/* Misc stuff */ + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t g_page_buffer[SAMD5E5_PAGE_WORDS]; +static sem_t g_page_sem; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: page_buffer_lock + * + * Description: + * Get exclusive access to the global page buffer + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void page_buffer_lock(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_page_sem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +#define page_buffer_unlock() nxsem_post(&g_page_sem) + +/**************************************************************************** + * Name: nvm_command + * + * Description: + * Send a FLASH command + * + * Input Parameters: + * cmd - The FLASH command to be sent + * arg - The argument to accompany the command + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static int nvm_command(uint16_t cmd, uint32_t arg) +{ + uint16_t regval; + + while (!(getreg16(SAM_NVMCTRL_STATUS) & NVMCTRL_STATUS_READY)) + { + /* Wait until this module isn't busy */ + } + + /* Check for errors */ + + regval = getreg16(SAM_NVMCTRL_INTFLAG); + if ((regval & (NVMCTRL_INT_LOCKE | + NVMCTRL_INT_PROGE | + NVMCTRL_INT_ADDRE)) != 0) + { + ferr("ERROR: cmd=0x%x regval=0x%x\n", cmd, regval); + return -EIO; + } + + putreg16(NVMCTRL_INT_DONE, SAM_NVMCTRL_INTFLAG); + + /* Set address */ + + if (arg) + putreg32(arg, SAM_NVMCTRL_ADDR); + + /* Write the command to the flash command register */ + + putreg16(cmd | NVMCTRL_CTRLB_CMDEX_KEY, SAM_NVMCTRL_CTRLB); + + while (!(getreg16(SAM_NVMCTRL_STATUS) & NVMCTRL_STATUS_READY)) + { + /* Wait until this module isn't busy */ + } + + return OK; +} + +#ifdef USE_UNLOCK +/**************************************************************************** + * Name: nvm_unlock + * + * Description: + * Make sure that the FLASH is unlocked + * + * Input Parameters: + * page - The first page to unlock + * npages - The number of consecutive pages to unlock + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nvm_unlock(size_t page, size_t npages) +{ + size_t start_region; + size_t end_region; + size_t unlockregion; + int ret; + + /* Align the page to the unlock region */ + + end_region = SAMD5E5_PAGE2BYTE(page + npages) & + ~SAMD5E5_LOCK_REGION_MASK; + start_region = SAMD5E5_PAGE2BYTE(page) & + ~SAMD5E5_LOCK_REGION_MASK; + unlockregion = start_region; + + do + { + finfo("INFO: unlock region=%d address=0x%x\n", + unlockregion >> SAMD5E5_LOCK_REGION_SHIFT, unlockregion); + ret = nvm_command(NVMCTRL_CTRLB_CMD_UR, unlockregion); + if (ret < 0) + { + return ret; + } + + unlockregion += SAMD5E5_LOCK_REGION_SIZE; + } + while (unlockregion < end_region); + + return OK; +} +#endif + +#ifdef USE_LOCK + +/**************************************************************************** + * Name: nvm_lock + * + * Description: + * Make sure that the FLASH is locked + * + * Input Parameters: + * page - The first page to lock + * npages - The number of consecutive pages to lock + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nvm_lock(size_t page, size_t npages) +{ + size_t start_region; + size_t end_region; + size_t lockregion; + int ret; + + /* Align the page to the unlock region */ + + end_region = SAMD5E5_PAGE2BYTE(page + npages) & + ~SAMD5E5_LOCK_REGION_MASK; + start_region = SAMD5E5_PAGE2BYTE(page) & + ~SAMD5E5_LOCK_REGION_MASK; + lockregion = start_region; + + do + { + finfo("INFO: lock region=%d address=0x%x\n", + lockregion >> SAMD5E5_LOCK_REGION_SHIFT, lockregion); + ret = nvm_command(NVMCTRL_CTRLB_CMD_LR, lockregion); + if (ret < 0) + { + return ret; + } + + lockregion += SAMD5E5_LOCK_REGION_SIZE; + } + while (lockregion < end_region); + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_progmem_initialize + * + * Description: + * Call to initialize FLASH programming memory access + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_progmem_initialize(void) +{ + uint16_t ctrla; + + ctrla = getreg16(SAM_NVMCTRL_CTRLA); + ctrla &= ~(NVMCTRL_CTRLA_AHBNS1 | + NVMCTRL_CTRLA_AHBNS0 | + NVMCTRL_CTRLA_PRM_MASK | + NVMCTRL_CTRLA_WMODE_MASK | + NVMCTRL_CTRLA_SUSPEN); + + ctrla |= NVMCTRL_CTRLA_CACHEDIS1 | + NVMCTRL_CTRLA_CACHEDIS0 | + NVMCTRL_CTRLA_PRM_MANUAL | + NVMCTRL_CTRLA_WMODE_MAN | + NVMCTRL_CTRLA_AUTOWS; + putreg16(ctrla, SAM_NVMCTRL_CTRLA); + + /* Initialize the semaphore that manages exclusive access to the global + * page buffer. + */ + + nxsem_init(&g_page_sem, 0, 1); +} + +/**************************************************************************** + * Name: up_progmem_neraseblocks + * + * Description: + * Return number of clusters in the available FLASH memory. + * + ****************************************************************************/ + +size_t up_progmem_neraseblocks(void) +{ + return SAMD5E5_PROGMEM_NCLUSTERS; +} + +/**************************************************************************** + * Name: up_progmem_isuniform + * + * Description: + * Cluster size is uniform? Say 'yes' even though that is not strictly + * true do to the odd organization of sector 0. + * + ****************************************************************************/ + +bool up_progmem_isuniform(void) +{ + return true; +} + +/**************************************************************************** + * Name: up_progmem_pagesize + * + * Description: + * Return page size + * + ****************************************************************************/ + +size_t up_progmem_pagesize(size_t cluster) +{ + return SAMD5E5_PAGE_SIZE; +} + +/**************************************************************************** + * Name: up_progmem_erasesize + * + * Description: + * Return cluster size + * + ****************************************************************************/ + +size_t up_progmem_erasesize(size_t cluster) +{ + return SAMD5E5_CLUSTER_SIZE; +} + +/**************************************************************************** + * Name: up_progmem_getpage + * + * Description: + * Address to page conversion + * + * Input Parameters: + * address - Address with or without flash offset + * + * Returned Value: + * Page or negative value on error. The following errors are reported + * (errno is not set!): + * + * -EFAULT: On invalid address + * + ****************************************************************************/ + +ssize_t up_progmem_getpage(size_t address) +{ + if (address >= SAMD5E5_PROGMEM_START) + { + address -= SAMD5E5_PROGMEM_START; + } + + if (address >= SAMD5E5_PROGMEM_NBYTES) + { + return -EFAULT; + } + + return address >> SAMD5E5_PAGE_SHIFT; +} + +/**************************************************************************** + * Name: up_progmem_getaddress + * + * Description: + * Cluster to address conversion + * + * Input Parameters: + * page - page index + * + * Returned Value: + * Base address of given page, maximum size if page index is not valid. + * + ****************************************************************************/ + +size_t up_progmem_getaddress(size_t page) +{ + if (page >= SAMD5E5_PROGMEM_NPAGES) + { + return SAMD5E5_PROGMEM_NBYTES; + } + + return (page << SAMD5E5_PAGE_SHIFT) + SAMD5E5_PROGMEM_START; +} + +/**************************************************************************** + * Name: up_progmem_eraseblock + * + * Description: + * Erase selected cluster. + * + * Input Parameters: + * cluster - cluster index to be erased + * + * Returned Value: + * Page size or negative value on error. The following errors are reported + * (errno is not set!): + * + * -EFAULT: On invalid cluster + * -EIO: On unsuccessful erase + * -EROFS: On access to write protected area + * -EACCES: Insufficient permissions (read/write protected) + * -EPERM: If operation is not permitted due to some other constraints + * (i.e. some internal block is not running etc.) + * + ****************************************************************************/ + +ssize_t up_progmem_eraseblock(size_t cluster) +{ + uint32_t page; + int ret; + + finfo("INFO: cluster=%d\n", cluster); + if (cluster >= SAMD5E5_PROGMEM_NCLUSTERS) + { + return -EFAULT; + } + + /* Get the page number of the start of the cluster */ + + page = SAMD5E5_CLUST2PAGE((uint32_t)cluster) + SAMD5E5_PROGMEM_STARTPAGE; + + /* Erase all pages in the cluster */ + +#ifdef USE_UNLOCK + (void)nvm_unlock(page, SAMD5E5_PAGE_PER_CLUSTER); +#endif + + finfo("INFO: erase block=%d address=0x%x\n", + page, SAMD5E5_PAGE2BYTE(page)); + ret = nvm_command(NVMCTRL_CTRLB_CMD_EB, SAMD5E5_PAGE2BYTE(page)); + +#ifdef USE_LOCK + (void)nvm_lock(page, SAMD5E5_PAGE_PER_CLUSTER); +#endif + + if (ret < 0) + { + return ret; + } + + /* Verify that the cluster of pages is really erased */ + + if (up_progmem_ispageerased(cluster) == 0) + { + return SAMD5E5_CLUSTER_SIZE; /* Success */ + } + else + { + return -EIO; /* Failure */ + } +} + +/**************************************************************************** + * Name: up_progmem_ispageerased + * + * Description: + * Checks whether cluster is erased + * + * Input Parameters: + * cluster - cluster to be checked + * + * Returned Value: + * Returns number of bytes erased or negative value on error. If it + * returns zero then complete cluster is empty (erased). + * + * The following errors are reported (errno is not set!) + * -EFAULT: On invalid cluster + * + ****************************************************************************/ + +ssize_t up_progmem_ispageerased(size_t cluster) +{ + size_t address; + size_t nwritten; + int nleft; + + finfo("INFO: cluster=%d\n", cluster); + if (cluster >= SAMD5E5_PROGMEM_NCLUSTERS) + { + return -EFAULT; + } + + /* Flush and invalidate D-Cache for this address range */ + + address = (cluster << SAMD5E5_CLUSTER_SHIFT) + SAMD5E5_PROGMEM_START; + up_flush_dcache(address, address + SAMD5E5_CLUSTER_SIZE); + + /* Verify that the cluster is erased (i.e., all 0xff) */ + + for (nleft = SAMD5E5_CLUSTER_SIZE, nwritten = 0; + nleft > 0; + nleft--, address++) + { + if (getreg8(address) != 0xff) + { + nwritten++; + } + } + + if (nwritten) + fwarn("WARN: non written=%d\n", nwritten); + return nwritten; +} + +/**************************************************************************** + * Name: up_progmem_write + * + * Description: + * Program data at given address + * + * Input Parameters: + * address - Address with or without flash offset + * buffer - Pointer to buffer + * buflen - Number of bytes to write + * + * Returned Value: + * Bytes written or negative value on error. The following errors are + * reported (errno is not set!) + * + * EINVAL: If buflen is not aligned with the flash boundaries (i.e. + * some MCU's require per half-word or even word access) + * EFAULT: On invalid address + * EIO: On unsuccessful write + * EROFS: On access to write protected area + * EACCES: Insufficient permissions (read/write protected) + * EPERM: If operation is not permitted due to some other constraints + * (i.e. some internal block is not running etc.) + * + ****************************************************************************/ + +ssize_t up_progmem_write(size_t address, const void *buffer, size_t buflen) +{ + irqstate_t flags; + FAR uint32_t *dest; + FAR const uint32_t *src; + size_t written; + size_t xfrsize; + size_t offset; + size_t page; + size_t i; + int ret; +#ifdef USE_UNLOCK + size_t lock; + size_t locksize; +#endif + + finfo("INFO: address=0x%x buflen=%d\n", address, buflen); + + /* Convert the address into a FLASH byte offset, if necessary */ + + offset = address; + if (address >= SAMD5E5_PROGMEM_START) + { + /* Convert address to an offset relative to be beginning of the + * writable FLASH region. + */ + + offset -= SAMD5E5_PROGMEM_START; + } + + /* Check for valid address range */ + + if ((offset + buflen) > SAMD5E5_PROGMEM_NBYTES) + { + return -EFAULT; + } + + /* Get exclusive access to the global page buffer */ + + page_buffer_lock(); + + /* Get the page number corresponding to the flash offset and the byte + * offset into the page. + */ + + page = SAMD5E5_BYTE2PAGE((uint32_t)offset) + SAMD5E5_PROGMEM_STARTPAGE; + offset &= SAMD5E5_PAGE_MASK; + +#ifdef USE_UNLOCK /* Make sure that the FLASH is unlocked */ + lock = page; + locksize = SAMD5E5_BYTE2PAGE(buflen); + (void)nvm_unlock(lock, locksize); +#endif + + flags = enter_critical_section(); + + /* Loop until all of the data has been written */ + + dest = (FAR uint32_t *)(address & ~SAMD5E5_PAGE_MASK); + written = 0; + while (buflen > 0) + { + /* How much can we write into this page? */ + + xfrsize = MIN((size_t)SAMD5E5_PAGE_SIZE - offset, buflen); + + /* Do we need to use the intermediate buffer? */ + + if (offset == 0 && xfrsize == SAMD5E5_PAGE_SIZE) + { + /* No, we can take the data directly from the user buffer */ + + src = (FAR const uint32_t *)buffer; + } + else + { + /* Yes, copy data into global page buffer */ + + if (offset > 0) + { + memcpy((uint8_t *)g_page_buffer, (uint8_t *)dest, offset); + } + + memcpy((uint8_t *)g_page_buffer + offset, + (uint8_t *)buffer, xfrsize); + + if (offset + xfrsize < SAMD5E5_PAGE_SIZE) + { + memcpy((uint8_t *)g_page_buffer + offset + xfrsize, + (const uint8_t *)dest + offset + xfrsize, + SAMD5E5_PAGE_SIZE - offset - xfrsize); + } + + src = g_page_buffer; + } + +#ifdef USE_WRITE_WQW + if (xfrsize <= (0x10 - (offset & 0xf))) + { + /* Write the page buffer */ + + dest += (offset & ~0xf) >> 2; + src += (offset & ~0xf) >> 2; + + /* Dump flash data */ + + for (i = 0; i < 4; i++) + { + finfo("INFO: dest=%p write 0x%x over 0x%x\n", + dest + i, *(src + i), *(dest + i)); + } + + nvm_command(NVMCTRL_CTRLB_CMD_PBC, 0); + + /* Write the page buffer */ + + for (i = 0; i < 4; i++) + { + *dest++ = *src++; + } + + /* Flush the data cache to memory */ + + up_clean_dcache(address & ~SAMD5E5_PAGE_MASK, + (address & ~SAMD5E5_PAGE_MASK) + SAMD5E5_PAGE_SIZE); + + /* Send the 4 words write command */ + + ret = nvm_command(NVMCTRL_CTRLB_CMD_WQW, 0); + if (ret >= 0) + { + written += xfrsize; + } + + dest -= i; + src -= i; + + /* Compare page data */ + + for (i = 0; i < 4; i++) + { + if (*dest != *src) + { + fwarn("WQW dest=%p (dest 0x%x != src 0x%x) address=0x%x", + dest, *dest, *src, address); + fwarn("offset=0x%x xfrsize=%d buflen=%d ECCERR=0x%x\n", + offset, xfrsize, buflen, getreg32(SAM_NVMCTRL_ECCERR)); + } + + dest++; + src++; + } + } + else + { +#endif + nvm_command(NVMCTRL_CTRLB_CMD_PBC, 0); + + /* Write the page buffer */ + + for (i = 0; i < (SAMD5E5_PAGE_SIZE / sizeof(uint32_t)); i++) + { + *dest++ = *src++; + } + + /* Flush the data cache to memory */ + + up_clean_dcache(address & ~SAMD5E5_PAGE_MASK, + (address & ~SAMD5E5_PAGE_MASK) + SAMD5E5_PAGE_SIZE); + + /* Send the write command */ + + finfo("INFO: WP address=0x%x\n", address & ~SAMD5E5_PAGE_MASK); + ret = nvm_command(NVMCTRL_CTRLB_CMD_WP, 0); + if (ret >= 0) + { + written += xfrsize; + } + + dest -= i; + src -= i; + + /* Compare page data */ + + for (i = 0; i < (SAMD5E5_PAGE_SIZE / sizeof(uint32_t)); i++) + { + if (*dest != *src) + { + fwarn("WQW dest=%p (dest 0x%x != src 0x%x) address=0x%x", + dest, *dest, *src, address); + fwarn("offset=0x%x xfrsize=%d buflen=%d ECCERR=0x%x\n", + offset, xfrsize, buflen, getreg32(SAM_NVMCTRL_ECCERR)); + } + + dest++; + src++; + } + +#ifdef USE_WRITE_WQW + } +#endif + + /* Adjust pointers and counts for the next time through the loop */ + + address += xfrsize; + dest = (FAR uint32_t *)address; + buffer = (FAR void *)((uint8_t *)buffer + xfrsize); + buflen -= xfrsize; + offset = 0; + page++; + } + +#ifdef USE_LOCK + (void)nvm_lock(lock, locksize); +#endif + + leave_critical_section(flags); + page_buffer_unlock(); + return written; +} + +/**************************************************************************** + * The NVM User Row contains calibration data that are + * automatically read at device power on. + * The NVM User Row can be read at address 0x804000. + ****************************************************************************/ + +#ifndef _NVM_USER_ROW_BASE +#define _NVM_USER_ROW_BASE 0x804000 +#endif +#define _NVM_USER_ROW_N_BITS 96 +#define _NVM_USER_ROW_N_BYTES (_NVM_USER_ROW_N_BITS / 8) +#define _NVM_USER_ROW_END (((uint8_t *)_NVM_USER_ROW_BASE) + _NVM_USER_ROW_N_BYTES - 1) +#define _IS_NVM_USER_ROW(b) \ + (((uint8_t *)(b) >= (uint8_t *)(_NVM_USER_ROW_BASE)) && ((uint8_t *)(b) <= (uint8_t *)(_NVM_USER_ROW_END))) +#define _IN_NVM_USER_ROW(b, o) (((uint8_t *)(b) + (o)) <= (uint8_t *)(_NVM_USER_ROW_END)) + +#define _NVM_USER_PAGE_SIZE 512 +#define _NVM_USER_PAGE_OFFSET 32 +#define _IS_NVM_USER_PAGE(b) \ + (((uint8_t *)(b) >= (uint8_t *)(_NVM_USER_ROW_BASE)) && ((uint8_t *)(b) <= (((uint8_t *)_NVM_USER_ROW_BASE) + _NVM_USER_PAGE_SIZE - 1))) + +/**************************************************************************** + * The NVM Software Calibration Area can be read at address 0x00800080. + * The NVM Software Calibration Area can not be written. + ****************************************************************************/ + +#ifndef _NVM_SW_CALIB_AREA_BASE +#define _NVM_SW_CALIB_AREA_BASE 0x00800080 +#endif +#define _NVM_SW_CALIB_AREA_N_BITS 45 +#define _NVM_SW_CALIB_AREA_N_BYTES (_NVM_SW_CALIB_AREA_N_BITS / 8) +#define _NVM_SW_CALIB_AREA_END (((uint8_t *)_NVM_SW_CALIB_AREA_BASE) + _NVM_SW_CALIB_AREA_N_BYTES - 1) +#define _IS_NVM_SW_CALIB_AREA(b) \ + (((uint8_t *)(b) >= (uint8_t *)_NVM_SW_CALIB_AREA_BASE) && ((uint8_t *)(b) <= (uint8_t *)_NVM_SW_CALIB_AREA_END)) +#define _IN_NVM_SW_CALIB_AREA(b, o) (((uint8_t *)(b) + (o)) <= (uint8_t *)(_NVM_SW_CALIB_AREA_END)) + +ssize_t up_progmem_writeuserpage(const uint32_t offset, + const uint8_t *buffer, + uint16_t count) +{ + size_t i; + size_t written; + FAR uint32_t *dest; + FAR const uint32_t *src; + uint32_t userpage[128]; /* Copy of user page */ + + ASSERT(buffer); + + /* Parameter check. */ + + if (!_IS_NVM_USER_PAGE(_NVM_USER_ROW_BASE + offset)) + { + return -EFAULT; + } + + /* Cut off if request too many bytes */ + + if (!_IS_NVM_USER_PAGE(_NVM_USER_ROW_BASE + offset + count - 1)) + { + return -EFAULT; + } + + /* Store previous data. */ + + memcpy((uint8_t *)userpage, + ((uint8_t *)_NVM_USER_ROW_BASE), + _NVM_USER_PAGE_SIZE); + + /* Modify with buffer data. */ + + memcpy((uint8_t *)userpage + offset, buffer, count); + + /* Erase AUX page. */ + + nvm_command(NVMCTRL_CTRLB_CMD_EP, _NVM_USER_ROW_BASE); + + dest = (FAR uint32_t *)(_NVM_USER_ROW_BASE); + src = (FAR const uint32_t *)userpage; + for (written = 0; written < + _NVM_USER_PAGE_SIZE; written += 4*sizeof(uint32_t)) + { + /* Page buffer clear & write. */ + + nvm_command(NVMCTRL_CTRLB_CMD_PBC, 0); + + /* Write the page buffer */ + + for (i = 0; i < 4; i++) + { + *dest++ = *src++; + } + + /* Send the 4 words write command */ + + nvm_command(NVMCTRL_CTRLB_CMD_WQW, 0); + + dest -= i; + src -= i; + + /* Compare page data */ + + for (i = 0; i < 4; i++) + { + if (*dest != *src) + { + fwarn("WQW dest=%p (dest 0x%x != src 0x%x) ECCERR=0x%x\n", + dest, *dest, *src, getreg32(SAM_NVMCTRL_ECCERR)); + } + + dest++; + src++; + } + } + + return OK; +} + +ssize_t up_progmem_readuserpage(const uint32_t offset, + uint8_t *buffer, + uint16_t count) +{ + ASSERT(buffer); + + /* Parameter check. */ + + if (!_IS_NVM_USER_PAGE(_NVM_USER_ROW_BASE + offset)) + { + return -EFAULT; + } + + /* Cut off if request too many bytes */ + + if (!_IS_NVM_USER_PAGE(_NVM_USER_ROW_BASE + offset + count - 1)) + { + return -EFAULT; + } + + /* Copy data */ + + memcpy(buffer, ((uint8_t *)_NVM_USER_ROW_BASE) + offset, count); + + return OK; +} diff --git a/arch/arm/src/samd5e5/sam_progmem.h b/arch/arm/src/samd5e5/sam_progmem.h new file mode 100644 index 0000000000..4734c847a7 --- /dev/null +++ b/arch/arm/src/samd5e5/sam_progmem.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * arch/arm/src/samd5e5/sam_progmem.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMD5E5_SAM_PROGMEM_H +#define __ARCH_ARM_SRC_SAMD5E5_SAM_PROGMEM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "arm_arch.h" +#include "hardware/sam_nvmctrl.h" + +#include + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_progmem_initialize + * + * Description: + * Call to initialize FLASH programming memory access + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_progmem_initialize(void); + +#endif /* __ARCH_ARM_SRC_SAMD5E5_SAM_PROGMEM_H */ diff --git a/boards/arm/samd5e5/metro-m4/Kconfig b/boards/arm/samd5e5/metro-m4/Kconfig index 06e5bbba64..5b44bfd4ab 100644 --- a/boards/arm/samd5e5/metro-m4/Kconfig +++ b/boards/arm/samd5e5/metro-m4/Kconfig @@ -45,6 +45,36 @@ config METRO_M4_32KHZXTAL slow clock source. This option will select instead XOSC32 as the slow clock source. +config METRO_M4_AT24_BLOCKMOUNT + bool "AT24 Serial EEPROM auto-mount" + default n + depends on MTD_AT24XX + ---help--- + Automatically initialize the AT24 I2C EEPROM driver when NSH starts. + +choice + prompt "AT24 serial EPPROM configuration" + default METRO_M4_AT24_FTL + depends on METRO_M4_AT24_BLOCKMOUNT + +config METRO_M4_AT24_FTL + bool "Create AT24 block driver" + ---help--- + Create the MTD driver for the AT24 and "wrap" the AT24 as a standard + block driver that could then, for example, be mounted using FAT or + any other file system. Any file system may be used, but there will + be no wear-leveling. + +config METRO_M4_AT24_NXFFS + bool "Create AT24 NXFFS file system" + depends on FS_NXFFS + ---help--- + Create the MTD driver for the AT24 and mount the AT24 device as + a wear-leveling, NuttX FLASH file system (NXFFS). The downside of + NXFFS is that it can be very slow. + +endchoice # AT24 serial EPPROM configuration + if FS_AUTOMOUNTER config METRO_M4_USB_AUTOMOUNT bool "USB automounter" diff --git a/boards/arm/samd5e5/metro-m4/src/Makefile b/boards/arm/samd5e5/metro-m4/src/Makefile index e8e7b6ca87..29cf8c70fb 100644 --- a/boards/arm/samd5e5/metro-m4/src/Makefile +++ b/boards/arm/samd5e5/metro-m4/src/Makefile @@ -69,6 +69,21 @@ endif ifeq ($(CONFIG_SAMD5E5_SERCOM5_ISI2C),y) CSRCS += sam_i2c.c +ifeq ($(CONFIG_MTD_AT24XX),y) +CSRCS += sam_at24.c +endif +endif + +ifeq ($(CONFIG_FS_SMARTFS),y) +CSRCS += sam_smartfs.c +endif + +ifeq ($(CONFIG_BQ27426),y) +CSRCS += sam_bq27426.c +endif + +ifeq ($(CONFIG_DEV_GPIO),y) +CSRCS += sam_gpio.c endif include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/samd5e5/metro-m4/src/metro-m4.h b/boards/arm/samd5e5/metro-m4/src/metro-m4.h index ba0cc77eca..5228d2e40f 100644 --- a/boards/arm/samd5e5/metro-m4/src/metro-m4.h +++ b/boards/arm/samd5e5/metro-m4/src/metro-m4.h @@ -68,8 +68,47 @@ * D13 PA16 GPIO output */ -#define PORT_RED_LED (PORT_OUTPUT | PORT_PULL_NONE | PORT_OUTPUT_SET | \ - PORTA | PORT_PIN16) +#define PORT_RED_LED (PORT_OUTPUT | PORT_PULL_NONE | PORT_OUTPUT_SET | PORTA | PORT_PIN16) + +#define PORT_D8 (PORT_OUTPUT | PORT_PULL_NONE | PORT_OUTPUT_SET | PORTA | PORT_PIN21) +#define PORT_D9 (PORT_INTERRUPT | PORT_PULL_NONE | PORT_INT_RISING | PORTA | PORT_PIN20) +#define PORT_D10 (PORT_INPUT | PORT_PULL_NONE | PORT_INT_CHANGE | PORTA | PORT_PIN18) + +/* GPIO pins used by the GPIO Subsystem */ + +#define BOARD_NGPIOIN 1 /* Amount of GPIO Input pins */ +#define BOARD_NGPIOOUT 1 /* Amount of GPIO Output pins */ +#define BOARD_NGPIOINT 1 /* Amount of GPIO Input w/ Interruption pins */ + +#define HAVE_AT24 1 + +/* AT24 Serial EEPROM */ + +#define AT24_I2C_BUS 5 /* AT24C256 connected to I2C5 */ +#define AT24_MINOR 0 + +#if !defined(CONFIG_MTD_AT24XX) || !defined(CONFIG_SAMD5E5_SERCOM5_ISI2C) +# undef HAVE_AT24 +#endif + +/* Can't support AT24 features if mountpoints are disabled or if we were not + * asked to mount the AT25 part + */ + +#if defined(CONFIG_DISABLE_MOUNTPOINT) || \ + !defined(CONFIG_METRO_M4_AT24_BLOCKMOUNT) +# undef HAVE_AT24 +#endif + +/* On-chip Programming Memory */ + +#if defined(CONFIG_SAMD5E5_PROGMEM) || defined(CONFIG_MTD_PROGMEM) +#define HAVE_PROGMEM_CHARDEV +#endif + +/* This is the on-chip progmem memroy driver minor number */ + +#define PROGMEM_MTD_MINOR 0 /**************************************************************************** * Public Types @@ -123,5 +162,17 @@ FAR struct i2c_master_s *g_i2c5_dev; int metro_m4_i2cdev_initialize(void); #endif +#ifdef CONFIG_FS_SMARTFS +int sam_smartfs_initialize(void); +#endif + +#ifdef CONFIG_BQ27426 +int sam_bq27426_initialize(FAR const char *devname); +#endif + +#ifdef CONFIG_DEV_GPIO +int sam_gpio_initialize(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_SAMD5E5_METRO_M4_SRC_METRO_M4_H */ diff --git a/boards/arm/samd5e5/metro-m4/src/sam_at24.c b/boards/arm/samd5e5/metro-m4/src/sam_at24.c new file mode 100644 index 0000000000..21f9608af1 --- /dev/null +++ b/boards/arm/samd5e5/metro-m4/src/sam_at24.c @@ -0,0 +1,126 @@ +/**************************************************************************** + * boards/arm/samd5e5/metro-m4/src/sam_at24.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "metro-m4.h" + +#ifdef HAVE_AT24 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_at24_automount + * + * Description: + * Initialize and configure the AT24 serial EEPROM + * + ****************************************************************************/ + +extern FAR struct i2c_master_s *g_i2c5_dev; + +int sam_at24_automount(int minor) +{ + FAR struct mtd_dev_s *mtd; + static bool initialized = false; + int ret; + + /* Have we already initialized? */ + + if (!initialized) + { + /* Now bind the I2C interface to the AT24 I2C EEPROM driver */ + + finfo("Bind the AT24 EEPROM driver to I2C%d\n", AT24_I2C_BUS); + mtd = at24c_initialize(g_i2c5_dev); + if (!mtd) + { + ferr("ERROR: Failed to bind TWI%d to the AT24 EEPROM driver\n", + AT24_I2C_BUS); + return -ENODEV; + } + +#if defined(CONFIG_METRO_M4_AT24_FTL) + /* And finally, use the FTL layer + * to wrap the MTD driver as a block driver + */ + + finfo("Initialize the FTL layer to create /dev/mtdblock%d\n", + AT24_MINOR); + ret = ftl_initialize(AT24_MINOR, mtd); + if (ret < 0) + { + ferr("ERROR: Failed to initialize the FTL layer: %d\n", ret); + return ret; + } + +#elif defined(CONFIG_METRO_M4_AT24_NXFFS) + /* Initialize to provide NXFFS on the MTD interface */ + + finfo("Initialize the NXFFS file system\n"); + ret = nxffs_initialize(mtd); + if (ret < 0) + { + ferr("ERROR: NXFFS initialization failed: %d\n", ret); + return ret; + } + + /* Mount the file system at /mnt/at24 */ + + finfo("Mount the NXFFS file system at /dev/at24\n"); + ret = mount(NULL, "/mnt/at24", "nxffs", 0, NULL); + if (ret < 0) + { + ferr("ERROR: Failed to mount the NXFFS volume: %d\n", + errno); + return ret; + } +#endif + + /* Now we are initialized */ + + initialized = true; + } + + return OK; +} + +#endif /* HAVE_AT24 */ diff --git a/boards/arm/samd5e5/metro-m4/src/sam_bq27426.c b/boards/arm/samd5e5/metro-m4/src/sam_bq27426.c new file mode 100644 index 0000000000..e81ca10ab1 --- /dev/null +++ b/boards/arm/samd5e5/metro-m4/src/sam_bq27426.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * boards/arm/samd5e5/metro-m4/src/sam_bq27426.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "arm_arch.h" +#include "chip.h" +#include "hardware/sam_i2c_master.h" +#include "metro-m4.h" + +#include +#include "sam_config.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef CONFIG_BQ27426 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_bq24298_initialize + * + * Description: + * Called to configure bq24298 + * + ****************************************************************************/ + +int sam_bq27426_initialize(FAR const char *devname) +{ + FAR struct battery_gauge_dev_s *bq27426_m4; + + bq27426_m4 = (FAR struct battery_gauge_dev_s *)bq27426_initialize( + g_i2c5_dev, + BQ27426_I2C_ADDRESS, + 100000); + return battery_gauge_register(devname, bq27426_m4); +} + +#endif /* CONFIG_BQ27426 */ \ No newline at end of file diff --git a/boards/arm/samd5e5/metro-m4/src/sam_bringup.c b/boards/arm/samd5e5/metro-m4/src/sam_bringup.c index 409ccde6b6..261b2e3590 100644 --- a/boards/arm/samd5e5/metro-m4/src/sam_bringup.c +++ b/boards/arm/samd5e5/metro-m4/src/sam_bringup.c @@ -64,6 +64,11 @@ # include #endif +#ifdef CONFIG_BQ27426 + #include + #include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -98,7 +103,7 @@ int sam_bringup(void) ret = mount(NULL, PROCFS_MOUNTPOINT, "procfs", 0, NULL); if (ret < 0) { - syslot(LOG_ERR, "ERROR: Failed to mount procfs at %s: %d\n", + syslog(LOG_ERR, "ERROR: Failed to mount procfs at %s: %d\n", PROCFS_MOUNTPOINT, ret); } #endif @@ -137,12 +142,47 @@ int sam_bringup(void) } #endif +#ifdef CONFIG_FS_SMARTFS + /* Initialize Smart File System (SMARTFS) */ + + ret = sam_smartfs_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to mount smartfs %d\n", ret); + return ret; + } + + /* Mount the file system at /mnt/nvm */ + + ret = mount("/dev/smart0", "/mnt/nvm", "smartfs", 0, NULL); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to mount the SmartFS volume: %d\n", + ret); + return ret; + } +#endif + #ifdef CONFIG_FS_AUTOMOUNTER /* Initialize the auto-mounter */ sam_automount_initialize(); #endif +#ifdef CONFIG_BQ27426 + /* Configure and initialize the BQ2426 distance sensor */ + + ret = sam_bq27426_initialize("/dev/batt1"); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: sam_bq27426_initialize() failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_DEV_GPIO + ret = sam_gpio_initialize(); +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm/samd5e5/metro-m4/src/sam_gpio.c b/boards/arm/samd5e5/metro-m4/src/sam_gpio.c new file mode 100644 index 0000000000..7db1497989 --- /dev/null +++ b/boards/arm/samd5e5/metro-m4/src/sam_gpio.c @@ -0,0 +1,341 @@ +/**************************************************************************** + * boards/arm/samd5e5/metro-m4/src/sam_gpio.c + * + * Copyright 2020 Falker Automacao Agricola LTDA. + * Author: Leomar Mateus Radke + * Author: Ricardo Wartchow + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "irq/irq.h" +#include "chip.h" +#include + +#include "sam_config.h" +#include "metro-m4.h" +#include "sam_port.h" +#include "sam_eic.h" + +#if defined(CONFIG_DEV_GPIO) && !defined(CONFIG_GPIO_LOWER_HALF) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct samgpio_dev_s +{ + struct gpio_dev_s gpio; + uint8_t id; +}; + +struct samgpint_dev_s +{ + struct samgpio_dev_s samgpio; + pin_interrupt_t callback; +}; + +irqstate_t flags; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#if BOARD_NGPIOIN > 0 +static int gpin_read(FAR struct gpio_dev_s *dev, FAR bool *value); +#endif +static int gpout_read(FAR struct gpio_dev_s *dev, FAR bool *value); +static int gpout_write(FAR struct gpio_dev_s *dev, bool value); + +static int gpint_read(FAR struct gpio_dev_s *dev, FAR bool *value); +static int gpint_attach(FAR struct gpio_dev_s *dev, + pin_interrupt_t callback); +static int gpint_enable(FAR struct gpio_dev_s *dev, bool enable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if BOARD_NGPIOIN > 0 + +static const struct gpio_operations_s gpin_ops = +{ + .go_read = gpin_read, + .go_write = NULL, + .go_attach = NULL, + .go_enable = NULL, +}; +#endif +static const struct gpio_operations_s gpout_ops = +{ + .go_read = gpout_read, + .go_write = gpout_write, + .go_attach = NULL, + .go_enable = NULL, +}; + +static const struct gpio_operations_s gpint_ops = +{ + .go_read = gpint_read, + .go_write = NULL, + .go_attach = gpint_attach, + .go_enable = gpint_enable, +}; + +static const int g_extint[1] = /* SAM_IRQ_EXTINT0 offset! */ +{ + 4, /* PORT_D9 - PA20 - EXTINT 4 */ +}; + +#if BOARD_NGPIOIN > 0 +/* This array maps the GPIO pins used as INPUT */ + +static const uint32_t g_gpioinputs[BOARD_NGPIOIN] = +{ + PORT_D10, +}; + +static struct samgpio_dev_s g_gpin[BOARD_NGPIOIN]; +#endif + +#if BOARD_NGPIOOUT +/* This array maps the GPIO pins used as OUTPUT */ + +static const uint32_t g_gpiooutputs[BOARD_NGPIOOUT] = +{ + PORT_D8, +}; + +static struct samgpio_dev_s g_gpout[BOARD_NGPIOOUT]; +#endif + +#if BOARD_NGPIOINT > 0 +/* This array maps the GPIO pins used as INTERRUPT INPUTS */ + +static const uint32_t g_gpiointinputs[BOARD_NGPIOINT] = +{ + PORT_D9 +}; + +static struct samgpint_dev_s g_gpint[BOARD_NGPIOINT]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int samgpio_interrupt(int irq, void *context, void *arg) +{ + FAR struct samgpint_dev_s *samgpint = (FAR struct samgpint_dev_s *)arg; + + DEBUGASSERT(samgpint != NULL && samgpint->callback != NULL); + gpioinfo("Interrupt! callback=%p\n", samgpint->callback); + + samgpint->callback(&samgpint->samgpio.gpio, samgpint->samgpio.id); + return OK; +} +#if BOARD_NGPIOIN > 0 + +static int gpin_read(FAR struct gpio_dev_s *dev, FAR bool *value) +{ + FAR struct samgpio_dev_s *samgpio = (FAR struct samgpio_dev_s *)dev; + + DEBUGASSERT(samgpio != NULL && value != NULL); + DEBUGASSERT(samgpio->id < BOARD_NGPIOIN); + gpioinfo("Reading...\n"); + + *value = sam_portread(g_gpioinputs[samgpio->id]); + return OK; +} +#endif + +static int gpout_read(FAR struct gpio_dev_s *dev, FAR bool *value) +{ + FAR struct samgpio_dev_s *samgpio = (FAR struct samgpio_dev_s *)dev; + + DEBUGASSERT(samgpio != NULL && value != NULL); + DEBUGASSERT(samgpio->id < BOARD_NGPIOOUT); + gpioinfo("Reading...\n"); + + *value = sam_portread(g_gpiooutputs[samgpio->id]); + return OK; +} + +static int gpout_write(FAR struct gpio_dev_s *dev, bool value) +{ + FAR struct samgpio_dev_s *samgpio = (FAR struct samgpio_dev_s *)dev; + + DEBUGASSERT(samgpio != NULL); + DEBUGASSERT(samgpio->id < BOARD_NGPIOOUT); + gpioinfo("Writing %d\n", (int)value); + + sam_portwrite(g_gpiooutputs[samgpio->id], value); + return OK; +} + +static int gpint_read(FAR struct gpio_dev_s *dev, FAR bool *value) +{ + FAR struct samgpint_dev_s *samgpint = (FAR struct samgpint_dev_s *)dev; + + DEBUGASSERT(samgpint != NULL && value != NULL); + DEBUGASSERT(samgpint->samgpio.id < BOARD_NGPIOINT); + gpioinfo("Reading int pin...\n"); + + *value = sam_portread(g_gpiointinputs[samgpint->samgpio.id]); + return OK; +} + +static int gpint_attach(FAR struct gpio_dev_s *dev, + pin_interrupt_t callback) +{ + FAR struct samgpint_dev_s *samgpint = (FAR struct samgpint_dev_s *)dev; + + gpioinfo("Attaching the callback\n"); + + flags = enter_critical_section(); + + /* Make sure the interrupt is disabled */ + + int ret = irq_attach(SAM_IRQ_EXTINT0 + g_extint[samgpint->samgpio.id], + samgpio_interrupt, &g_gpint[samgpint->samgpio.id]); + if (ret == OK) + { + /* Configure the interrupt edge sensitivity + * in CONFIGn register of the EIC + */ + + sam_eic_configure(g_extint[samgpint->samgpio.id], + g_gpiointinputs[samgpint->samgpio.id]); + } + + leave_critical_section(flags); + gpioinfo("Attach %p\n", callback); + samgpint->callback = callback; + return OK; +} + +static int gpint_enable(FAR struct gpio_dev_s *dev, bool enable) +{ + FAR struct samgpint_dev_s *samgpint = (FAR struct samgpint_dev_s *)dev; + flags = enter_critical_section(); + if (enable) + { + if (samgpint->callback != NULL) + { + gpioinfo("Enabling the interrupt\n"); + up_enable_irq(SAM_IRQ_EXTINT0 + g_extint[samgpint->samgpio.id]); + } + } + else + { + up_disable_irq(SAM_IRQ_EXTINT0 + g_extint[samgpint->samgpio.id]); + gpioinfo("Disable the interrupt\n"); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_gpio_initialize + * + * Description: + * Initialize GPIO drivers for use with /apps/examples/gpio + * + ****************************************************************************/ + +int sam_gpio_initialize(void) +{ + int i; + int pincount = 0; + +#if BOARD_NGPIOIN > 0 + for (i = 0; i < BOARD_NGPIOIN; i++) + { + /* Setup and register the GPIO pin */ + + g_gpin[i].gpio.gp_pintype = GPIO_INPUT_PIN; + g_gpin[i].gpio.gp_ops = &gpin_ops; + g_gpin[i].id = i; + (void)gpio_pin_register(&g_gpin[i].gpio, pincount); + + /* Configure the pin that will be used as input */ + + sam_portconfig(g_gpioinputs[i]); + + pincount++; + } +#endif + +#if BOARD_NGPIOOUT > 0 + for (i = 0; i < BOARD_NGPIOOUT; i++) + { + /* Setup and register the GPIO pin */ + + g_gpout[i].gpio.gp_pintype = GPIO_OUTPUT_PIN; + g_gpout[i].gpio.gp_ops = &gpout_ops; + g_gpout[i].id = i; + (void)gpio_pin_register(&g_gpout[i].gpio, pincount); + + /* Configure the pin that will be used as output */ + + sam_portconfig(g_gpiooutputs[i]); + pincount++; + } +#endif + +#if BOARD_NGPIOINT > 0 + for (i = 0; i < BOARD_NGPIOINT; i++) + { + /* Setup and register the GPIO pin */ + + g_gpint[i].samgpio.gpio.gp_pintype = GPIO_INTERRUPT_PIN; + g_gpint[i].samgpio.gpio.gp_ops = &gpint_ops; + g_gpint[i].samgpio.id = i; + (void)gpio_pin_register(&g_gpint[i].samgpio.gpio, pincount); + + /* Configure the pin that will be used as interrupt input */ + + sam_portconfig(g_gpiointinputs[i]); + + pincount++; + } +#endif + + return 0; +} +#endif /* CONFIG_DEV_GPIO && !CONFIG_GPIO_LOWER_HALF */ diff --git a/boards/arm/samd5e5/metro-m4/src/sam_smartfs.c b/boards/arm/samd5e5/metro-m4/src/sam_smartfs.c new file mode 100644 index 0000000000..4f0b6f522a --- /dev/null +++ b/boards/arm/samd5e5/metro-m4/src/sam_smartfs.c @@ -0,0 +1,235 @@ +/**************************************************************************** + * boards/arm/samd5e5/metro-m4/src/sam_smartfs.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "arm_arch.h" +#include "chip.h" +#include + +#include "metro-m4.h" + +#ifdef CONFIG_MTD +# include +# include +# include +# include "sam_progmem.h" +#endif + +#ifdef CONFIG_FS_SMARTFS +# include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_FS_SMARTFS + int ret; + FAR struct mtd_dev_s *mtd; + FAR struct mtd_geometry_s geo; +#endif + +#if defined(CONFIG_MTD_PARTITION_NAMES) + const char *partname = "mnta"; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_FS_SMARTFS +int sam_smartfs_initialize(void) +{ + /* Initialize the SAMD5E5 FLASH programming memory library */ + + sam_progmem_initialize(); + + /* Create an instance of the SAMD5E5 FLASH program memory device driver */ + + mtd = progmem_initialize(); + if (!mtd) + { + syslog(LOG_ERR, "ERROR: progmem_initialize failed\n"); + } + + ret = mtd->ioctl(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&geo)); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: mtd->ioctl failed: %d\n", ret); + return ret; + } + + #ifdef CONFIG_MTD_PARTITION + { + int partno; + int partsize; + int partoffset; + int partszbytes; + int erasesize; + const char *partstring = "256"; + const char *ptr; + FAR struct mtd_dev_s *mtd_part; + char partref[4]; + + /* Now create a partition on the FLASH device */ + + partno = 0; + ptr = partstring; + partoffset = 0; + + /* Get the Flash erase size */ + + erasesize = geo.erasesize; + + while (*ptr != '\0') + { + /* Get the partition size */ + + partsize = atoi(ptr); + partszbytes = (partsize << 10); /* partsize is defined in KB */ + printf("partsize %d partszbytes %d\n", partsize, partszbytes); + + /* Check if partition size is bigger then erase block */ + + if (partszbytes < erasesize) + { + syslog(LOG_ERR, + "ERROR: Partition size is lesser than erasesize!\n"); + return -1; + } + + /* Check if partition size is multiple of erase block */ + + if ((partszbytes % erasesize) != 0) + { + syslog(LOG_ERR, + "ERROR: Partition size is not multiple of erasesize!\n"); + return -1; + } + + mtd_part = mtd_partition(mtd, partoffset, + partszbytes / erasesize); + partoffset += partszbytes / erasesize; + + /* Test if this is the config partition */ + + #if defined CONFIG_MTD_CONFIG + if (partno == 0) + { + /* Register the partition as the config device */ + + mtdconfig_register(mtd_part); + } + else + #endif + { + /* Now initialize a SMART Flash block device + * and bind it to the MTD device. + */ + + #if defined(CONFIG_MTD_SMART) && defined(CONFIG_FS_SMARTFS) + sprintf(partref, "p%d", partno); + smart_initialize(0, mtd_part, partref); + #endif + } + + /* Set the partition name */ + + #ifdef CONFIG_MTD_PARTITION_NAMES + if (!mtd_part) + { + syslog(LOG_ERR, + "Error: failed to create partition %s\n", + partname); + return -1; + } + + mtd_setpartitionname(mtd_part, partname); + + /* Now skip to next name. + * We don't need to split the string here + * because the MTD partition logic will only + * display names up to the comma, + * thus allowing us to use a single static name + * in the code. + */ + + while (*partname != ',' && *partname != '\0') + { + /* Skip to next ',' */ + + partname++; + } + + if (*partname == ',') + { + partname++; + } + #endif + + /* Update the pointer to point to the next size in the list */ + + while ((*ptr >= '0') && (*ptr <= '9')) + { + ptr++; + } + + if (*ptr == ',') + { + ptr++; + } + + /* Increment the part number */ + + partno++; + } + } + #else /* CONFIG_MTD_PARTITION */ + + /* Configure the device with no partition support */ + + smart_initialize(0, mtd, NULL); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: SmartFS initialization failed: %d\n", ret); + return ret; + } + + #endif + + return OK; +} + +#endif /* CONFIG_FS_SMARTFS */ \ No newline at end of file diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 0eb6b2eb29..1e87151779 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -448,6 +448,27 @@ config MAX1704X The MAX17040 is configured to operate with a single lithium cell and the MAX17041 is configured for a dual-cell 2S pack. +config BQ27426 + bool "BQ27426 Battery fuel gauge support" + default n + select I2C + depends on BATTERY_GAUGE + ---help--- + The bq27426 battery fuel gauge accurately predicts the battery capacity and other + operational characteristics of a single, Li-based, rechargeable cell. + It can be interrogated by a system processor to provide cell information, + such as state-of-charge (SOC). + +if BQ27426 + +config DEBUG_BQ27426 + bool "BQ27426 Debug Features" + default n + ---help--- + Enable BQ27426 battery management debug features. + +endif # BQ27426 + config I2C_BQ2425X bool default y if BQ2425X diff --git a/drivers/power/Make.defs b/drivers/power/Make.defs index 4f5c79926f..0d8d7ef19a 100644 --- a/drivers/power/Make.defs +++ b/drivers/power/Make.defs @@ -152,6 +152,12 @@ ifeq ($(CONFIG_I2C_MAX1704X),y) CSRCS += max1704x.c endif +# Add the bq27426 I2C-based battery guage driver + +ifeq ($(CONFIG_BQ27426),y) +CSRCS += bq27426.c +endif + endif # Include battery support in the build diff --git a/drivers/power/bq27426.c b/drivers/power/bq27426.c new file mode 100644 index 0000000000..7c0007e423 --- /dev/null +++ b/drivers/power/bq27426.c @@ -0,0 +1,457 @@ +/**************************************************************************** + * drivers/power/bq27426.c + * + * Lower half driver for BQ27426 battery fuel gauge + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* This driver requires: + * + * CONFIG_BATTERY - Upper half battery driver support + * CONFIG_I2C - I2C support + * CONFIG_BQ27426 - And the driver must be explictly selected. + */ + +#if defined(CONFIG_BATTERY_GAUGE) && defined(CONFIG_I2C) && \ + defined(CONFIG_BQ27426) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_BQ27426 +# define baterr _err +# define batdbg _info +# define batinfo _info +#else +# ifdef CONFIG_CPP_HAVE_VARARGS +# define baterr(x...) +# define batdbg(x...) +# define batinfo(x...) +# else +# define baterr (void) +# define batdbg (void) +# define batinfo(void) +# endif +#endif + +/**************************************************************************** + * Private + ****************************************************************************/ + +/* Parameters for the capacity() function, + * to specify which capacity to read in mAh + */ + +typedef enum +{ + REMAIN, /* Remaining Capacity (DEFAULT) */ + FULL, /* Full Capacity */ + AVAIL, /* Available Capacity */ + AVAIL_FULL, /* Full Available Capacity */ + REMAIN_F, /* Remaining Capacity Filtered */ + REMAIN_UF, /* Remaining Capacity Unfiltered */ + FULL_F, /* Full Capacity Filtered */ + FULL_UF /* Full Capacity Unfiltered */ +} capacity_measure; + +/**************************************************************************** + * Private + ****************************************************************************/ + +struct bq27426_dev_s +{ + /* The common part of the battery driver visible to the upper-half driver */ + + FAR const struct battery_gauge_operations_s *ops; /* Battery operations */ + sem_t batsem; /* Enforce mutually exclusive access */ + + /* Data fields specific to the lower half bq27426 driver follow */ + + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + uint32_t frequency; /* I2C frequency */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* I2C support */ + +static int bq27426_getreg16(FAR struct bq27426_dev_s *priv, uint8_t regaddr, + FAR uint16_t *regval); +static int bq27426_putreg16(FAR struct bq27426_dev_s *priv, uint8_t regaddr, + uint16_t regval); +static inline int bq27426_getvoltage(FAR struct bq27426_dev_s *priv, + b16_t *voltage); +static inline int bq27426_getsoc(FAR struct bq27426_dev_s *priv, + b16_t *soc); +static inline int bq27426_getcapacity(FAR struct bq27426_dev_s *priv, + uint8_t capacity, + b16_t *value); +static inline int bq27426_get_device_id(FAR struct bq27426_dev_s *priv, + uint16_t *id); + +/* Battery driver lower half methods */ + +static int bq27426_state(struct battery_gauge_dev_s *dev, int *status); +static int bq27426_online(struct battery_gauge_dev_s *dev, bool *status); +static int bq27426_voltage(struct battery_gauge_dev_s *dev, b16_t *value); +static int bq27426_capacity_full(struct battery_gauge_dev_s *dev, + b16_t *capacity_full); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct battery_gauge_operations_s g_bq27426ops = +{ + bq27426_state, + bq27426_online, + bq27426_voltage, + bq27426_capacity_full, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bq27426_getreg16 + * + * Description: + * Read a 16-bit value from a bq27426 register pair. + * + * START ACK ACK + * REPEATED-START ACK Data0 ACK Data1 NO-ACK STOP + * + ****************************************************************************/ + +static int bq27426_getreg16(FAR struct bq27426_dev_s *priv, uint8_t regaddr, + FAR uint16_t *regval) +{ + struct i2c_config_s config; + uint8_t buffer[2]; + int ret; + + /* Set up the configuration and perform the write-read operation */ + + config.frequency = priv->frequency; + config.address = priv->addr; + config.addrlen = 7; + + /* Write the register address */ + + ret = i2c_write(priv->i2c, &config, ®addr, 1); + if (ret < 0) + { + baterr("i2c_write failed: %d\n", ret); + return ret; + } + + /* Restart and read 16-bits from the register */ + + ret = i2c_read(priv->i2c, &config, buffer, 2); + if (ret < 0) + { + baterr("i2c_read failed: %d\n", ret); + return ret; + } + + *regval = ((uint16_t) buffer[1] << 8) | buffer[0]; + + return OK; +} + +/**************************************************************************** + * Name: bq27426_putreg16 + * + * Description: + * Write a 16-bit value to a bq27426 register pair. + * + * START ACK ACK Data0 ACK Data1 ACK STOP + * + ****************************************************************************/ + +static int bq27426_putreg16(FAR struct bq27426_dev_s *priv, uint8_t regaddr, + uint16_t regval) +{ + struct i2c_config_s config; + uint8_t buffer[3]; + + baterr("addr: %02x regval: %04x\n", regaddr, regval); + + /* Set up a 3 byte message to send */ + + buffer[0] = regaddr; + buffer[1] = (uint8_t)(regval & 0x00ff); + buffer[2] = (uint8_t)(regval >> 8); + + /* Set up the configuration and perform the write-read operation */ + + config.frequency = priv->frequency; + config.address = priv->addr; + config.addrlen = 7; + + /* Write the register address followed by the data (no RESTART) */ + + return i2c_write(priv->i2c, &config, buffer, 3); +} + +/**************************************************************************** + * Name: bq27426_get_device_id + * + * Description: + * Read the ID register + * + ****************************************************************************/ + +static inline int bq27426_get_device_id(FAR struct bq27426_dev_s *priv, + uint16_t *id) +{ + uint16_t regval = 0; + int ret; + + ret = bq27426_putreg16(priv, BQ27426_COMMAND_CONTROL, + BQ27426_CONTROL_DEVICE_TYPE); + if (ret == OK) + { + ret = bq27426_getreg16(priv, BQ27426_COMMAND_CONTROL, ®val); + *id = regval; + } + + return ret; +} + +/**************************************************************************** + * Name: bq27426_getvoltage + * + * Description: + * Read the VCELL register and scale the returned value + * + ****************************************************************************/ + +static inline int bq27426_getvoltage(FAR struct bq27426_dev_s *priv, + b16_t *voltage) +{ + uint16_t regval = 0; + int ret; + + ret = bq27426_getreg16(priv, BQ27426_COMMAND_VOLTAGE, ®val); + if (ret == OK) + { + *voltage = regval; + } + + return ret; +} + +/**************************************************************************** + * Name: bq27426_getsoc + * + * Description: + * Read the SOC register and scale the returned value + * + ****************************************************************************/ + +static inline int bq27426_getsoc(FAR struct bq27426_dev_s *priv, b16_t *soc) +{ + uint16_t regval = 0; + int ret; + + ret = bq27426_getreg16(priv, BQ27426_COMMAND_SOC, ®val); + if (ret == OK) + *soc = regval; + + return ret; +} + +/**************************************************************************** + * Name: bq27426_getcapacity + * + * Description: + * Read the SOC register and scale the returned value + * + ****************************************************************************/ + +static inline int bq27426_getcapacity(FAR struct bq27426_dev_s *priv, + uint8_t capacity, b16_t *value) +{ + uint16_t regval = 0; + return bq27426_getreg16(priv, capacity, ®val); +} + +/**************************************************************************** + * Name: bq27426_state + * + * Description: + * Return the battery state + * + ****************************************************************************/ + +static int bq27426_state(struct battery_gauge_dev_s *dev, int *status) +{ + FAR struct bq27426_dev_s *priv = (FAR struct bq27426_dev_s *)dev; + b16_t soc = 0; + int ret; + + /* Only a few of the possible battery states are supported by this driver: + * + * BATTERY_UNKNOWN - Returned on error conditions + * BATTERY_IDLE - This is what will usually be reported + * BATTERY_FULL - This will be reported if the SoC is greater than 95% + * BATTERY_CHARGING and BATTERY_DISCHARGING - I don't think this hardware + * knows anything about current (charging or dischargin). + * + */ + + ret = bq27426_getsoc(priv, &soc); + if (ret < 0) + { + *status = BATTERY_UNKNOWN; + return ret; + } + + *status = soc; + + return OK; +} + +/**************************************************************************** + * Name: bq27426_voltage + * + * Description: + * Current battery voltage + * + ****************************************************************************/ + +static int bq27426_voltage(struct battery_gauge_dev_s *dev, b16_t *value) +{ + FAR struct bq27426_dev_s *priv = (FAR struct bq27426_dev_s *)dev; + return bq27426_getvoltage(priv, value); +} + +/**************************************************************************** + * Name: bq27426_capacity_full + * + * Description: + * Battery capacity (mAh) + * + ****************************************************************************/ + +static int bq27426_capacity_full(struct battery_gauge_dev_s *dev, + b16_t *value) +{ + FAR struct bq27426_dev_s *priv = (FAR struct bq27426_dev_s *)dev; + return bq27426_getcapacity(priv, BQ27426_COMMAND_FULL_CAP_FIL, value); +} + +/**************************************************************************** + * Name: bq27426_online + * + * Description: + * Return true if the batter is online + * + ****************************************************************************/ + +static int bq27426_online(struct battery_gauge_dev_s *dev, bool *status) +{ + /* There is no concept of online/offline in this driver */ + + *status = true; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bq27426_initialize + * + * Description: + * Initialize the bq27426 battery driver and return an instance of the + * lower_half interface that may be used with battery_register(); + * + * This driver requires: + * + * CONFIG_BATTERY - Upper half battery driver support + * CONFIG_I2C - I2C support + * CONFIG_BQ27426 - And the driver must be explictly selected. + * + * Input Parameters: + * i2c - An instance of the I2C interface to use to communicate with the bq + * addr - The I2C address of the bq27426 (Better be 0x55). + * frequency - The I2C frequency + * + * Returned Value: + * A pointer to the initializeed lower-half driver instance. A NULL + * pointer is returned on a failure to initialize the bq27426 lower half. + * + ****************************************************************************/ + +FAR struct battery_gauge_dev_s *bq27426_initialize( + FAR struct i2c_master_s *i2c, + uint8_t addr, + uint32_t frequency) +{ + FAR struct bq27426_dev_s *priv; + + /* Initialize the bq27426 device structure */ + + priv = (FAR struct bq27426_dev_s *)kmm_zalloc(sizeof( + struct bq27426_dev_s)); + if (priv) + { + /* Initialize the bq27426 device structure */ + + nxsem_init(&priv->batsem, 0, 1); + priv->ops = &g_bq27426ops; + priv->i2c = i2c; + priv->addr = addr; + priv->frequency = frequency; + } + + return (FAR struct battery_gauge_dev_s *)priv; +} + +#endif /* CONFIG_BATTERY && CONFIG_I2C && CONFIG_BQ27426 */ diff --git a/include/nuttx/power/battery_gauge.h b/include/nuttx/power/battery_gauge.h index 9ea6812462..c7dd644650 100644 --- a/include/nuttx/power/battery_gauge.h +++ b/include/nuttx/power/battery_gauge.h @@ -53,7 +53,9 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* Configuration ************************************************************/ + +/* Configuration */ + /* CONFIG_BATTERY_GAUGE - Upper half battery fuel gauge driver support * * Specific, lower-half drivers will have other configuration requirements @@ -63,7 +65,8 @@ * CONFIG_I2C_MAX1704X - The MAX1704x driver must be explicitly selected. */ -/* IOCTL Commands ***********************************************************/ +/* IOCTL Commands */ + /* The upper-half battery fuel gauge driver provides a character driver * "wrapper" * around the lower-half battery driver that does all of the * real work. @@ -89,6 +92,7 @@ /**************************************************************************** * Public Types ****************************************************************************/ + /* Battery status */ enum battery_gauge_status_e @@ -100,7 +104,7 @@ enum battery_gauge_status_e BATTERY_DISCHARGING /* Probably not full, discharging */ }; - /* This structure defines the lower half battery interface */ +/* This structure defines the lower half battery interface */ struct battery_gauge_dev_s; struct battery_gauge_operations_s @@ -129,7 +133,7 @@ struct battery_gauge_dev_s /* Fields required by the upper-half driver */ FAR const struct battery_gauge_operations_s *ops; /* Battery operations */ - sem_t batsem; /* Enforce mutually exclusive access */ + sem_t batsem; /* Enforce mutually exclusive access */ /* Data fields specific to the lower-half driver may follow */ }; @@ -151,6 +155,7 @@ extern "C" /**************************************************************************** * Public Function Prototypes ****************************************************************************/ + /**************************************************************************** * Name: battery_gauge_register * @@ -171,6 +176,40 @@ extern "C" int battery_gauge_register(FAR const char *devpath, FAR struct battery_gauge_dev_s *dev); +/**************************************************************************** + * Name: bq27426_initialize + * + * Description: + * Initialize the bq2742 battery driver and return an instance of the + * lower_half interface that may be used with battery_gauge_register(); + * + * This driver requires: + * + * CONFIG_BATTERY_GAUGE - Upper half battery fuel gauge driver support + * CONFIG_I2C - I2C support + * CONFIG_BQ27426 - And the driver must be explictly selected. + * + * Input Parameters: + * i2c - An instance of the I2C interface to use to communicate with the + * bq27426 + * addr - The I2C address of the bq27426 (Better be 0x55). + * frequency - The I2C frequency + * + * Returned Value: + * A pointer to the initializeed battery driver instance. A NULL pointer + * is returned on a failure to initialize the bq27426 lower half. + * + ****************************************************************************/ + +#if defined(CONFIG_I2C) && defined(CONFIG_BQ27426) +struct i2c_master_s; /* Forward reference */ + +FAR struct battery_gauge_dev_s *bq27426_initialize( + FAR struct i2c_master_s *i2c, + uint8_t addr, + uint32_t frequency); +#endif + /**************************************************************************** * Name: max1704x_initialize * @@ -187,7 +226,8 @@ int battery_gauge_register(FAR const char *devpath, * chip is on the board in order to scale the voltage correctly. * * Input Parameters: - * i2c - An instance of the I2C interface to use to communicate with the MAX1704x + * i2c - An instance of the I2C interface to use to communicate with + * the MAX1704x. * addr - The I2C address of the MAX1704x (Better be 0x36). * frequency - The I2C frequency * @@ -200,9 +240,10 @@ int battery_gauge_register(FAR const char *devpath, #if defined(CONFIG_I2C) && defined(CONFIG_I2C_MAX1704X) struct i2c_master_s; /* Forward reference */ -FAR struct battery_gauge_dev_s *max1704x_initialize(FAR struct i2c_master_s *i2c, - uint8_t addr, - uint32_t frequency); +FAR struct battery_gauge_dev_s *max1704x_initialize( + FAR struct i2c_master_s *i2c, + uint8_t addr, + uint32_t frequency); #endif #undef EXTERN diff --git a/include/nuttx/power/bq27426.h b/include/nuttx/power/bq27426.h new file mode 100644 index 0000000000..c02e1a8af0 --- /dev/null +++ b/include/nuttx/power/bq27426.h @@ -0,0 +1,210 @@ +/**************************************************************************** + * include/nuttx/power/bq27426.h + * Lower half driver for bq27426 battery fuel gauge. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +#ifndef __DRIVERS_POWER_BQ27426_H +#define __DRIVERS_POWER_BQ27426_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Auxiliary Definitions */ + +#define BQ27426_I2C_TIMEOUT 2000 + +#define BQ27426_I2C_ADDRESS 0x55 /* Default I2C address of the BQ27426 */ + +/* General Constants */ + +#define BQ27426_UNSEAL_KEY 0x8000 /* Secret code to unseal the BQ27426 */ +#define BQ27426_DEVICE_ID 0x0426 /* Default device ID */ + +/* Standard Commands */ + +/* The fuel gauge uses a series of 2-byte standard commands to enable system + * reading and writing of battery information. Each command has an associated + * sequential command-code pair. + */ + +#define BQ27426_COMMAND_CONTROL 0x00 /* Control() */ +#define BQ27426_COMMAND_TEMP 0x02 /* Temperature() */ +#define BQ27426_COMMAND_VOLTAGE 0x04 /* Voltage() */ +#define BQ27426_COMMAND_FLAGS 0x06 /* Flags() */ +#define BQ27426_COMMAND_NOM_CAPACITY 0x08 /* NominalAvailableCapacity() */ +#define BQ27426_COMMAND_AVAIL_CAPACITY 0x0A /* FullAvailableCapacity() */ +#define BQ27426_COMMAND_REM_CAPACITY 0x0C /* RemainingCapacity() */ +#define BQ27426_COMMAND_FULL_CAPACITY 0x0E /* FullChargeCapacity() */ +#define BQ27426_COMMAND_AVG_CURRENT 0x10 /* AverageCurrent() */ +#define BQ27426_COMMAND_AVG_POWER 0x18 /* AveragePower() */ +#define BQ27426_COMMAND_SOC 0x1C /* StateOfCharge() */ +#define BQ27426_COMMAND_INT_TEMP 0x1E /* InternalTemperature() */ +#define BQ27426_COMMAND_SOH 0x20 /* StateOfHealth() */ +#define BQ27426_COMMAND_REM_CAP_UNFL 0x28 /* RemainingCapacityUnfiltered() */ +#define BQ27426_COMMAND_REM_CAP_FIL 0x2A /* RemainingCapacityFiltered() */ +#define BQ27426_COMMAND_FULL_CAP_UNFL 0x2C /* FullChargeCapacityUnfiltered() */ +#define BQ27426_COMMAND_FULL_CAP_FIL 0x2E /* FullChargeCapacityFiltered() */ +#define BQ27426_COMMAND_SOC_UNFL 0x30 /* StateOfChargeUnfiltered() */ + +/* Control Sub-commands */ + +/* Issuing a Control() command requires a subsequent 2-byte subcommand. These + * additional bytes specify the particular control function desired. The + * Control() command allows the system to control specific features of the + * fuel gauge during normal operation and additional features when the + * device is in different access modes. + */ + +#define BQ27426_CONTROL_STATUS 0x0000 +#define BQ27426_CONTROL_DEVICE_TYPE 0x0001 +#define BQ27426_CONTROL_FW_VERSION 0x0002 +#define BQ27426_CONTROL_DM_CODE 0x0004 +#define BQ27426_CONTROL_PREV_MACWRITE 0x0007 +#define BQ27426_CONTROL_CHEM_ID 0x0008 +#define BQ27426_CONTROL_BAT_INSERT 0x000C +#define BQ27426_CONTROL_BAT_REMOVE 0x000D +#define BQ27426_CONTROL_SET_CFGUPDATE 0x0013 +#define BQ27426_CONTROL_SMOOTH_SYNC 0x0019 +#define BQ27426_CONTROL_SHUTDOWN_ENABLE 0x001B +#define BQ27426_CONTROL_SHUTDOWN 0x001C +#define BQ27426_CONTROL_SEALED 0x0020 +#define BQ27426_CONTROL_PULSE_SOC_INT 0x0023 +#define BQ27426_CONTROL_CHEM_A 0x0030 +#define BQ27426_CONTROL_CHEM_B 0x0031 +#define BQ27426_CONTROL_CHEM_C 0x0032 +#define BQ27426_CONTROL_RESET 0x0041 +#define BQ27426_CONTROL_SOFT_RESET 0x0042 + +/* Control Status Word - Bit Definitions */ + +/* Bit positions for the 16-bit data of CONTROL_STATUS. + * CONTROL_STATUS instructs the fuel gauge to return status + * information to Control() addresses 0x00 and 0x01. + * The read-only status word contains status bits that are + * set or cleared either automatically as conditions warrant or + * through using specified subcommands. + */ + +#define BQ27426_STATUS_SHUTDOWNEN (1 << 15) +#define BQ27426_STATUS_WDRESET (1 << 14) +#define BQ27426_STATUS_SS (1 << 13) +#define BQ27426_STATUS_CALMODE (1 << 12) +#define BQ27426_STATUS_CCA (1 << 11) +#define BQ27426_STATUS_BCA (1 << 10) +#define BQ27426_STATUS_QMAX_UP (1 << 9) +#define BQ27426_STATUS_RES_UP (1 << 8) +#define BQ27426_STATUS_INITCOMP (1 << 7) +#define BQ27426_STATUS_SLEEP (1 << 4) +#define BQ27426_STATUS_LDMD (1 << 3) +#define BQ27426_STATUS_RUP_DIS (1 << 2) +#define BQ27426_STATUS_VOK (1 << 1) +#define BQ27426_STATUS_CHEMCHANGE (1 << 0) + +/* Flag Command - Bit Definitions */ + +/* Bit positions for the 16-bit data of Flags() + * This read-word function returns the contents of the fuel gauging status + * register, depicting the current operating status. + */ + +#define BQ27426_FLAG_OT (1 << 15) +#define BQ27426_FLAG_UT (1 << 14) +#define BQ27426_FLAG_FC (1 << 9) +#define BQ27426_FLAG_CHG (1 << 8) +#define BQ27426_FLAG_OCVTAKEN (1 << 7) +#define BQ27426_FLAG_DODCORRECT (1 << 6) +#define BQ27426_FLAG_ITPOR (1 << 5) +#define BQ27426_FLAG_CFGUPMODE (1 << 4) +#define BQ27426_FLAG_BAT_DET (1 << 3) +#define BQ27426_FLAG_SOC1 (1 << 2) +#define BQ27426_FLAG_SOCF (1 << 1) +#define BQ27426_FLAG_DSG (1 << 0) + +/* Extended Data Commands */ + +/* Extended data commands offer additional functionality beyond + * the standard set of commands. + * They are used in the same manner; however, unlike standard + * scommands, extended commands are not limited to 2-byte words. + */ + +#define BQ27426_EXTENDED_OPCONFIG 0x3A /* OpConfig()*/ +#define BQ27426_EXTENDED_CAPACITY 0x3C /* DesignCapacity()*/ +#define BQ27426_EXTENDED_DATACLASS 0x3E /* DataClass()*/ +#define BQ27426_EXTENDED_DATABLOCK 0x3F /* DataBlock()*/ +#define BQ27426_EXTENDED_BLOCKDATA 0x40 /* BlockData()*/ +#define BQ27426_EXTENDED_CHECKSUM 0x60 /* BlockDataCheckSum()*/ +#define BQ27426_EXTENDED_CONTROL 0x61 /* BlockDataControl()*/ + +/* Configuration Class, Subclass ID's */ + +/* To access a subclass of the extended data, set the DataClass() + * function with one of these values. + */ + +/* Configuration Classes */ + +#define BQ27426_ID_SAFETY 2 /* Safety */ +#define BQ27426_ID_CHG_TERMINATION 6 /* Charge Termination */ +#define BQ27426_ID_CONFIG_DATA 48 /* Data */ +#define BQ27426_ID_DISCHARGE 49 /* Discharge */ +#define BQ27426_ID_REGISTERS 64 /* Registers */ +#define BQ27426_ID_POWER 68 /* Power */ + +/* Gas Gauging Classes */ + +#define BQ27426_ID_IT_CFG 80 /* IT Cfg */ +#define BQ27426_ID_CURRENT_THRESH 81 /* Current Thresholds */ +#define BQ27426_ID_STATE 82 /* State */ + +/* Ra Tables Classes */ + +#define BQ27426_ID_R_A_RAM 89 /* R_a RAM */ + +/* Calibration Classes */ + +#define BQ27426_ID_CALIB_DATA 104 /* Data */ +#define BQ27426_ID_CC_CAL 105 /* CC Cal */ +#define BQ27426_ID_CURRENT 107 /* Current */ + +/* Security Classes */ + +#define BQ27426_ID_CODES 112 /* Codes */ + +/* OpConfig Register - Bit Definitions */ + +/* Bit positions of the OpConfig Register */ + +#define BQ27426_OPCONFIG_BIE (1 << 13) +#define BQ27426_OPCONFIG_GPIOPOL (1 << 11) +#define BQ27426_OPCONFIG_RESFACTSTEP (1 << 6) +#define BQ27426_OPCONFIG_SLEEP (1 << 5) +#define BQ27426_OPCONFIG_RMFCC (1 << 4) +#define BQ27426_OPCONFIG_FASTCONVEN (1 << 3) +#define BQ27426_OPCONFIG_BATLOWEN (1 << 2) +#define BQ27426_OPCONFIG_TEMPS (1 << 0) + +#define BQ27426_ACCESS_SUB_CLASS_80 0x50 +#define BQ27426_ACCESS_SUB_CLASS_81 0x51 +#define BQ27426_ACCESS_SUB_CLASS_82 0x52 +#define BQ27426_ACCESS_SUB_CLASS_89 0x59 +#define BQ27426_ACCESS_SUB_CLASS_64 0x40 + +#endif /* __DRIVERS_POWER_BQ27426_H */