diff --git a/arch/arm/src/cxd56xx/Kconfig b/arch/arm/src/cxd56xx/Kconfig index 487e7faaaf..e63b13bf7c 100644 --- a/arch/arm/src/cxd56xx/Kconfig +++ b/arch/arm/src/cxd56xx/Kconfig @@ -62,6 +62,7 @@ config CXD56_FARAPI_VERSION_CHECK if CXD56_FARAPI_VERSION_CHECK + config CXD56_FARAPI_VERSION_FAILED_PANIC bool "Far API Version Check Failed to PANIC" default n @@ -74,8 +75,35 @@ config CXD56_FARAPI_DEBUG endmenu # Far API Configuration +comment "Timer Options" + +menuconfig CXD56_RTC + bool "Real Time Clock (RTC)" + default y + ---help--- + Support RTC + +if CXD56_RTC + +config CXD56_RTC_LATEINIT + bool "Late RTC initialization" + default y + ---help--- + Enable the late RTC initialization after waiting until the external + CXD5247 RTC clock is stable. It will take 2 seconds typically at the + initial boot by power on reset. + +endif # CXD56_RTC + menu "CXD56xx Peripheral Support" +config CXD56_DMAC + bool "DMAC" + default y + ---help--- + Enables DMAC + Currently supports SPI4 TX/RX and SPI5 TX/RX + config CXD56_GPIO_IRQ bool "GPIO interrupt" default y @@ -98,6 +126,171 @@ config CXD56_UART2 ---help--- UART interface with hardware flow control in the application subsystem. +config CXD56_SPI + bool "SPI" + +if CXD56_SPI + +config CXD56_SPI0 + bool "SPI0" + +menuconfig CXD56_SPI3 + bool "SPI3" + +if CXD56_SPI3 + +config CXD56_SPI3_SCUSEQ + bool "SCU Sequencer" + default y + depends on CXD56_SCU + ---help--- + Use the sensor control unit (SCU) sequencer. + +config CXD56_SPI3_CS0 + bool "SPI3 Chip Select 0" + default y + ---help--- + Enable chip select 0 of SPI3 + +config CXD56_SPI3_CS1 + bool "SPI3 Chip Select 1" + default n + ---help--- + Enable chip select 1 of SPI3 + +config CXD56_SPI3_CS2 + bool "SPI3 Chip Select 2" + default n + ---help--- + Enable chip select 2 of SPI3 + +endif # CXD56_SPI3 + +menuconfig CXD56_SPI4 + bool "SPI4" + +if CXD56_SPI4 + +config CXD56_DMAC_SPI4_TX + bool "DMAC support for SPI4 TX" + default n + select CXD56_DMAC + ---help--- + Enables DMAC for SPI4 TX + +if CXD56_DMAC_SPI4_TX + +config CXD56_DMAC_SPI4_TX_CH + int "TX channel" + default 2 + range 2 6 + +config CXD56_DMAC_SPI4_TX_MAXSIZE + int "Max size to be sent in bytes" + default 192000 + range 1 1572864 + ---help--- + This value should be same as RX. + +endif # CXD56_DMAC_SPI4_TX + +config CXD56_DMAC_SPI4_RX + bool "DMAC support for SPI4 RX" + default n + select CXD56_DMAC + + ---help--- + Enables DMAC for SPI4 RX + +if CXD56_DMAC_SPI4_RX + +config CXD56_DMAC_SPI4_RX_CH + int "RX channel" + default 3 + range 2 6 + +config CXD56_DMAC_SPI4_RX_MAXSIZE + int "Max size to be received in bytes" + default 192000 + range 1 1572864 + ---help--- + This value should be same as TX. + +endif # CXD56_DMAC_SPI4_RX + +endif # CXD56_SPI4 + +menuconfig CXD56_SPI5 + bool "SPI5" + +if CXD56_SPI5 + +choice + prompt "SPI5 pin configuration" + default CXD56_SPI5_PINMAP_EMMC + +config CXD56_SPI5_PINMAP_EMMC + bool "SPI5 pin assign to eMMC" + ---help--- + SPI5 assigns to the shared pins with eMMC. + +config CXD56_SPI5_PINMAP_SDIO + bool "SPI5 pin assign to SDIO" + ---help--- + SPI5 assigns to the shared pins with SDIO. +endchoice + +config CXD56_DMAC_SPI5_TX + bool "DMAC support for SPI5 TX" + default n + select CXD56_DMAC + ---help--- + Enables DMAC for SPI5 TX + +if CXD56_DMAC_SPI5_TX + +config CXD56_DMAC_SPI5_TX_CH + int "TX channel" + default 4 + range 2 6 + +config CXD56_DMAC_SPI5_TX_MAXSIZE + int "Max size to be sent in bytes" + default 1516 + range 1 1572864 + ---help--- + This value should be same as RX. + +endif # CXD56_DMAC_SPI5_TX + +config CXD56_DMAC_SPI5_RX + bool "DMAC support for SPI5 RX" + default n + select CXD56_DMAC + + ---help--- + Enables DMAC for SPI5 RX + +if CXD56_DMAC_SPI5_RX + +config CXD56_DMAC_SPI5_RX_CH + int "RX channel" + default 5 + range 2 6 + +config CXD56_DMAC_SPI5_RX_MAXSIZE + int "Max size to be received in bytes" + default 1516 + range 1 1572864 + ---help--- + This value should be same as TX. + +endif # CXD56_DMAC_SPI5_RX + +endif # CXD56_SPI5 + +endif + config CXD56_USBDEV bool "USB" default n diff --git a/arch/arm/src/cxd56xx/Make.defs b/arch/arm/src/cxd56xx/Make.defs index ead46aa974..27a6a51ce8 100644 --- a/arch/arm/src/cxd56xx/Make.defs +++ b/arch/arm/src/cxd56xx/Make.defs @@ -99,6 +99,13 @@ CHIP_CSRCS += cxd56_powermgr.c CHIP_CSRCS += cxd56_farapi.c CHIP_CSRCS += cxd56_sysctl.c +ifeq ($(CONFIG_CXD56_RTC),y) +CHIP_CSRCS += cxd56_rtc.c +ifeq ($(CONFIG_RTC_DRIVER),y) +CHIP_CSRCS += cxd56_rtc_lowerhalf.c +endif +endif + ifeq ($(CONFIG_CXD56_GPIO_IRQ),y) CHIP_CSRCS += cxd56_gpioint.c endif @@ -110,3 +117,11 @@ endif ifeq ($(CONFIG_CXD56_SDIO),y) CHIP_CSRCS += cxd56_sdhci.c endif + +ifeq ($(CONFIG_CXD56_SPI),y) +CHIP_CSRCS += cxd56_spi.c +endif + +ifeq ($(CONFIG_CXD56_DMAC),y) +CHIP_CSRCS += cxd56_dmac.c +endif diff --git a/arch/arm/src/cxd56xx/cxd56_dmac.c b/arch/arm/src/cxd56xx/cxd56_dmac.c new file mode 100644 index 0000000000..851065822d --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_dmac.c @@ -0,0 +1,980 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_dmac.c + * + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sony Semiconductor Solutions Corporation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cxd56_dmac.h" + +#define PM_APP_ADMAC 51 +#define PM_APP_SKDMAC 52 +#define PM_APP_IDMAC 54 + +#define DMAC1_REG_BASE 0x4e020000u /* SMP_DMAC */ +#define DMAC2_REG_BASE 0x4e021000u /* SMP_SAKE */ +#define DMAC3_REG_BASE 0x4e102000u /* IMG_DMAC */ + +#define is_dmac(n, dev) ((dev) == (struct dmac_register_map *)\ + DMAC ## n ## _REG_BASE) + +#define NCHANNELS 9 + +#define __RO volatile const +#define __WO volatile +#define __RW volatile + +struct dmac_ch_register_map { + __RW uint32_t srcaddr; + __RW uint32_t destaddr; + __RW uint32_t lli; + __RW uint32_t control; + __RW uint32_t configuration; + uint32_t reserved[3]; +}; + +struct dmac080_ch_register_map { + __RW uint32_t srcaddr; + __RW uint32_t destaddr; + __RW uint32_t lli; + __RW uint32_t control; + __RW uint32_t configuration; + __RW uint32_t deflli; + uint32_t reserved[2]; +}; + +struct dmac_register_map { + __RO uint32_t intstatus; + __RO uint32_t inttcstatus; + __WO uint32_t inttcclear; + __RO uint32_t interrorstatus; + __WO uint32_t interrorclear; + __RO uint32_t rawinttcstatus; + __RO uint32_t rawinterrorstatus; + __RO uint32_t enbldchns; + __RW uint32_t softbreq; + __RW uint32_t softsreq; + __RW uint32_t softlbreq; + __RW uint32_t softlsreq; + __RW uint32_t configuration; + __RW uint32_t sync; + + uint32_t reserved0[50]; + + struct dmac_ch_register_map channel[2]; +}; + +struct dmac080_register_map { + __RO uint32_t intstatus; + __RO uint32_t inttcstatus; + __WO uint32_t inttcclear; + __RO uint32_t interrorstatus; + __WO uint32_t interrorclear; + __RO uint32_t rawinttcstatus; + __RO uint32_t rawinterrorstatus; + __RO uint32_t enbldchns; + __RW uint32_t softbreq; + __RW uint32_t softsreq; + __RW uint32_t softlbreq; + __RW uint32_t softlsreq; + __RW uint32_t configuration; + __RW uint32_t sync; + __RW uint32_t sreqmask; + + uint32_t reserved0[49]; + + /* XXX: deflli not supported */ + + struct dmac_ch_register_map channel[5]; +}; + +#define DMAC_CH_ENABLE (1u<<0) +#define DMAC_CH_HALT (1u<<18) +#define DMAC_CH_ACTIVE (1u<<17) + +#ifndef itemsof +#define itemsof(a) (sizeof(a)/sizeof(a[0])) +#endif + +/** + * Link list item structure for use scatter/gather operation + */ + +typedef struct { + uint32_t src_addr; /**< Source address */ + uint32_t dest_addr; /**< Destination address */ + uint32_t nextlli; /**< Next link list */ + uint32_t control; /**< Transfer control */ +} dmac_lli_t; + +#define CXD56_DMAC_M2M 0 /**< Memory to memory */ +#define CXD56_DMAC_M2P 1 /**< Memory to peripheral, DMAC controlled */ +#define CXD56_DMAC_P2M 2 /**< Peripheral to memory, DMAC controlled */ +#define CXD56_DMAC_P2P 3 /**< Peripheral to peripheral */ +#define CXD56_DMAC_P2CP 4 /**< P2P destination controlled */ +#define CXD56_DMAC_M2CP 5 /**< M2P peripheral controlled */ +#define CXD56_DMAC_CP2M 6 /**< P2M peripheral controlled */ +#define CXD56_DMAC_CP2P 7 /**< P2P source controlled */ + +#define CXD56_DMAC_BSIZE1 0 /**< 1 burst */ +#define CXD56_DMAC_BSIZE4 1 /**< 4 burst */ +#define CXD56_DMAC_BSIZE8 2 /**< 8 burst */ +#define CXD56_DMAC_BSIZE16 3 /**< 16 burst */ +#define CXD56_DMAC_BSIZE32 4 /**< 32 burst */ +#define CXD56_DMAC_BSIZE64 5 /**< 64 burst */ +#define CXD56_DMAC_BSIZE128 6 /**< 128 burst */ +#define CXD56_DMAC_BSIZE256 7 /**< 256 burst */ + +#define CXD56_DMAC_LITTLE_ENDIAN 0 /**< Little endian */ +#define CXD56_DMAC_BIG_ENDIAN 1 /**< Bit endian */ + +#define CXD56_DMAC_MASTER1 0 /**< AHB master 1 */ +#define CXD56_DMAC_MASTER2 1 /**< AHB master 2 */ + +/* max transfer size at a time */ + +#define CXD56_DMAC_MAX_SIZE 0xfff + +/** + * Helper macro for construct transfer control parameter. + * Each parameters are the same with PD_DmacSetControl(). + * + * @par Example: + * Here is an example for transfer setting with no interrupt, + * address increments, 4 byte, 4 burst and 16380 bytes (4 x 4095). + * + * @code + * list.control = PD_DmacCtrlHelper(0, 1, 1, + * PD_DMAC_WIDTH32, PD_DMAC_WIDTH32, + * PD_DMAC_BSIZE4, PD_DMAC_BSIZE4, + * 0xfffu); + * @endcode + */ + +#define DMAC_CTRL_HELPER(intr, di, si, dwidth, swidth, dbsize, sbsize, tsize) \ + (((intr) & 1u) << 31 | \ + ((di) & 1u) << 27 | \ + ((si) & 1u) << 26 | \ + ((dwidth) & 7u) << 21 | \ + ((swidth) & 7u) << 18 | \ + ((dbsize) & 7u) << 15 | \ + ((sbsize) & 7u) << 12 | \ + ((tsize) & 0xfffu)) + +/** + * Helper macro for construct transfer control parameter + * (for APP DMAC channel 2 - 6). + * Each parameters are the same with PD_DmacSetExControl(). + * + * @par Example: + * Here is an example for transfer setting with no interrupt, + * address increments, 4 byte, 4 burst and 16380 bytes (4 x 4095). + * + * @code + * list.control = PD_DmacExCtrlHelper(0, 1, 1, 0, 0, + * PD_DMAC_WIDTH32, PD_DMAC_WIDTH32, + * PD_DMAC_BSIZE4, PD_DMAC_BSIZE4, + * 0xfffu); + * @endcode + * + * @note If you want to different burst sizes to source and destination, + * then data may remained in FIFO. In this case, DMAC cannot clear them. + * Do not use this configuration to transferring unknown size data (especially + * communication peripherals). I recommend the same setting to burst sizes. + */ + +#define DMAC_EX_CTRL_HELPER(\ + intr, di, si, dmaster, smaster, dwidth, swidth, dbsize, sbsize, tsize) \ + (((intr) & 1u) << 31 | \ + ((di) & 1u) << 30 | \ + ((si) & 1u) << 29 | \ + ((dmaster) & 1u) << 28 | \ + ((smaster) & 1u) << 27 | \ + ((dwidth) & 3u) << 25 | \ + ((swidth) & 3u) << 23 | \ + ((dbsize) & 3u) << 21 | \ + ((sbsize) & 3u) << 19 | \ + ((tsize) & 0x7ffffu)) + +static int open_channels = 0; + +static int intr_handler_admac0(int irq, FAR void *context, FAR void *arg); +static int intr_handler_admac1(int irq, FAR void *context, FAR void *arg); +static int intr_handler_idmac(int irq, FAR void *context, FAR void *arg); +static int intr_handler_skdmac0(int irq, FAR void *context, FAR void *arg); +static int intr_handler_skdmac1(int irq, FAR void *context, FAR void *arg); +static uint32_t irq_map[] = { + CXD56_IRQ_APP_DMAC0, + CXD56_IRQ_APP_DMAC1, + CXD56_IRQ_IDMAC, + CXD56_IRQ_IDMAC, + CXD56_IRQ_IDMAC, + CXD56_IRQ_IDMAC, + CXD56_IRQ_IDMAC, + CXD56_IRQ_SKDMAC_0, + CXD56_IRQ_SKDMAC_1, +}; + +static int (*intc_handler[])(int irq, FAR void *context, FAR void *arg) = { + intr_handler_admac0, + intr_handler_admac1, + intr_handler_idmac, + intr_handler_idmac, + intr_handler_idmac, + intr_handler_idmac, + intr_handler_idmac, + intr_handler_skdmac0, + intr_handler_skdmac1, +}; + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This structure describes one DMA channel */ + +struct dma_channel_s +{ + uint8_t chan; /* DMA channel number (0-CXD56_DMA_NCHANNELS) */ + bool inuse; /* TRUE: The DMA channel is in use */ + dma_config_t config; /* Current configuration */ + dmac_lli_t * list; /* Link list */ + dma_callback_t callback; /* Callback invoked when the DMA completes */ + void *arg; /* Argument passed to callback function */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the array of all DMA channels */ + +static struct dma_channel_s g_dmach[NCHANNELS]; +static sem_t g_dmaexc; + +static int dma_init(int ch); +static int dma_uninit(int ch); +static int dma_open(int ch); +static int dma_close(int ch); +static int dma_setconfig(int ch, int itc, int ierr, int flowctrl, + int destperi, int srcperi); +static int dma_setintrcallback(int ch, dma_callback_t func, void *data); +static int dma_clearintrcallback(int ch); +static int dma_start(int ch, dmac_lli_t *list); +static int dma_stop(int ch); + +static int ch2dmac(int ch) +{ + switch (ch) { + case 0: case 1: + return 1; + case 2: case 3: case 4: case 5: case 6: /* APP IDMAC */ + return 3; + case 7: case 8: /* APP SKDMAC */ + return 2; + default: + return 0; + } +} + +static struct dmac_register_map *get_device(int ch) +{ + int id = ch2dmac(ch); + + switch (id) { + case 1: return (struct dmac_register_map *)DMAC1_REG_BASE; + case 2: return (struct dmac_register_map *)DMAC2_REG_BASE; + case 3: return (struct dmac_register_map *)DMAC3_REG_BASE; + } + return NULL; +} + +static struct dmac_ch_register_map *get_channel(int ch) +{ + struct dmac_register_map *dev = get_device(ch); + if (dev == NULL) + return NULL; + + if (is_dmac(2, dev)) { + return &dev->channel[ch - 7]; + } + else if (is_dmac(3, dev)) { + return &((struct dmac080_register_map *)dev)->channel[ch - 2]; + } + + return &dev->channel[ch & 1]; +} + +static int get_pmid(int ch) +{ + switch (ch) { + case 0: case 1: + return PM_APP_ADMAC; + case 2: case 3: case 4: case 5: case 6: + return PM_APP_IDMAC; + case 7: case 8: + return PM_APP_SKDMAC; + default: + break; /* may not comes here */ + } + return 0; +} + +struct dmac_ch_register_frame { + uint32_t srcaddr; + uint32_t destaddr; + uint32_t lli; + uint32_t control; + uint32_t configuration; +}; + +struct dmac_register_frame { + uint32_t configuration; + struct dmac_ch_register_frame channel[2]; +}; + +static void _dmac_intc_handler(int ch) +{ + struct dmac_register_map *dev = get_device(ch); + struct dma_channel_s *dmach; + uint32_t mask; + int itc; + int err; + + mask = (1u << (ch & 1)); + + if (is_dmac(2, dev)) { + mask = 1u << (ch - 7); + } + else if (is_dmac(3, dev)) { + mask = 1u << (ch - 2); + } + + itc = dev->inttcstatus & mask; + err = dev->interrorstatus & mask; + dev->inttcclear = itc; + dev->interrorclear = err; + + dmach = &g_dmach[ch]; + + if (dmach->callback) { + int flags = itc ? CXD56_DMA_INTR_ITC : 0; + flags |= err ? CXD56_DMA_INTR_ERR : 0; + dmach->callback((DMA_HANDLE)dmach, flags, dmach->arg); + } +} + +static int intr_handler_admac0(int irq, FAR void *context, FAR void *arg) +{ + _dmac_intc_handler(0); + return OK; +} + +static int intr_handler_admac1(int irq, FAR void *context, FAR void *arg) +{ + _dmac_intc_handler(1); + return OK; +} + +static int intr_handler_idmac(int irq, FAR void *context, FAR void *arg) +{ + struct dmac_register_map *dev = get_device(2); /* XXX */ + uint32_t stat = dev->intstatus & 0x1f; + int i; + + for (i = 2; stat; i++, stat >>= 1) { + if (stat & 1) + _dmac_intc_handler(i); + } + + return OK; +} + +static int intr_handler_skdmac0(int irq, FAR void *context, FAR void *arg) +{ + _dmac_intc_handler(7); + return OK; +} + +static int intr_handler_skdmac1(int irq, FAR void *context, FAR void *arg) +{ + _dmac_intc_handler(8); + return OK; +} + +static void controller_power_on(int ch) +{ + int id = get_pmid(ch); + + if (id == PM_APP_SKDMAC) + return; + +/* TODO power on */ +} + +static void controller_power_off(int ch) +{ + int id = get_pmid(ch); + + if (id == PM_APP_SKDMAC) /* Do not disable SKDMAC, leave it to SAKE driver. */ + return; + +/* TODO power off */ +} + +int dma_init(int ch) +{ + int id = ch2dmac(ch); + + if (!id) + return -ENODEV; + + controller_power_on(ch); + + irq_attach(irq_map[ch], intc_handler[ch], NULL); + + return 0; +} + +int dma_uninit(int ch) +{ + int id = ch2dmac(ch); + + if (!id) + return -ENODEV; + + controller_power_off(ch); + + return 0; +} + +int dma_open(int ch) +{ + struct dmac_register_map *dmac = get_device(ch); + irqstate_t flags; + + if (dmac == NULL) + return -ENODEV; + + flags = enter_critical_section(); + + if (open_channels & (1u << ch)) { + leave_critical_section(flags); + return -EBUSY; + } + open_channels |= 1u << ch; + + leave_critical_section(flags); + + g_dmach[ch].callback = NULL; + g_dmach[ch].arg = NULL; + + dmac->sync = 0; + dmac->configuration |= 1; + + return 0; +} + +static int dma_close(int ch) +{ + struct dmac_register_map *dmac = get_device(ch); + uint32_t enabled; + irqstate_t flags; + uint32_t chmask; + int shift; + + if (dmac == NULL) + return -ENODEV; + + shift = ch & 1; + + if (is_dmac(2, dmac)) { + shift = ch - 7; + } + else if (is_dmac(3, dmac)) { + shift = ch - 2; + } + + enabled = dmac->enbldchns; + if (enabled & (1 << shift)) + return -EBUSY; + + dma_clearintrcallback(ch); + + flags = enter_critical_section(); + + chmask = (3u << (ch & ~1)); + + if (is_dmac(2, dmac)) { + chmask = 0x3u << 7; + } + else if (is_dmac(3, dmac)) { + chmask = 0x1fu << 2; + } + + open_channels &= ~(1u << ch); + + /* Stop device if both of channels are already closed */ + + if (!(open_channels & chmask)) + dmac->configuration &= ~1; + + leave_critical_section(flags); + + return 0; +} + +static int dma_setconfig(int ch, int itc, int ierr, int flowctrl, + int destperi, int srcperi) +{ + struct dmac_ch_register_map *channel = get_channel(ch); + if (channel == NULL) + return -ENODEV; + + channel->configuration = (itc & 1) << 15 | + (ierr & 1) << 14 | + (flowctrl & 7) << 11 | + + /* Burst enable */ + + 1 << 25 | 1 << 24 | + + (destperi & 0xf) << 6 | + (srcperi & 0xf) << 1; + + return 0; +} + +static int dma_setintrcallback(int ch, dma_callback_t func, void *data) +{ + if (ch >= NCHANNELS) + return -ENODEV; + + g_dmach[ch].callback = func; + g_dmach[ch].arg = data; + + up_enable_irq(irq_map[ch]); + + return 0; +} + +static int dma_clearintrcallback(int ch) +{ + if (ch >= NCHANNELS) + return -ENODEV; + + g_dmach[ch].callback = NULL; + g_dmach[ch].arg = NULL; + + up_disable_irq(irq_map[ch]); + + return 0; +} + +static int dma_start(int ch, dmac_lli_t *list) +{ + struct dmac_ch_register_map *channel = get_channel(ch); + if (channel == NULL) + return -ENODEV; + + if (list) { + channel->srcaddr = list->src_addr; + channel->destaddr = list->dest_addr; + channel->lli = list->nextlli; + channel->control = list->control; + } + + channel->configuration |= DMAC_CH_ENABLE; + + return 0; +} + +static int dma_stop(int ch) +{ + struct dmac_ch_register_map *channel = get_channel(ch); + if (channel == NULL) + return -ENODEV; + + if (!(channel->configuration & DMAC_CH_ENABLE)) + return 0; /* already stopped */ + + /* Set HALT and poll Active bit for FIFO is cleaned */ + + channel->configuration |= DMAC_CH_HALT; + + (void) channel->lli; + (void) channel->lli; + + while (channel->configuration & DMAC_CH_ACTIVE); + + channel->configuration &= ~(DMAC_CH_HALT | DMAC_CH_ENABLE); + + return 0; +} + +/**************************************************************************** + * Name: cxd56_dmainitialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dmainitialize(void) +{ + int i; + + dmainfo("Initialize DMAC\n"); + + /* Initialize the channel list */ + + for (i = 0; i < NCHANNELS; i++) + { + g_dmach[i].chan = i; + } + + sem_init(&g_dmaexc, 0, 1); +} + +/**************************************************************************** + * Name: cxd56_dmachannel + * + * Description: + * Allocate a DMA channel. This function gives the caller mutually exclusive + * access to a DMA channel. + * + * If no DMA channel is available, then cxd56_dmachannel() will wait until + * the holder of a channel relinquishes the channel by calling + * cxd56_dmafree(). + * + * Input parameters: + * ch - DMA channel to use + * maxsize - Max size to be transfered in bytes + * + * Returned Value: + * This function ALWAYS returns a non-NULL, void* DMA channel handle. + * + * Assumptions: + * - The caller can wait for a DMA channel to be freed if it is not + * available. + * + ****************************************************************************/ + +DMA_HANDLE cxd56_dmachannel(int ch, ssize_t maxsize) +{ + struct dma_channel_s *dmach; + int n; + + /* Get exclusive access to allocate channel */ + + sem_wait(&g_dmaexc); + + if (ch < 0 || ch >= NCHANNELS) + { + dmaerr("Invalid channel number %d.\n", ch); + goto err; + } + dmach = &g_dmach[ch]; + + if (maxsize == 0) + { + dmaerr("Invalid max size: %d\n", maxsize); + goto err; + } + + if (dmach->inuse) + { + dmaerr("Channel already in use.\n"); + goto err; + } + + dmainfo("DMA channel %d\n", dmach->chan); + + n = maxsize / CXD56_DMAC_MAX_SIZE; + if ((maxsize % CXD56_DMAC_MAX_SIZE) != 0) + { + n++; + } + + dmach->list = (dmac_lli_t *)kmm_malloc(n * sizeof(dmac_lli_t)); + if (dmach->list == NULL) + { + dmainfo("Failed to malloc\n"); + goto err; + } + + /* Initialize hardware */ + + dma_init(dmach->chan); + dma_open(dmach->chan); + + dmach->inuse = true; + + sem_post(&g_dmaexc); + + return (DMA_HANDLE)dmach; + +err: + sem_post(&g_dmaexc); + return NULL; +} + +/**************************************************************************** + * Name: cxd56_dmafree + * + * Description: + * Release a DMA channel. If another thread is waiting for this DMA channel + * in a call to cxd56_dmachannel, then this function will re-assign the + * DMA channel to that thread and wake it up. + * + * NOTE: The 'handle' used in this argument must NEVER be used again until + * cxd56_dmachannel() is called again to re-gain access to the channel. + * + * Returned Value: + * None + * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * + ****************************************************************************/ + +void cxd56_dmafree(DMA_HANDLE handle) +{ + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + + if (dmach == NULL) + { + dmaerr("Invalid handle.\n"); + return; + } + + sem_wait(&g_dmaexc); + + if (!dmach->inuse) + { + dmaerr("Channel %d already freed.\n", dmach->chan); + goto err; + } + + dmainfo("free DMA channel %d\n", dmach->chan); + + kmm_free(dmach->list); + + dma_close(dmach->chan); + dma_uninit(dmach->chan); + + dmach->inuse = false; + +err: + sem_post(&g_dmaexc); +} + +/**************************************************************************** + * Name: cxd56_rxdmasetup + * + * Description: + * Configure an RX (peripheral-to-memory) DMA before starting the transfer. + * + * Input Parameters: + * paddr - Peripheral address (source) + * maddr - Memory address (destination) + * nbytes - Number of bytes to transfer. Must be an even multiple of the + * configured transfer size. + * config - Channel configuration selections + * + ****************************************************************************/ + +void cxd56_rxdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, + size_t nbytes, dma_config_t config) +{ + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + int i; + int list_num; + uintptr_t dst; + size_t rest; + int peri; + + DEBUGASSERT(dmach != NULL && dmach->inuse && dmach->list != NULL); + + dst = maddr; + rest = nbytes; + + list_num = (nbytes + CXD56_DMAC_MAX_SIZE - 1) / CXD56_DMAC_MAX_SIZE; + for (i = 0; i < list_num - 1; i++) + { + dmach->list[i].src_addr = paddr; + dmach->list[i].dest_addr = dst; + dmach->list[i].nextlli = (uint32_t)&dmach->list[i + 1]; + dmach->list[i].control = DMAC_EX_CTRL_HELPER(0, 1, 0, /* interrupt / Dest inc / Src inc */ + CXD56_DMAC_MASTER1, CXD56_DMAC_MASTER2, /* AHB dst master / AHB src master (fixed) */ + config.dest_width, config.src_width, /* Dest / Src transfer width */ + CXD56_DMAC_BSIZE4, CXD56_DMAC_BSIZE4, /* Dest / Src burst size (fixed) */ + CXD56_DMAC_MAX_SIZE); + + dst += CXD56_DMAC_MAX_SIZE; + rest -= CXD56_DMAC_MAX_SIZE; + } + + dmach->list[i].src_addr = paddr; + dmach->list[i].dest_addr = dst; + dmach->list[i].nextlli = 0; + dmach->list[i].control = DMAC_EX_CTRL_HELPER(1, 1, 0, /* interrupt / Dest inc / Src inc */ + CXD56_DMAC_MASTER1, CXD56_DMAC_MASTER2, /* AHB dst master / AHB src master (fixed) */ + config.dest_width, config.src_width, /* Dest / Src transfer width */ + CXD56_DMAC_BSIZE4, CXD56_DMAC_BSIZE4, /* Dest / Src burst size (fixed) */ + rest); + + peri = config.channel_cfg & CXD56_DMA_PERIPHERAL_MASK; + dma_setconfig(dmach->chan, 1, 1, CXD56_DMAC_P2M, 0, peri); +} + +/**************************************************************************** + * Name: cxd56_txdmasetup + * + * Description: + * Configure an TX (memory-to-peripheral) DMA before starting the transfer. + * + * Input Parameters: + * paddr - Peripheral address (destination) + * maddr - Memory address (source) + * nbytes - Number of bytes to transfer. Must be an even multiple of the + * configured transfer size. + * config - Channel configuration selections + * + ****************************************************************************/ + +void cxd56_txdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, + size_t nbytes, dma_config_t config) +{ + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + int i; + int list_num; + uintptr_t src; + size_t rest; + int peri; + + DEBUGASSERT(dmach != NULL && dmach->inuse && dmach->list != NULL); + + src = maddr; + rest = nbytes; + + list_num = (nbytes + CXD56_DMAC_MAX_SIZE - 1) / CXD56_DMAC_MAX_SIZE; + for (i = 0; i < list_num - 1; i++) + { + dmach->list[i].src_addr = src; + dmach->list[i].dest_addr = paddr; + dmach->list[i].nextlli = (uint32_t)&dmach->list[i + 1]; + dmach->list[i].control = DMAC_EX_CTRL_HELPER(0, 0, 1, /* interrupt / Dest inc / Src inc */ + CXD56_DMAC_MASTER2, CXD56_DMAC_MASTER1, /* AHB dst master / AHB src master (fixed) */ + config.dest_width, config.src_width, /* Dest / Src transfer width (fixed) */ + CXD56_DMAC_BSIZE1, CXD56_DMAC_BSIZE1, /* Dest / Src burst size (fixed) */ + CXD56_DMAC_MAX_SIZE); + + src += CXD56_DMAC_MAX_SIZE; + rest -= CXD56_DMAC_MAX_SIZE; + } + + dmach->list[i].src_addr = src; + dmach->list[i].dest_addr = paddr; + dmach->list[i].nextlli = 0; + dmach->list[i].control = DMAC_EX_CTRL_HELPER(1, 0, 1, /* interrupt / Dest inc / Src inc */ + CXD56_DMAC_MASTER2, CXD56_DMAC_MASTER1, /* AHB dst master / AHB src master (fixed) */ + config.dest_width, config.src_width, /* Dest / Src transfer width (fixed) */ + CXD56_DMAC_BSIZE4, CXD56_DMAC_BSIZE4, /* Dest / Src burst size (fixed) */ + rest); + + peri = config.channel_cfg & CXD56_DMA_PERIPHERAL_MASK; + dma_setconfig(dmach->chan, 1, 1, CXD56_DMAC_M2P, peri, 0); +} + +/**************************************************************************** + * Name: cxd56_dmastart + * + * Description: + * Start the DMA transfer + * + * Assumptions: + * - DMA handle allocated by cxd56_dmachannel() + * - No DMA in progress + * + ****************************************************************************/ + +void cxd56_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) +{ + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + + DEBUGASSERT(dmach && dmach->inuse); + + /* Save the DMA complete callback info */ + + dma_setintrcallback(dmach->chan, callback, arg); + dma_start(dmach->chan, dmach->list); +} + +/**************************************************************************** + * Name: cxd56_dmastop + * + * Description: + * Cancel the DMA. After cxd56_dmastop() is called, the DMA channel is + * reset and cxd56_dmasetup() must be called before cxd56_dmastart() can be + * called again + * + * Assumptions: + * - DMA handle allocated by cxd56_dmachannel() + * + ****************************************************************************/ + +void cxd56_dmastop(DMA_HANDLE handle) +{ + struct dma_channel_s *dmach = (struct dma_channel_s *)handle; + + DEBUGASSERT(dmach); + + dma_stop(dmach->chan); + dma_clearintrcallback(dmach->chan); +} diff --git a/arch/arm/src/cxd56xx/cxd56_dmac.h b/arch/arm/src/cxd56xx/cxd56_dmac.h new file mode 100644 index 0000000000..1cc55d9816 --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_dmac.h @@ -0,0 +1,209 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_dmac.h + * + * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file cxd56_dmac.h + */ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_H +#define __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_H + +#include + +#include "hardware/cxd56_dmac_common.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CXD56_DMA_PERIPHERAL_MASK (0x0f) +#define CXD56_DMA_PERIPHERAL_UART2_TX (0) +#define CXD56_DMA_PERIPHERAL_UART2_RX (1) +#define CXD56_DMA_PERIPHERAL_SPI4_TX (2) +#define CXD56_DMA_PERIPHERAL_SPI4_RX (3) +#define CXD56_DMA_PERIPHERAL_SPI5_TX (4) +#define CXD56_DMA_PERIPHERAL_SPI5_RX (5) + +#define CXD56_DMA_INTR_ITC (1u<<0) /**< Terminal count interrupt status */ +#define CXD56_DMA_INTR_ERR (1u<<1) /**< Error interrupt status */ + +#define CXD56_DMAC_WIDTH8 0 /**< 8 bit width */ +#define CXD56_DMAC_WIDTH16 1 /**< 16 bit width */ +#define CXD56_DMAC_WIDTH32 2 /**< 32 bit width */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: cxd56_dmachannel + * + * Description: + * Allocate a DMA channel. This function gives the caller mutually exclusive + * access to a DMA channel. + * + * If no DMA channel is available, then cxd56_dmachannel() will wait until + * the holder of a channel relinquishes the channel by calling + * cxd56_dmafree(). + * + * Input parameters: + * ch - DMA channel to use + * maxsize - Max size to be transfered in bytes + * + * Returned Value: + * This function ALWAYS returns a non-NULL, void* DMA channel handle. + * + * Assumptions: + * - The caller can wait for a DMA channel to be freed if it is not + * available. + * + ****************************************************************************/ + +DMA_HANDLE cxd56_dmachannel(int ch, ssize_t maxsize); + +/**************************************************************************** + * Name: cxd56_dmafree + * + * Description: + * Release a DMA channel. If another thread is waiting for this DMA channel + * in a call to cxd56_dmachannel, then this function will re-assign the DMA + * channel to that thread and wake it up. + * + * NOTE: The 'handle' used in this argument must NEVER be used again until + * cxd56_dmachannel() is called again to re-gain access to the channel. + * + * Returned Value: + * None + * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * + ****************************************************************************/ + +void cxd56_dmafree(DMA_HANDLE handle); + +/**************************************************************************** + * Name: cxd56_rxdmasetup + * + * Description: + * Configure an RX (peripheral-to-memory) DMA before starting the transfer. + * + * Input Parameters: + * paddr - Peripheral address (source) + * maddr - Memory address (destination) + * nbytes - Number of bytes to transfer. Must be an even multiple of the + * configured transfer size. + * config - Channel configuration selections + * + ****************************************************************************/ + +void cxd56_rxdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, + size_t nbytes, dma_config_t config); + +/**************************************************************************** + * Name: cxd56_txdmasetup + * + * Description: + * Configure an TX (memory-to-peripheral) DMA before starting the transfer. + * + * Input Parameters: + * paddr - Peripheral address (destination) + * maddr - Memory address (source) + * nbytes - Number of bytes to transfer. Must be an even multiple of the + * configured transfer size. + * config - Channel configuration selections + * + ****************************************************************************/ + +void cxd56_txdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, + size_t nbytes, dma_config_t config); + +/**************************************************************************** + * Name: cxd56_dmastart + * + * Description: + * Start the DMA transfer + * + * Assumptions: + * - DMA handle allocated by cxd56_dmachannel() + * - No DMA in progress + * + ****************************************************************************/ + +void cxd56_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg); + +/**************************************************************************** + * Name: cxd56_dmastop + * + * Description: + * Cancel the DMA. After cxd56_dmastop() is called, the DMA channel is reset + * and cxd56_dmasetup() must be called before cxd56_dmastart() can be called + * again + * + * Assumptions: + * - DMA handle allocated by cxd56_dmachannel() + * + ****************************************************************************/ + +void cxd56_dmastop(DMA_HANDLE handle); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_H */ diff --git a/arch/arm/src/cxd56xx/cxd56_rtc.c b/arch/arm/src/cxd56xx/cxd56_rtc.c new file mode 100644 index 0000000000..c9d216625e --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_rtc.c @@ -0,0 +1,665 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_rtc.c + * + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sony Semiconductor Solutions Corporation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "up_arch.h" +#include "cxd56_rtc.h" + +#include "hardware/cxd5602_topreg.h" +#include "hardware/cxd5602_memorymap.h" +#include "hardware/cxd5602_backupmem.h" +#include "hardware/cxd56_rtc.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#ifdef CONFIG_RTC_HIRES +# ifndef CONFIG_RTC_FREQUENCY +# define CONFIG_RTC_FREQUENCY 32768 +# endif +# if CONFIG_RTC_FREQUENCY != 32768 +# error "Only lo-res CONFIG_RTC_FREQUENCY of 32.768kHz is supported" +# endif +#else +# ifndef CONFIG_RTC_FREQUENCY +# define CONFIG_RTC_FREQUENCY 1 +# endif +# if CONFIG_RTC_FREQUENCY != 1 +# error "Only lo-res CONFIG_RTC_FREQUENCY of 1Hz is supported" +# endif +#endif + +/* convert seconds to 64bit counter value running at 32kHz */ + +#define SEC_TO_CNT(sec) ((uint64_t)(((uint64_t)(sec)) << 15)) + +/* convert nano-seconds to 32kHz counter less than 1 second */ + +#define NSEC_TO_PRECNT(nsec) \ + (((nsec) / (NSEC_PER_SEC / CONFIG_RTC_FREQUENCY)) & 0x7fff) + +#define MAGIC_RTC_SAVE (0x12aae190077a80ull) + +/* RTC clcok stable waiting time (interval x retry) */ + +#define RTC_CLOCK_CHECK_INTERVAL (200) /* milliseconds */ +#define RTC_CLOCK_CHECK_MAX_RETRY (15) + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +struct alm_cbinfo_s +{ + volatile alm_callback_t ac_cb; /* Client callback function */ + volatile FAR void *ac_arg; /* Argument to pass with the callback function */ +}; +#endif + +struct rtc_backup_s +{ + uint64_t magic; + int64_t reserved0; + int64_t offset; /* offset time from RTC HW value */ + int64_t reserved1; +}; + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +/* Callback to use when the alarm expires */ + +#ifdef CONFIG_RTC_ALARM +static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST]; +#endif + +/* Saved data for persistent RTC time */ + +static struct rtc_backup_s *g_rtc_save; + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: rtc_dumptime + * + * Description: + * Dump RTC + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ************************************************************************************/ + +#ifdef CONFIG_DEBUG_RTC +static void rtc_dumptime(FAR const struct timespec *tp, FAR const char *msg) +{ + FAR struct tm tm; + + (void)gmtime_r(&tp->tv_sec, &tm); + + rtcinfo("%s:\n", msg); + rtcinfo("RTC %u.%09u\n", tp->tv_sec, tp->tv_nsec); + rtcinfo("%4d/%02d/%02d %02d:%02d:%02d\n", + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} +#else +# define rtc_dumptime(tp, msg) +#endif + +/************************************************************************************ + * Name: cxd56_rtc_interrupt + * + * Description: + * RTC interrupt service routine + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * context - Architecture specific register save information. + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int cxd56_rtc_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct alm_cbinfo_s *cbinfo; + alm_callback_t cb; + FAR void *cb_arg; + uint32_t source, clear; + int id; + int ret = OK; + + /* interrupt clear */ + + source = getreg32(CXD56_RTC0_ALMFLG); + + if (source & RTCREG_ALM0_MASK) + { + id = RTC_ALARM0; + clear = source & RTCREG_ALM0_MASK; + } + else if (source & RTCREG_ALM1_MASK) + { + id = RTC_ALARM1; + clear = source & RTCREG_ALM1_MASK; + } + else if (source & RTCREG_ALM2_MASK) + { + id = RTC_ALARM2; + clear = source & RTCREG_ALM2_MASK; + } + else + { + rtcerr("ERROR: Invalid ALARM\n"); + return ret; + } + putreg32(clear, CXD56_RTC0_ALMCLR); + putreg32(0, CXD56_RTC0_ALMOUTEN(id)); + + cbinfo = &g_alarmcb[id]; + + if (cbinfo->ac_cb != NULL) + { + /* Alarm callback */ + + cb = cbinfo->ac_cb; + cb_arg = (FAR void*)cbinfo->ac_arg; + + cbinfo->ac_cb = NULL; + cbinfo->ac_arg = NULL; + + cb(cb_arg, id); + } + + return 0; +} +#endif + + +/************************************************************************************ + * Name: cxd56_rtc_initialize + * + * Description: + * Actually initialize the hardware RTC. This function is called in the + * initialization sequence, thereafter may be called when wdog timer is expired. + * + * Input Parameters: + * arg: Not used + * + ************************************************************************************/ + +static void cxd56_rtc_initialize(int argc, uint32_t arg) +{ + struct timespec ts; +#ifdef CONFIG_CXD56_RTC_LATEINIT + static WDOG_ID s_wdog = NULL; + static int s_retry = 0; + + if (s_wdog == NULL) + { + s_wdog = wd_create(); + } + + /* Check whether RTC clock source selects the external RTC and the synchronization + * from the external RTC is completed. + */ + + g_rtc_save = (struct rtc_backup_s*)BKUP->rtc_saved_data; + + if (((getreg32(CXD56_TOPREG_CKSEL_ROOT) & STATUS_RTC_MASK) != STATUS_RTC_SEL) || + (g_rtc_save->magic != MAGIC_RTC_SAVE)) + { + + /* Retry until RTC clock is stable */ + + if (s_retry++ < RTC_CLOCK_CHECK_MAX_RETRY) { + + rtcinfo("retry count: %d\n", s_retry); + + if (OK == wd_start(s_wdog, MSEC2TICK(RTC_CLOCK_CHECK_INTERVAL), + (wdentry_t)cxd56_rtc_initialize, 1, (wdparm_t)NULL)) + { + /* Again, this function is called recursively */ + + return; + } + } + + rtcerr("ERROR: Use inaccurate RCRTC instead of RTC\n"); + } + + /* RTC clock is stable, or give up using the external RTC */ + + if (s_wdog != NULL) + { + wd_delete(s_wdog); + } +#endif + +#ifdef CONFIG_RTC_ALARM + /* Configure RTC interrupt to catch overflow and alarm interrupts. */ + + irq_attach(CXD56_IRQ_RTC0_A0, cxd56_rtc_interrupt, NULL); + irq_attach(CXD56_IRQ_RTC0_A2, cxd56_rtc_interrupt, NULL); + irq_attach(CXD56_IRQ_RTC_INT, cxd56_rtc_interrupt, NULL); + up_enable_irq(CXD56_IRQ_RTC0_A0); + up_enable_irq(CXD56_IRQ_RTC0_A2); + up_enable_irq(CXD56_IRQ_RTC_INT); +#endif + + /* If saved data is invalid, clear offset information */ + + if (g_rtc_save->magic != MAGIC_RTC_SAVE) + { + g_rtc_save->offset = 0; + } + + if (g_rtc_save->offset == 0) + { + /* Keep the system operating time before RTC is enabled. */ + + clock_systimespec(&ts); + } + + /* Synchronize the system time to the RTC time */ + + clock_synchronize(); + + if (g_rtc_save->offset == 0) + { + /* Reflect the system operating time to RTC offset data. */ + + g_rtc_save->offset = SEC_TO_CNT(ts.tv_sec) | NSEC_TO_PRECNT(ts.tv_nsec); + } + + /* Make it possible to use the RTC timer functions */ + + g_rtc_enabled = true; + + return; +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: up_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This function is + * called once during the OS initialization sequence + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_initialize(void) +{ + cxd56_rtc_initialize(1, (wdparm_t)NULL); + return OK; +} + +/************************************************************************************ + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. This is similar to the standard time() + * function. This interface is only required if the low-resolution RTC/counter + * hardware implementation selected. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC is set but neither + * CONFIG_RTC_HIRES nor CONFIG_RTC_DATETIME are set. + * + * Input Parameters: + * None + * + * Returned Value: + * The current time in seconds + * + ************************************************************************************/ + +#ifndef CONFIG_RTC_HIRES +time_t up_rtc_time(void) +{ + uint64_t count; + + count = cxd56_rtc_count(); + count += g_rtc_save->offset; + count >>= 15; /* convert to 1sec resolution */ + + return (time_t)count/CONFIG_RTC_FREQUENCY; +} +#endif + +/************************************************************************************ + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC clock/counter. This interface + * is only supported by the high-resolution RTC/counter hardware implementation. + * It is used to replace the system timer. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(FAR struct timespec *tp) +{ + uint64_t count; + + count = cxd56_rtc_count(); + count += g_rtc_save->offset; + + /* Then we can save the time in seconds and fractional seconds. */ + + tp->tv_sec = count / CONFIG_RTC_FREQUENCY; + tp->tv_nsec = (count % CONFIG_RTC_FREQUENCY)*(NSEC_PER_SEC/CONFIG_RTC_FREQUENCY); + + rtc_dumptime(tp, "Getting time"); + + return OK; +} +#endif + +/************************************************************************************ + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. All RTC implementations must be able to + * set their time based on a standard timespec. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + irqstate_t flags; + uint64_t count; + + flags = enter_critical_section(); + +#ifdef RTC_DIRECT_CONTROL + /* wait until previous write request is completed */ + + while (RTCREG_WREQ_BUSYA_MASK & getreg32(CXD56_RTC0_WRREGREQ)); + + putreg32(tp->tv_sec, CXD56_RTC0_WRREGPOSTCNT); + putreg32(NSEC_TO_PRECNT(tp->tv_nsec), CXD56_RTC0_WRREGPRECNT); + + putreg32(RTCREG_WREQ_BUSYA_MASK, CXD56_RTC0_WRREGREQ); + + /* wait until write request reflected */ + + while (RTCREG_WREQ_BUSYB_MASK & getreg32(CXD56_RTC0_WRREGREQ)); + +#else + /* Only save the difference from HW raw value */ + + count = SEC_TO_CNT(tp->tv_sec) | NSEC_TO_PRECNT(tp->tv_nsec); + g_rtc_save->offset = (int64_t)count - (int64_t)cxd56_rtc_count(); +#endif + + leave_critical_section(flags); + + rtc_dumptime(tp, "Setting time"); + + return OK; +} + +/************************************************************************************ + * Name: cxd56_rtc_count + * + * Description: + * Get RTC raw counter value + * + * Returned Value: + * 64bit counter value running at 32kHz + * + ************************************************************************************/ + +uint64_t cxd56_rtc_count(void) +{ + uint64_t val; + irqstate_t flags; + + /* + * The pre register is latched with reading the post rtcounter register, + * so these registers always have to been read in the below order, + * 1st post -> 2nd pre, and should be operated in atomic. + */ + + flags = enter_critical_section(); + + val = (uint64_t)getreg32(CXD56_RTC0_RTPOSTCNT) << 15; + val |= getreg32(CXD56_RTC0_RTPRECNT); + + leave_critical_section(flags); + + return val; +} + +/************************************************************************************ + * Name: cxd56_rtc_almcount + * + * Description: + * Get RTC raw alarm counter value + * + * Returned Value: + * 64bit alarm counter value running at 32kHz + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +uint64_t cxd56_rtc_almcount(void) +{ + uint64_t val; + irqstate_t flags; + + flags = enter_critical_section(); + + val = (uint64_t)getreg32(CXD56_RTC0_SETALMPOSTCNT(0)) << 15; + val |= (getreg32(CXD56_RTC0_SETALMPRECNT(0)) & 0x7fff); + + leave_critical_section(flags); + + return val; +} +#endif + +/************************************************************************************ + * Name: cxd56_rtc_setalarm + * + * Description: + * Set up an alarm. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int cxd56_rtc_setalarm(FAR struct alm_setalarm_s *alminfo) +{ + FAR struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + int ret = -EBUSY; + int id; + uint64_t count; + + ASSERT(alminfo != NULL); + DEBUGASSERT(RTC_ALARM_LAST > alminfo->as_id); + + /* Set the alarm in hardware and enable interrupts */ + + id = alminfo->as_id; + cbinfo = &g_alarmcb[id]; + + if (cbinfo->ac_cb == NULL) + { + /* The set the alarm */ + + flags = enter_critical_section(); + + cbinfo->ac_cb = alminfo->as_cb; + cbinfo->ac_arg = alminfo->as_arg; + + count = SEC_TO_CNT(alminfo->as_time.tv_sec) | + NSEC_TO_PRECNT(alminfo->as_time.tv_nsec); + + count -= g_rtc_save->offset; + + /* wait until previous alarm request is completed */ + + while (RTCREG_ASET_BUSY_MASK & getreg32(CXD56_RTC0_SETALMPRECNT(id))); + + putreg32((uint32_t)(count >> 15), CXD56_RTC0_SETALMPOSTCNT(id)); + putreg32((uint32_t)(count & 0x7fff), CXD56_RTC0_SETALMPRECNT(id)); + + while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(id))); + + putreg32(RTCREG_ALM_EN_MASK | RTCREG_ALM_ERREN_MASK, + CXD56_RTC0_ALMOUTEN(id)); + + while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(id))); + + leave_critical_section(flags); + + rtc_dumptime(&alminfo->as_time, "New Alarm time"); + ret = OK; + } + + return ret; +} +#endif + +/************************************************************************************ + * Name: cxd56_rtc_cancelalarm + * + * Description: + * Cancel an alaram. + * + * Input Parameters: + * alarmid - Identifies the alarm to be cancelled + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int cxd56_rtc_cancelalarm(enum alm_id_e alarmid) +{ + FAR struct alm_cbinfo_s *cbinfo; + irqstate_t flags; + int ret = -ENODATA; + + DEBUGASSERT(RTC_ALARM_LAST > alarmid); + + /* Set the alarm in hardware and enable interrupts */ + + cbinfo = &g_alarmcb[alarmid]; + + if (cbinfo->ac_cb != NULL) + { + /* Unset the alarm */ + + flags = enter_critical_section(); + + cbinfo->ac_cb = NULL; + + while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(alarmid))); + + putreg32(0, CXD56_RTC0_ALMOUTEN(alarmid)); + + leave_critical_section(flags); + + ret = OK; + } + + return ret; +} +#endif diff --git a/arch/arm/src/cxd56xx/cxd56_rtc.h b/arch/arm/src/cxd56xx/cxd56_rtc.h new file mode 100644 index 0000000000..a086440bee --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_rtc.h @@ -0,0 +1,191 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_rtc.h + * + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sony Semiconductor Solutions Corporation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H +#define __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_RTC_ALARM + +/* The form of an alarm callback */ + +typedef CODE void (*alm_callback_t)(FAR void *arg, unsigned int alarmid); + +enum alm_id_e +{ + RTC_ALARM0 = 0, /* RTC ALARM 0 */ + RTC_ALARM_LAST, + RTC_ALARM1 = 1, /* RTC ALARM 1 */ + RTC_ALARM2, /* RTC ALARM 2 (relative) */ +}; + +/* Structure used to pass parmaters to set an alarm */ + +struct alm_setalarm_s +{ + int as_id; /* enum alm_id_e */ + struct timespec as_time; /* Alarm expiration time */ + alm_callback_t as_cb; /* Callback (if non-NULL) */ + FAR void *as_arg; /* Argument for callback */ +}; + +#endif /* CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/************************************************************************************ + * Name: cxd56_rtc_count + * + * Description: + * Get RTC raw counter value + * + * Returned Value: + * 64bit counter value running at 32kHz + * + ************************************************************************************/ + +uint64_t cxd56_rtc_count(void); + +/************************************************************************************ + * Name: cxd56_rtc_almcount + * + * Description: + * Get RTC raw alarm counter value + * + * Returned Value: + * 64bit alarm counter value running at 32kHz + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +uint64_t cxd56_rtc_almcount(void); +#endif /* CONFIG_RTC_ALARM */ + +/************************************************************************************ + * Name: cxd56_rtc_setalarm + * + * Description: + * Set up an alarm. + * + * Input Parameters: + * alminfo - Information about the alarm configuration. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int cxd56_rtc_setalarm(FAR struct alm_setalarm_s *alminfo); +#endif /* CONFIG_RTC_ALARM */ + +/************************************************************************************ + * Name: cxd56_rtc_cancelalarm + * + * Description: + * Cancel an alaram. + * + * Input Parameters: + * alarmid - Identifies the alarm to be cancelled + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int cxd56_rtc_cancelalarm(enum alm_id_e alarmid); +#endif /* CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: cxd56_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the STM32L4. General usage: + * + * #include + * #include "stm32l4_rtc.h> + * + * struct rtc_lowerhalf_s *lower; + * lower = stm32l4_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_DRIVER +FAR struct rtc_lowerhalf_s *cxd56_rtc_lowerhalf(void); +#endif /* CONFIG_RTC_DRIVER */ + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H */ diff --git a/arch/arm/src/cxd56xx/cxd56_rtc_lowerhalf.c b/arch/arm/src/cxd56xx/cxd56_rtc_lowerhalf.c new file mode 100644 index 0000000000..2cd73d7a5d --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_rtc_lowerhalf.c @@ -0,0 +1,544 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_rtc_lowerhalf.c + * + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sony Semiconductor Solutions Corporation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* REVISIT: This driver is *not* thread-safe! */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "cxd56_rtc.h" + +#ifdef CONFIG_RTC_DRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +struct cxd56_cbinfo_s +{ + volatile rtc_alarm_callback_t cb; /* Callback when the alarm expires */ + volatile FAR void *priv; /* Private argurment to accompany callback */ +}; +#endif + +/* This is the private type for the RTC state. It must be cast compatible + * with struct rtc_lowerhalf_s. + */ + +struct cxd56_lowerhalf_s +{ + /* This is the contained reference to the read-only, lower-half + * operations vtable (which may lie in FLASH or ROM) + */ + + FAR const struct rtc_ops_s *ops; + + /* Data following is private to this driver and not visible outside of + * this file. + */ + +#ifdef CONFIG_RTC_ALARM + /* Alarm callback information */ + + struct cxd56_cbinfo_s cbinfo[RTC_ALARM_LAST]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Prototypes for static methods in struct rtc_ops_s */ + +static int cxd56_rdtime(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_time *rtctime); +static int cxd56_settime(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_time *rtctime); + +#ifdef CONFIG_RTC_ALARM +static int cxd56_setalarm(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo); +static int cxd56_setrelative(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setrelative_s *alarminfo); +static int cxd56_cancelalarm(FAR struct rtc_lowerhalf_s *lower, + int alarmid); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* CXD56 RTC driver operations */ + +static const struct rtc_ops_s g_rtc_ops = +{ + .rdtime = cxd56_rdtime, + .settime = cxd56_settime, +#ifdef CONFIG_RTC_ALARM + .setalarm = cxd56_setalarm, + .setrelative = cxd56_setrelative, + .cancelalarm = cxd56_cancelalarm, +#endif +#ifdef CONFIG_RTC_IOCTL + .ioctl = NULL, +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .destroy = NULL, +#endif +}; + +/* CXD56 RTC device state */ + +static struct cxd56_lowerhalf_s g_rtc_lowerhalf = +{ + .ops = &g_rtc_ops, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cxd56_alarm_callback + * + * Description: + * This is the function that is called from the RTC driver when the alarm + * goes off. It just invokes the upper half drivers callback. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static void cxd56_alarm_callback(FAR void *arg, unsigned int alarmid) +{ + FAR struct cxd56_lowerhalf_s *lower; + FAR struct cxd56_cbinfo_s *cbinfo; + rtc_alarm_callback_t cb; + FAR void *priv; + + DEBUGASSERT((RTC_ALARM0 <= alarmid) && (alarmid < RTC_ALARM_LAST)); + + lower = (struct cxd56_lowerhalf_s *)arg; + cbinfo = &lower->cbinfo[alarmid]; + + /* Sample and clear the callback information to minimize the window in + * time in which race conditions can occur. + */ + + cb = (rtc_alarm_callback_t)cbinfo->cb; + priv = (FAR void *)cbinfo->priv; + + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Perform the callback */ + + if (cb != NULL) + { + cb(priv, alarmid); + } +} +#endif /* CONFIG_RTC_ALARM */ + +/**************************************************************************** + * Name: cxd56_rdtime + * + * Description: + * Implements the rdtime() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * rcttime - The location in which to return the current RTC time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +static int cxd56_rdtime(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_time *rtctime) +{ +#if defined(CONFIG_RTC_HIRES) + FAR struct timespec ts; + int ret; + + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + goto errout_with_errno; + } + + /* Convert the one second epoch time to a struct tm. This operation + * depends on the fact that struct rtc_time and struct tm are cast + * compatible. + */ + + if (!gmtime_r(&ts.tv_sec, (FAR struct tm *)rtctime)) + { + goto errout_with_errno; + } + + return OK; + +errout_with_errno: + ret = get_errno(); + DEBUGASSERT(ret > 0); + return -ret; + +#else + time_t timer; + + /* The resolution of time is only 1 second */ + + timer = up_rtc_time(); + + /* Convert the one second epoch time to a struct tm */ + + if (!gmtime_r(&timer, (FAR struct tm *)rtctime)) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + return OK; +#endif +} + +/**************************************************************************** + * Name: cxd56_settime + * + * Description: + * Implements the settime() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * rcttime - The new time to set + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +static int cxd56_settime(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_time *rtctime) +{ + struct timespec ts; + + /* Convert the struct rtc_time to a time_t. Here we assume that struct + * rtc_time is cast compatible with struct tm. + */ + + ts.tv_sec = mktime((FAR struct tm *)rtctime); + ts.tv_nsec = 0; + + /* Now set the time (to one second accuracy) */ + + return up_rtc_settime(&ts); +} + +/**************************************************************************** + * Name: cxd56_setalarm + * + * Description: + * Set a new alarm. This function implements the setalarm() method of the + * RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * alarminfo - Provided information needed to set the alarm + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int cxd56_setalarm(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo) +{ + FAR struct cxd56_lowerhalf_s *priv; + FAR struct cxd56_cbinfo_s *cbinfo; + struct alm_setalarm_s lowerinfo; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + DEBUGASSERT((RTC_ALARM0 == alarminfo->id) || (RTC_ALARM1 == alarminfo->id)); + + priv = (FAR struct cxd56_lowerhalf_s *)lower; + + if ((RTC_ALARM0 == alarminfo->id) || (RTC_ALARM1 == alarminfo->id)) + { + /* Remember the callback information */ + + cbinfo = &priv->cbinfo[alarminfo->id]; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* Set the alarm */ + + lowerinfo.as_id = alarminfo->id; + lowerinfo.as_cb = cxd56_alarm_callback; + lowerinfo.as_arg = priv; + + /* Convert the RTC time to a timespec (1 second accuracy) */ + + lowerinfo.as_time.tv_sec = mktime((FAR struct tm *)&alarminfo->time); + lowerinfo.as_time.tv_nsec = 0; + + /* And set the alarm */ + + ret = cxd56_rtc_setalarm(&lowerinfo); + if (ret < 0) + { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: cxd56_setrelative + * + * Description: + * Set a new alarm relative to the current time. This function implements + * the setrelative() method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * alarminfo - Provided information needed to set the alarm + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int cxd56_setrelative(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setrelative_s *alarminfo) +{ + struct lower_setalarm_s setalarm; + FAR struct timespec ts; + struct alm_setalarm_s lowerinfo; + FAR struct cxd56_lowerhalf_s *priv; + FAR struct cxd56_cbinfo_s *cbinfo; + time_t seconds; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL && alarminfo != NULL); + DEBUGASSERT((RTC_ALARM0 <= alarminfo->id) && (alarminfo->id < RTC_ALARM_LAST)); + + if (((alarminfo->id == RTC_ALARM0) || (alarminfo->id == RTC_ALARM1)) && + (alarminfo->reltime > 0)) + { + /* Disable pre-emption while we do this so that we don't have to worry + * about being suspended and working on an old time. + */ + + sched_lock(); + +#if defined(CONFIG_RTC_HIRES) + /* Get the higher resolution time */ + + ret = up_rtc_gettime(&ts); + if (ret < 0) + { + sched_unlock(); + return ret; + } +#else + /* The resolution of time is only 1 second */ + + ts.tv_sec = up_rtc_time(); + ts.tv_nsec = 0; +#endif + + /* Add the seconds offset. Add one to the number of seconds because + * we are unsure of the phase of the timer. + */ + + seconds = ts.tv_sec + (alarminfo->reltime + 1); + + (void)gmtime_r(&seconds, (FAR struct tm *)&setalarm.time); + + /* The set the alarm using this absolute time */ + + setalarm.id = alarminfo->id; + setalarm.cb = alarminfo->cb; + setalarm.priv = alarminfo->priv; + + ret = cxd56_setalarm(lower, &setalarm); + + sched_unlock(); + } + else if ((alarminfo->id == RTC_ALARM2) && (alarminfo->reltime > 0)) + { + sched_lock(); + + priv = (FAR struct cxd56_lowerhalf_s *)lower; + + /* Remember the callback information */ + + cbinfo = &priv->cbinfo[alarminfo->id]; + cbinfo->cb = alarminfo->cb; + cbinfo->priv = alarminfo->priv; + + /* Set the alarm */ + + lowerinfo.as_id = alarminfo->id; + lowerinfo.as_cb = cxd56_alarm_callback; + lowerinfo.as_arg = priv; + + lowerinfo.as_time.tv_sec = alarminfo->reltime; + lowerinfo.as_time.tv_nsec = 0; + + ret = cxd56_rtc_setalarm(&lowerinfo); + if (ret < 0) + { + cbinfo->cb = NULL; + cbinfo->priv = NULL; + } + + sched_unlock(); + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: cxd56_cancelalarm + * + * Description: + * Cancel the current alarm. This function implements the cancelalarm() + * method of the RTC driver interface + * + * Input Parameters: + * lower - A reference to RTC lower half driver state structure + * alarmid - the alarm id + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static int cxd56_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) +{ + FAR struct cxd56_lowerhalf_s *priv; + FAR struct cxd56_cbinfo_s *cbinfo; + int ret = -EINVAL; + + DEBUGASSERT(lower != NULL); + DEBUGASSERT((RTC_ALARM0 <= alarmid) && (alarmid < RTC_ALARM_LAST)); + + priv = (FAR struct cxd56_lowerhalf_s *)lower; + + if ((RTC_ALARM0 <= alarmid) && (alarmid < RTC_ALARM_LAST)) + { + /* Nullify callback information to reduce window for race conditions */ + + cbinfo = &priv->cbinfo[alarmid]; + cbinfo->cb = NULL; + cbinfo->priv = NULL; + + /* Then cancel the alarm */ + + ret = cxd56_rtc_cancelalarm((enum alm_id_e)alarmid); + } + + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cxd56_rtc_lowerhalf + * + * Description: + * Instantiate the RTC lower half driver for the CXD56. General usage: + * + * #include + * #include "cxd56_rtc.h" + * + * struct rtc_lowerhalf_s *lower; + * lower = cxd56_rtc_lowerhalf(); + * rtc_initialize(0, lower); + * + * Input Parameters: + * None + * + * Returned Value: + * On success, a non-NULL RTC lower interface is returned. NULL is + * returned on any failure. + * + ****************************************************************************/ + +FAR struct rtc_lowerhalf_s *cxd56_rtc_lowerhalf(void) +{ + return (FAR struct rtc_lowerhalf_s *)&g_rtc_lowerhalf; +} + +#endif /* CONFIG_RTC_DRIVER */ diff --git a/arch/arm/src/cxd56xx/cxd56_spi.c b/arch/arm/src/cxd56xx/cxd56_spi.c new file mode 100644 index 0000000000..a72a420248 --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_spi.c @@ -0,0 +1,1625 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_spi.c + * + * Copyright (C) 2015-2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" + +#include "cxd56_spi.h" +#include "chip/cxd56_spi.h" +#include "cxd56_clock.h" +#include "cxd56_pinconfig.h" +#include "cxd56_powermgr.h" + +#ifdef CONFIG_CXD56_DMAC +#include "cxd56_dmac.h" +#endif + +#ifdef CONFIG_CXD56_SPI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure descibes the state of the SPI driver */ + +struct cxd56_spidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ + uint32_t spibasefreq; +#ifdef CONFIG_CXD56_SPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint8_t nbits; /* Width of word in bits (4 to 16) */ + uint8_t mode; /* Mode 0,1,2,3 */ + uint8_t port; /* Port number */ + int initialized; /* Initialized flag */ +#ifdef CONFIG_CXD56_DMAC + DMA_HANDLE rxdmach; /* RX DMA channel handle */ + DMA_HANDLE txdmach; /* TX DMA channel handle */ + sem_t dmasem; /* Wait for DMA to complete */ + dma_config_t rxconfig; /* RX DMA configuration */ + dma_config_t txconfig; /* TX DMA configuration */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static inline uint32_t spi_getreg(FAR struct cxd56_spidev_s *priv, + uint8_t offset); +static inline void spi_putreg(FAR struct cxd56_spidev_s *priv, uint8_t offset, + uint32_t value); + +/* DMA support */ + +#ifdef CONFIG_CXD56_DMAC +static void __unused spi_dmaexchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords); +static void spi_dmatxwait(FAR struct cxd56_spidev_s *priv); +static void spi_dmarxwait(FAR struct cxd56_spidev_s *priv); +static void spi_dmatrxwait(FAR struct cxd56_spidev_s *priv); +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t status, void *data); +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t status, void *data); +static void spi_dmatxsetup(FAR struct cxd56_spidev_s *priv, + FAR const void *txbuffer, size_t nwords); +static void spi_dmarxsetup(FAR struct cxd56_spidev_s *priv, + FAR const void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void spi_dmasndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, + size_t nwords); +#endif +#endif + +/* SPI methods */ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency); +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode); +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits); +static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t ch); +static void __unused spi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, + size_t nwords); +static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, + size_t nwords); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * SPI Channels mapped as follows: + * 4 - IMG SPI + * 5 - IMG WSPI + * 0 - SPIM (SYSIOP) + * 3 - SCU SPI + ****************************************************************************/ + +#ifdef CONFIG_CXD56_SPI4 +static const struct spi_ops_s g_spi4ops = +{ + .lock = spi_lock, + .select = cxd56_spi4select, /* Provided externally */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +#endif + .status = cxd56_spi4status, /* Provided externally */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = cxd56_spi4cmddata, /* Provided externally */ +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE +# if defined(CONFIG_CXD56_DMAC_SPI4_TX) || defined(CONFIG_CXD56_DMAC_SPI4_RX) + .exchange = spi_dmaexchange, +# else + .exchange = spi_exchange, +# endif +#else +# if defined(CONFIG_CXD56_DMAC_SPI4_TX) || defined(CONFIG_CXD56_DMAC_SPI4_RX) + .sndblock = spi_dmasndblock, + .recvblock = spi_dmarecvblock, +# else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +# endif +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = cxd56_spi4register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct cxd56_spidev_s g_spi4dev = +{ + .spidev = { &g_spi4ops }, + .spibase = CXD56_IMG_SPI_BASE, + .spibasefreq = 0, + .port = 4, + .initialized = 0, +#ifdef CONFIG_CXD56_SPI_INTERRUPTS + .spiirq = CXD56_IRQ_IMG_SPI, +#endif +}; + +#endif + +#ifdef CONFIG_CXD56_SPI5 +static const struct spi_ops_s g_spi5ops = +{ + .lock = spi_lock, + .select = cxd56_spi5select, /* Provided externally */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = cxd56_spi5status, /* Provided externally */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = cxd56_spi5cmddata, /* Provided externally */ +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE +# if defined(CONFIG_CXD56_DMAC_SPI5_TX) || defined(CONFIG_CXD56_DMAC_SPI5_RX) + .exchange = spi_dmaexchange, +# else + .exchange = spi_exchange, +# endif +#else +# if defined(CONFIG_CXD56_DMAC_SPI5_TX) || defined(CONFIG_CXD56_DMAC_SPI5_RX) + .sndblock = spi_dmasndblock, + .recvblock = spi_dmarecvblock, +# else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +# endif +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = cxd56_spi5register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct cxd56_spidev_s g_spi5dev = +{ + .spidev = { &g_spi5ops }, + .spibase = CXD56_IMG_WSPI_BASE, + .spibasefreq = 0, + .port = 5, + .initialized = 0, +#ifdef CONFIG_CXD56_SPI_INTERRUPTS + .spiirq = CXD56_IRQ_IMG_WSPI, +#endif +}; +#endif + +#ifdef CONFIG_CXD56_SPI0 +static const struct spi_ops_s g_spi0ops = +{ + .lock = spi_lock, + .select = cxd56_spi0select, /* Provided externally */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = cxd56_spi0status, /* Provided externally */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = cxd56_spi0cmddata, /* Provided externally */ +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = cxd56_spi0register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct cxd56_spidev_s g_spi0dev = +{ + .spidev = { &g_spi0ops }, + .spibase = CXD56_SPIM_BASE, + .spibasefreq = 0, + .port = 0, + .initialized = 0, +#ifdef CONFIG_CXD56_SPI_INTERRUPTS + .spiirq = CXD56_IRQ_SPIM, +#endif +}; +#endif + +#ifdef CONFIG_CXD56_SPI3 +static const struct spi_ops_s g_spi3ops = +{ + .lock = spi_lock, + .select = cxd56_spi3select, /* Provided externally */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, + .status = cxd56_spi3status, /* Provided externally */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = cxd56_spi3cmddata, /* Provided externally */ +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = cxd56_spi3register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct cxd56_spidev_s g_spi3dev = +{ + .spidev = { &g_spi3ops }, + .spibase = CXD56_SCU_SPI_BASE, + .spibasefreq = 0, + .port = 3, + .initialized = 0, +#ifdef CONFIG_CXD56_SPI_INTERRUPTS + .spiirq = CXD56_IRQ_SCU_SPI, +#endif +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t spi_getreg(FAR struct cxd56_spidev_s *priv, + uint8_t offset) +{ + return getreg32(priv->spibase + (uint32_t)offset); +} + +/**************************************************************************** + * Name: spi_putreg + * + * Description: + * Write a 32-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 16-bit value to be written + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void spi_putreg(FAR struct cxd56_spidev_s *priv, uint8_t offset, + uint32_t value) +{ + putreg32(value, priv->spibase + (uint32_t)offset); +} + +/**************************************************************************** + * Name: spi_lock + * + * Description: + * On SPI busses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the busses for a sequence of + * transfers. The bus should be locked before the chip is selected. After + * locking the SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI buss is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int spi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + + if (lock) + { + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&priv->exclsem) != 0) + { + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } + } + else + { + (void)sem_post(&priv->exclsem); + } + + return OK; +} + +/**************************************************************************** + * Name: spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t divisor; + uint32_t actual; + + /* Set SPI_CLOCK */ + + cxd56_spi_clock_gear_adjust(priv->port, frequency); + + /* frequency = SPI_CLOCK / divisor, or divisor = SPI_CLOCK / frequency */ + + priv->spibasefreq = cxd56_get_spi_baseclock(priv->port); + divisor = priv->spibasefreq / frequency; + + /* "In master mode, CPSDVSRmin = 2 or larger (even numbers only)" */ + + if (divisor < 2) + { + divisor = 2; + } + else if (divisor > 254) + { + divisor = 254; + } + + divisor = (divisor + 1) & ~1; + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + /* Save the new divisor value */ + + spi_putreg(priv, CXD56_SPI_CPSR_OFFSET, divisor); + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); + + /* Calculate the new actual */ + + actual = priv->spibasefreq / divisor; + + /* Save the frequency setting */ + + priv->frequency = frequency; + priv->actual = actual; + + spiinfo("Frequency %d->%d\n", frequency, actual); + return actual; +} + +/**************************************************************************** + * Name: spi_setmode + * + * Description: + * Set the SPI mode. Optional. See enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t regval; + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Yes... Set CR0 appropriately */ + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + regval = spi_getreg(priv, CXD56_SPI_CR0_OFFSET); + regval &= ~(SPI_CR0_CPOL | SPI_CR0_CPHA); + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + regval |= SPI_CR0_CPHA; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + regval |= SPI_CR0_CPOL; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + regval |= (SPI_CR0_CPOL | SPI_CR0_CPHA); + break; + + default: + spierr("Bad mode: %d\n", mode); + DEBUGASSERT(FALSE); + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); + + return; + } + + spi_putreg(priv, CXD56_SPI_CR0_OFFSET, regval); + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + } +} + +/**************************************************************************** + * Name: spi_setbits + * + * Description: + * Set the number if bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requests + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void spi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t regval; + + /* Has the number of bits changed? */ + + DEBUGASSERT(priv && nbits > 3 && nbits < 17); + + if (nbits != priv->nbits) + { + /* Yes... Set CR0 appropriately */ + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + regval = spi_getreg(priv, CXD56_SPI_CR0_OFFSET); + regval &= ~SPI_CR0_DSS_MASK; + regval |= ((nbits - 1) << SPI_CR0_DSS_SHIFT); + spi_putreg(priv, CXD56_SPI_CR0_OFFSET, regval); + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); + + /* Save the selection so that re-configurations will be faster + */ + + priv->nbits = nbits; + if (priv->nbits > 8) + { + priv->txconfig.dest_width = CXD56_DMAC_WIDTH16; + priv->txconfig.src_width = CXD56_DMAC_WIDTH16; + priv->rxconfig.dest_width = CXD56_DMAC_WIDTH16; + priv->rxconfig.src_width = CXD56_DMAC_WIDTH16; + } + else + { + priv->txconfig.dest_width = CXD56_DMAC_WIDTH8; + priv->txconfig.src_width = CXD56_DMAC_WIDTH8; + priv->rxconfig.dest_width = CXD56_DMAC_WIDTH8; + priv->rxconfig.src_width = CXD56_DMAC_WIDTH8; + } + } +} + +/**************************************************************************** + * Name: spi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ****************************************************************************/ + +static uint16_t spi_send(FAR struct spi_dev_s *dev, uint16_t wd) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + register uint32_t regval; + register uint32_t cr1val = 0; + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + if (priv->port == 3) + { + /* Enable SPI HW */ + + cr1val = spi_getreg(priv, CXD56_SPI_CR1_OFFSET); + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, cr1val | SPI_CR1_SSE); + } + + /* Wait while the TX FIFO is full */ + + while (!(spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_TNF)); + + /* Write the byte to the TX FIFO */ + + spi_putreg(priv, CXD56_SPI_DR_OFFSET, (uint32_t)wd); + + /* Wait for the RX FIFO not empty */ + + while (!(spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_RNE)); + + /* Get the value from the RX FIFO and return it */ + + regval = spi_getreg(priv, CXD56_SPI_DR_OFFSET); + spiinfo("%04x->%04x\n", wd, regval); + + if (priv->port == 3) + { + /* Restore SPI HW state */ + + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, cr1val); + } + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); + + return (uint16_t)regval; +} + +/**************************************************************************** + * Name: spi_exchange + * + * Description: + * Exahange a block of data from SPI. Required. + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t regval = 0; + + union + { + FAR const uint8_t *p8; + FAR const uint16_t *p16; + FAR const void *pv; + } tx; + union + { + FAR uint8_t *p8; + FAR uint16_t *p16; + FAR void *pv; + } rx; + uint32_t data; + uint32_t datadummy = (priv->nbits > 8) ? 0xffff : 0xff; + uint32_t rxpending = 0; + + /* Remaining data to be sent (and no synchronization error has occurred) */ + + tx.pv = txbuffer; + rx.pv = rxbuffer; + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + if (priv->port == 3) + { + /* Enable SPI HW */ + + regval = spi_getreg(priv, CXD56_SPI_CR1_OFFSET); + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval | SPI_CR1_SSE); + } + + while (nwords || rxpending) + { + /* Write data to the data register while (1) the TX FIFO is + * not full, (2) we have not exceeded the depth of the TX FIFO, + * and (3) there are more bytes to be sent. + */ + + spiinfo("TX: rxpending: %d nwords: %d\n", rxpending, nwords); + while ((spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_TNF) && + (rxpending < CXD56_SPI_FIFOSZ) && nwords) + { + if (txbuffer) + { + if (priv->nbits > 8) + { + data = (uint32_t)*tx.p16++; + } + else + { + data = (uint32_t)*tx.p8++; + } + } + + spi_putreg(priv, CXD56_SPI_DR_OFFSET, txbuffer ? data : datadummy); + nwords--; + rxpending++; + } + + /* Now, read the RX data from the RX FIFO while the RX FIFO is not empty + */ + + spiinfo("RX: rxpending: %d\n", rxpending); + while (spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_RNE) + { + data = spi_getreg(priv, CXD56_SPI_DR_OFFSET); + if (rxbuffer) + { + if (priv->nbits > 8) + { + *rx.p16++ = (uint16_t)data; + } + else + { + *rx.p8++ = (uint8_t)data; + } + } + + rxpending--; + } + } + + if (priv->port == 3) + { + /* Restore SPI HW state */ + + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval); + } + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); +} + +/**************************************************************************** + * Name: spi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * buffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, + size_t nwords) +{ + return spi_exchange(dev, buffer, NULL, nwords); +} + +/**************************************************************************** + * Name: spi_recvblock + * + * Description: + * Revice a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * buffer - A pointer to the buffer in which to recieve data + * nwords - the length of data that can be received in the buffer in number + * of words. The wordsize is determined by the number of + *bits-per-word selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, + size_t nwords) +{ + return spi_exchange(dev, NULL, buffer, nwords); +} +#endif /* !CONFIG_SPI_EXCHANGE */ + +/**************************************************************************** + * Name: cxd56_spi_pincontrol + * + * Description: + * Configure the SPI pin + * + * Input Parameter: + * on - true: enable pin, false: disable pin + * + ****************************************************************************/ + +static void cxd56_spi_pincontrol(int ch, bool on) +{ + switch (ch) + { +#ifdef CONFIG_CXD56_SPI0 + case 0: +#ifdef CONFIG_CXD56_SPI0_PINMAP_DEBUG_UART + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI0); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI0_GPIO); + } +#endif +#ifdef CONFIG_CXD56_SPI0_PINMAP_SPIFLASH + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI1A_SPI0); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI1A_GPIO); + } +#endif + break; +#endif + +#ifdef CONFIG_CXD56_SPI3 + case 3: + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_GPIO); + } +#ifdef CONFIG_CXD56_SPI3_CS0 + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS0_X); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS0_X_GPIO); + } +#endif +#ifdef CONFIG_CXD56_SPI3_CS1 + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS1_X); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS1_X_GPIO); + } +#endif +#ifdef CONFIG_CXD56_SPI3_CS2 + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS2_X); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI3_CS2_X_GPIO); + } +#endif + break; +#endif + +#ifdef CONFIG_CXD56_SPI4 + case 4: + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SPI4); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SPI4_GPIO); + } + break; +#endif + +#ifdef CONFIG_CXD56_SPI5 + case 5: +#ifdef CONFIG_CXD56_SPI5_PINMAP_EMMC + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_EMMCA_SPI5); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_EMMCA_GPIO); + } +#endif +#ifdef CONFIG_CXD56_SPI5_PINMAP_SDIO + if (on) + { + CXD56_PIN_CONFIGS(PINCONFS_SDIOA_SPI5); + } + else + { + CXD56_PIN_CONFIGS(PINCONFS_SDIOA_GPIO); + } +#endif + break; +#endif + default: + break; + } +} + +#ifdef CONFIG_CXD56_SPI4 + +/**************************************************************************** + * Name: spi4_colockchange + * + * Description: + * pm event callback for SPI4 + * + * Input Parameter: + * id - PM callback ID + * + ****************************************************************************/ + +static int spi4_colockchange(uint8_t id) +{ + FAR struct cxd56_spidev_s *priv = &g_spi4dev; + + switch (id) + { + case CXD56_PM_CALLBACK_ID_CLK_CHG_END: + spi_setfrequency(&priv->spidev, priv->frequency); + break; + default: + break; + } + + return 0; +} + +#endif + +#ifdef CONFIG_CXD56_SPI5 + +/**************************************************************************** + * Name: spi5_colockchange + * + * Description: + * pm event callback for SPI5 + * + * Input Parameter: + * id - PM callback ID + * + ****************************************************************************/ + +static int spi5_colockchange(uint8_t id) +{ + FAR struct cxd56_spidev_s *priv = &g_spi5dev; + + switch (id) + { + case CXD56_PM_CALLBACK_ID_CLK_CHG_END: + spi_setfrequency(&priv->spidev, priv->frequency); + break; + default: + break; + } + + return 0; +} + +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cxd56_spibus_initialize + * + * Description: + * Initialize the selected SPI port + * + * Input Parameter: + * port - Port number + * + * Returned Value: + * Valid SPI device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct spi_dev_s *cxd56_spibus_initialize(int port) +{ + FAR struct cxd56_spidev_s *priv; + uint32_t regval; + int i; + + switch (port) + { +#ifdef CONFIG_CXD56_SPI4 + case 4: + priv = &g_spi4dev; + if (!priv->initialized) + { + cxd56_pm_register_callback(PM_CLOCK_APP_SPI, spi4_colockchange); + } + break; +#endif + +#ifdef CONFIG_CXD56_SPI5 + case 5: + priv = &g_spi5dev; + if (!priv->initialized) + { + cxd56_pm_register_callback(PM_CLOCK_APP_WSPI, spi5_colockchange); + } + break; +#endif + +#ifdef CONFIG_CXD56_SPI0 + case 0: + priv = &g_spi0dev; + break; +#endif + +#ifdef CONFIG_CXD56_SPI3 + case 3: + priv = &g_spi3dev; + break; +#endif + default: + return NULL; + } + + /* If already initialized */ + + if (priv->initialized) + { + return &priv->spidev; + } + + /* Configure clocking */ + + cxd56_spi_clock_enable(port); + priv->spibasefreq = cxd56_get_spi_baseclock(port); + + /* DMA settings */ + +#if defined(CONFIG_CXD56_DMAC_SPI4_TX) || defined(CONFIG_CXD56_DMAC_SPI4_RX) + if (port == 4) + { +#if defined(CONFIG_CXD56_DMAC_SPI4_TX) + priv->txconfig.channel_cfg = CXD56_DMA_PERIPHERAL_SPI4_TX; + priv->txdmach = cxd56_dmachannel(CONFIG_CXD56_DMAC_SPI4_TX_CH, + CONFIG_CXD56_DMAC_SPI4_TX_MAXSIZE); + if (priv->txdmach == NULL) + { + return NULL; + } +#endif +#if defined(CONFIG_CXD56_DMAC_SPI4_RX) + priv->rxconfig.channel_cfg = CXD56_DMA_PERIPHERAL_SPI4_RX; + priv->rxdmach = cxd56_dmachannel(CONFIG_CXD56_DMAC_SPI4_RX_CH, + CONFIG_CXD56_DMAC_SPI4_RX_MAXSIZE); + if (priv->rxdmach == NULL) + { + return NULL; + } +#endif + sem_init(&priv->dmasem, 0, 0); + } +#endif + +#if defined(CONFIG_CXD56_DMAC_SPI5_TX) || defined(CONFIG_CXD56_DMAC_SPI5_RX) + if (port == 5) + { +#if defined(CONFIG_CXD56_DMAC_SPI5_TX) + priv->txconfig.channel_cfg = CXD56_DMA_PERIPHERAL_SPI5_TX; + priv->txdmach = cxd56_dmachannel(CONFIG_CXD56_DMAC_SPI5_TX_CH, + CONFIG_CXD56_DMAC_SPI5_TX_MAXSIZE); + if (priv->txdmach == NULL) + { + return NULL; + } +#endif +#if defined(CONFIG_CXD56_DMAC_SPI5_RX) + priv->rxconfig.channel_cfg = CXD56_DMA_PERIPHERAL_SPI5_RX; + priv->rxdmach = cxd56_dmachannel(CONFIG_CXD56_DMAC_SPI5_RX_CH, + CONFIG_CXD56_DMAC_SPI5_RX_MAXSIZE); + if (priv->rxdmach == NULL) + { + return NULL; + } +#endif + sem_init(&priv->dmasem, 0, 0); + } +#endif + + /* CS control */ + + if ((port == 0) || (port == 3)) + { + spi_putreg(priv, CXD56_SPI_CSMODE_OFFSET, 1); + spi_putreg(priv, CXD56_SPI_CS_OFFSET, 1); + } + + /* Configure pin */ + + cxd56_spi_pincontrol(port, true); + + /* Configure 8-bit SPI mode */ + + spi_putreg(priv, CXD56_SPI_CR0_OFFSET, SPI_CR0_DSS_8BIT | SPI_CR0_FRF_SPI); + + /* Disable SPI and all interrupts (we'll poll for all data) */ + + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, 0); + spi_putreg(priv, CXD56_SPI_IMSC_OFFSET, 0); + + /* Clear interrupts */ + + spi_putreg(priv, CXD56_SPI_ICR_OFFSET, 0x3); + + /* Set the initial SPI configuration */ + + priv->frequency = 0; + priv->nbits = 8; + priv->mode = SPIDEV_MODE0; + + /* Select a default frequency of approx. 400KHz */ + + spi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + sem_init(&priv->exclsem, 0, 1); + +#ifdef CONFIG_CXD56_SPI3_SCUSEQ + /* Enable the SPI, but not enable port 3 when SCU support enabled. + * Because this enabler will be controlled by SCU. + */ + + if (port != 3) + { +#endif + regval = spi_getreg(priv, CXD56_SPI_CR1_OFFSET); + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval | SPI_CR1_SSE); +#ifdef CONFIG_CXD56_SPI3_SCUSEQ + } +#endif + + for (i = 0; i < CXD56_SPI_FIFOSZ; i++) + { + (void)spi_getreg(priv, CXD56_SPI_DR_OFFSET); + } + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(port); + + /* Set a initialized flag */ + + priv->initialized = 1; + + return &priv->spidev; +} + +/**************************************************************************** + * Name: spi_flush + * + * Description: + * Flush and discard any words left in the RX fifo. This can be done + * after a device is deselected if you worry about such things. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +void spi_flush(FAR struct spi_dev_s *dev) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t regval = 0; + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + if (priv->port == 3) + { + /* Enable SPI HW */ + + regval = spi_getreg(priv, CXD56_SPI_CR1_OFFSET); + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval | SPI_CR1_SSE); + } + + /* Wait for the TX FIFO not full indication */ + + while (!(spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_TNF)); + spi_putreg(priv, CXD56_SPI_DR_OFFSET, 0xff); + + /* Wait until TX FIFO and TX shift buffer are empty */ + + while (spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_BSY); + + /* Wait until RX FIFO is not empty */ + + while (!(spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_RNE)); + + /* Then read and discard bytes until the RX FIFO is empty */ + + do + { + (void)spi_getreg(priv, CXD56_SPI_DR_OFFSET); + } + while (spi_getreg(priv, CXD56_SPI_SR_OFFSET) & SPI_SR_RNE); + + if (priv->port == 3) + { + /* Restore SPI HW state */ + + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval); + } + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); +} + +#ifdef CONFIG_CXD56_DMAC + +/**************************************************************************** + * Name: spi_dmaexchange + * + * Description: + * Exchange a block of data from SPI using DMA + * + ****************************************************************************/ + +static void spi_dmaexchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)dev; + uint32_t regval = 0; + + DEBUGASSERT(priv && priv->spibase); + DEBUGASSERT(txbuffer || rxbuffer); + + /* Disable clock gating (clock enable) */ + + cxd56_spi_clock_gate_disable(priv->port); + + if (priv->port == 3) + { + /* Enable SPI HW */ + + regval = spi_getreg(priv, CXD56_SPI_CR1_OFFSET); + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval | SPI_CR1_SSE); + } + + /* Setup DMAs */ + + if (txbuffer) + { + spi_dmatxsetup(priv, txbuffer, nwords); + } + + if (rxbuffer) + { + spi_dmarxsetup(priv, rxbuffer, nwords); + } + + /* Start the DMAs */ + + if (rxbuffer) + { + cxd56_dmastart(priv->rxdmach, spi_dmarxcallback, priv); + } + + if (txbuffer) + { + cxd56_dmastart(priv->txdmach, spi_dmatxcallback, priv); + } + + /* Then wait for each to complete */ + + if (txbuffer && rxbuffer) + { + spi_dmatrxwait(priv); + } + else if (txbuffer) + { + spi_dmatxwait(priv); + } + else if (rxbuffer) + { + spi_dmarxwait(priv); + } + + if (priv->port == 3) + { + /* Restore SPI HW state */ + + spi_putreg(priv, CXD56_SPI_CR1_OFFSET, regval); + } + + /* Enable clock gating (clock disable) */ + + cxd56_spi_clock_gate_enable(priv->port); +} + +#ifndef CONFIG_SPI_EXCHANGE + +/**************************************************************************** + * Name: spi_dmasndblock + * + * Description: + * Send a block of data on SPI using DMA + * + ****************************************************************************/ + +static void spi_dmasndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, + size_t nwords) +{ + spi_dmaexchange(dev, buffer, NULL, nwords); +} + +/**************************************************************************** + * Name: spi_dmarecvblock + * + * Description: + * Receive a block of data on SPI using DMA + * + ****************************************************************************/ + +static void spi_dmarecvblock(FAR struct spi_dev_s *dev, + FAR const void *buffer, + size_t nwords) +{ + spi_dmaexchange(dev, NULL, buffer, nwords); +} +#endif + +/**************************************************************************** + * Name: spi_dmatxcallback + * + * Description: + * Called when the TX DMA completes + * + ****************************************************************************/ + +static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t status, void *data) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)data; + + /* Wake-up the SPI driver */ + + if ((status & CXD56_DMA_INTR_ERR) != 0) + { + spierr("dma error\n"); + } + + (void)sem_post(&priv->dmasem); +} + +/**************************************************************************** + * Name: spi_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + ****************************************************************************/ + +static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t status, void *data) +{ + FAR struct cxd56_spidev_s *priv = (FAR struct cxd56_spidev_s *)data; + + /* Wake-up the SPI driver */ + + if ((status & CXD56_DMA_INTR_ERR) != 0) + { + spierr("dma error\n"); + } + + (void)sem_post(&priv->dmasem); +} + +/**************************************************************************** + * Name: spi_dmatxsetup + * + * Description: + * Setup to perform TX DMA + * + ****************************************************************************/ + +static void spi_dmatxsetup(FAR struct cxd56_spidev_s *priv, + FAR const void *txbuffer, size_t nwords) +{ + uint32_t dst; + uint32_t val; + + val = spi_getreg(priv, CXD56_SPI_DMACR_OFFSET); + val |= SPI_DMACR_TXDMAE; + spi_putreg(priv, CXD56_SPI_DMACR_OFFSET, val); + + dst = (priv->spibase + CXD56_SPI_DR_OFFSET) & 0x03ffffffu; + cxd56_txdmasetup(priv->txdmach, (uintptr_t)dst, (uintptr_t)txbuffer, + nwords, priv->txconfig); +} + +/**************************************************************************** + * Name: spi_dmarxsetup + * + * Description: + * Setup to perform RX DMA + * + ****************************************************************************/ + +static void spi_dmarxsetup(FAR struct cxd56_spidev_s *priv, + FAR const void *rxbuffer, size_t nwords) +{ + uint32_t src; + uint32_t val; + + val = spi_getreg(priv, CXD56_SPI_DMACR_OFFSET); + val |= SPI_DMACR_RXDMAE; + spi_putreg(priv, CXD56_SPI_DMACR_OFFSET, val); + + src = (priv->spibase + CXD56_SPI_DR_OFFSET) & 0x03ffffffu; + cxd56_rxdmasetup(priv->rxdmach, (uintptr_t)src, (uintptr_t)rxbuffer, + nwords, priv->rxconfig); +} + +/**************************************************************************** + * Name: spi_dmatxwait + * + * Description: + * Wait for TX DMA to complete. + * + ****************************************************************************/ + +static void spi_dmatxwait(FAR struct cxd56_spidev_s *priv) +{ + uint32_t val; + + if (sem_wait(&priv->dmasem) != OK) + { + spierr("dma error\n"); + } + + cxd56_dmastop(priv->txdmach); + + val = spi_getreg(priv, CXD56_SPI_DMACR_OFFSET); + val &= ~SPI_DMACR_TXDMAE; + spi_putreg(priv, CXD56_SPI_DMACR_OFFSET, val); +} + +/**************************************************************************** + * Name: spi_dmarxwait + * + * Description: + * Wait for RX DMA to complete. + * + ****************************************************************************/ + +static void spi_dmarxwait(FAR struct cxd56_spidev_s *priv) +{ + uint32_t val; + + if (sem_wait(&priv->dmasem) != OK) + { + spierr("dma error\n"); + } + + cxd56_dmastop(priv->rxdmach); + + val = spi_getreg(priv, CXD56_SPI_DMACR_OFFSET); + val &= ~SPI_DMACR_RXDMAE; + spi_putreg(priv, CXD56_SPI_DMACR_OFFSET, val); +} + +/**************************************************************************** + * Name: spi_dmatrxwait + * + * Description: + * Wait for TX RX DMA to complete. + * + ****************************************************************************/ + +static void spi_dmatrxwait(FAR struct cxd56_spidev_s *priv) +{ + uint32_t val; + + if (sem_wait(&priv->dmasem) != OK) + { + spierr("dma error\n"); + } + + if (sem_wait(&priv->dmasem) != OK) + { + spierr("dma error\n"); + } + + cxd56_dmastop(priv->txdmach); + cxd56_dmastop(priv->rxdmach); + + val = spi_getreg(priv, CXD56_SPI_DMACR_OFFSET); + val &= ~(SPI_DMACR_RXDMAE | SPI_DMACR_TXDMAE); + spi_putreg(priv, CXD56_SPI_DMACR_OFFSET, val); +} + +#endif + +#endif diff --git a/arch/arm/src/cxd56xx/cxd56_spi.h b/arch/arm/src/cxd56xx/cxd56_spi.h new file mode 100644 index 0000000000..c33ba9f00d --- /dev/null +++ b/arch/arm/src/cxd56xx/cxd56_spi.h @@ -0,0 +1,224 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_spi.h + * + * Copyright (C) 2012, 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CXD56_SPI_H +#define __ARCH_ARM_SRC_CXD56XX_CXD56_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "hardware/cxd56_spi.h" + +#if defined(CONFIG_CXD56_SPI0) || defined(CONFIG_CXD56_SPI3) || \ + defined(CONFIG_CXD56_SPI4) || defined(CONFIG_CXD56_SPI5) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* This header file defines interfaces to common SPI logic. + * To use this common SPI logic on your board: + * + * 1. Provide logic in cxd56_boardinitialize() to configure SPI chip select pins. + * 2. Provide cxd56_spi0/1select() and cxd56_spi0/1status() functions in your + * board-specific logic. These functions will perform chip selection + * and status operations using GPIOs in the way your board is configured. + * 3. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide + * cxd56_spi0/1cmddata() functions in your board-specific logic. These + * functions will perform cmd/data selection operations using GPIOs in the + * way your board is configured. + * 4. Your low level board initialization logic should call cxd56_spibus_initialize. + * 5. The handle returned by cxd56_spibus_initialize() may then be used to bind the + * SPI driver to higher level logic (e.g., calling mmcsd_spislotinitialize(), + * for example, will bind the SPI driver to the SPI MMC/SD driver). + */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cxd56_spibus_initialize + * + * Description: + * Initialize the selected SPI port + * + * Input Parameter: + * port - Port number + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct spi_dev_s *cxd56_spibus_initialize(int port); + +/**************************************************************************** + * Name: cxd56_spiXselect, cxd56_spiXstatus, and cxd56_spiXcmddata + * + * Description: + * These functions must be provided in your board-specific logic. The + * cxd56_spi0/1select functions will perform chip selection and the + * cxd56_spi0/1status will perform status operations using GPIOs in the way your + * board is configured. + * + * If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, then + * cxd56_spi0/1cmddata must also be provided. This functions performs cmd/data + * selection operations using GPIOs in the way your board is configured. + * + ****************************************************************************/ + +#ifdef CONFIG_CXD56_SPI0 +void cxd56_spi0select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t cxd56_spi0status(FAR struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int cxd56_spi0cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#ifdef CONFIG_CXD56_SPI3 +void cxd56_spi3select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t cxd56_spi3status(FAR struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int cxd56_spi3cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#ifdef CONFIG_CXD56_SPI4 +void cxd56_spi4select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t cxd56_spi4status(FAR struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int cxd56_spi4cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#ifdef CONFIG_CXD56_SPI5 +void cxd56_spi5select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected); +uint8_t cxd56_spi5status(FAR struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int cxd56_spi5cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +/**************************************************************************** + * Name: spi_flush + * + * Description: + * Flush and discard any words left in the RX fifo. This can be called + * from spi0/1select after a device is deselected (if you worry about such + * things). + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +void spi_flush(FAR struct spi_dev_s *dev); + +/**************************************************************************** + * Name: cxd56_spiXregister + * + * Description: + * If the board supports a card detect callback to inform the SPI-based + * MMC/SD driver when an SD card is inserted or removed, then + * CONFIG_SPI_CALLBACK should be defined and the following function(s) must + * must be implemented. These functions implements the registercallback + * method of the SPI interface (see include/nuttx/spi/spi.h for details) + * + * Input Parameters: + * dev - Device-specific state data + * callback - The function to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CALLBACK +#ifdef CONFIG_CXD56_SPI0 +int cxd56_spi0register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_CXD56_SPI3 +int cxd56_spi3register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_CXD56_SPI4 +int cxd56_spi4register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif + +#ifdef CONFIG_CXD56_SPI5 +int cxd56_spi5register(FAR struct spi_dev_s *dev, spi_mediachange_t callback, + FAR void *arg); +#endif +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_CXD56_SPI0/3/4/5 */ +#endif /* __ARCH_ARM_SRC_CXD56XX_CXD56_SPI_H */ diff --git a/arch/arm/src/cxd56xx/hardware/cxd56_dmac_common.h b/arch/arm/src/cxd56xx/hardware/cxd56_dmac_common.h new file mode 100644 index 0000000000..52aec4b8b9 --- /dev/null +++ b/arch/arm/src/cxd56xx/hardware/cxd56_dmac_common.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/cxd56_dmac_common.h + * + * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file cxd56_dmac_common.h + */ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_COMMON_H +#define __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_COMMON_H + +#include + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/* DMA_HANDLE provides an opaque are reference that can be used to represent a DMA + * channel. + */ + +typedef FAR void *DMA_HANDLE; + +/* Description: + * This is the type of the callback that is used to inform the user of the the + * completion of the DMA. + * + * Input Parameters: + * handle - Refers tot he DMA channel or stream + * status - A bit encoded value that provides the completion status. See the + * DMASTATUS_* definitions above. + * arg - A user-provided value that was provided when cxd56_dmastart() was + * called. + */ + +typedef void (*dma_callback_t)(DMA_HANDLE handle, uint8_t status, void *arg); + +/* Type of 'config' argument passed to cxd56_rxdmasetup() and cxd56_txdmasetup. + * See CXD56_DMA_* encodings above. If these encodings exceed 16-bits, then this + * should be changed to a uint32_t. + */ + +typedef struct { + uint16_t channel_cfg; + uint8_t dest_width; + uint8_t src_width; +} dma_config_t; + +#endif /* __ARCH_ARM_SRC_CXD56XX_CXD56_DMAC_COMMON_H */ diff --git a/arch/arm/src/cxd56xx/hardware/cxd56_rtc.h b/arch/arm/src/cxd56xx/hardware/cxd56_rtc.h new file mode 100644 index 0000000000..00aaa82c3a --- /dev/null +++ b/arch/arm/src/cxd56xx/hardware/cxd56_rtc.h @@ -0,0 +1,140 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/hardware/cxd56_rtc.h + * + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sony Semiconductor Solutions Corporation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_RTC_H +#define __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_RTC_H + +/**************************************************************************************** + * Included Files + ****************************************************************************************/ + +#include "hardware/cxd5602_memorymap.h" + +/**************************************************************************************** + * Pre-processor Definitions + ****************************************************************************************/ + +/* Register offsets *********************************************************************/ + +#define RTC_WRREGPOSTCNT (0x0) +#define RTC_WRREGPRECNT (0x4) +#define RTC_WRREGREQ (0x8) +#define RTC_WRINTPOSTCNT (0x10) +#define RTC_WRINTPRECNT (0x14) +#define RTC_WRINTCTRL (0x18) +#define RTC_WRINTCLR (0x1c) +#define RTC_OFFSETVAL (0x20) +#define RTC_OFFSETREQ (0x24) +#define RTC_RDREQ (0x30) +#define RTC_RDPOSTCNT (0x34) +#define RTC_RDPRECNT (0x38) +#define RTC_RTPOSTCNT (0x40) +#define RTC_RTPRECNT (0x44) +#define RTC_SETALMPOSTCNT(id) (0x50 + ((id) * 0x8)) +#define RTC_SETALMPRECNT(id) (0x54 + ((id) * 0x8)) +#define RTC_SETALMPOSTCNT0 (0x50) +#define RTC_SETALMPRECNT0 (0x54) +#define RTC_SETALMPOSTCNT1 (0x58) +#define RTC_SETALMPRECNT1 (0x5c) +#define RTC_SETALMPOSTCNT2 (0x60) +#define RTC_SETALMPRECNT2 (0x64) +#define RTC_ALMCLR (0x70) +#define RTC_ALMOUTEN(id) (0x74 + ((id) * 0x4)) +#define RTC_ALMOUTEN0 (0x74) +#define RTC_ALMOUTEN1 (0x78) +#define RTC_ALMOUTEN2 (0x7c) +#define RTC_ALMFLG (0x80) +#define RTC_DBGSETALMPOSTCNT0 (0x90) +#define RTC_DBGSETALMPRECNT0 (0x94) +#define RTC_DBGSETALMPOSTCNT1 (0x98) +#define RTC_DBGSETALMPRECNT1 (0x9c) +#define RTC_DBGSETALMPOSTCNT2 (0xa0) +#define RTC_DBGSETALMPRECNT2 (0xa4) + +/* Register Addresses *******************************************************************/ + +#define CXD56_RTC0_WRREGPOSTCNT (CXD56_RTC0_BASE + RTC_WRREGPOSTCNT) +#define CXD56_RTC0_WRREGPRECNT (CXD56_RTC0_BASE + RTC_WRREGPRECNT) +#define CXD56_RTC0_WRREGREQ (CXD56_RTC0_BASE + RTC_WRREGREQ) +#define CXD56_RTC0_WRINTPOSTCNT (CXD56_RTC0_BASE + RTC_WRINTPOSTCNT) +#define CXD56_RTC0_WRINTPRECNT (CXD56_RTC0_BASE + RTC_WRINTPRECNT) +#define CXD56_RTC0_WRINTCTRL (CXD56_RTC0_BASE + RTC_WRINTCTRL) +#define CXD56_RTC0_WRINTCLR (CXD56_RTC0_BASE + RTC_WRINTCLR) +#define CXD56_RTC0_OFFSETVAL (CXD56_RTC0_BASE + RTC_OFFSETVAL) +#define CXD56_RTC0_OFFSETREQ (CXD56_RTC0_BASE + RTC_OFFSETREQ) +#define CXD56_RTC0_RDREQ (CXD56_RTC0_BASE + RTC_RDREQ) +#define CXD56_RTC0_RDPOSTCNT (CXD56_RTC0_BASE + RTC_RDPOSTCNT) +#define CXD56_RTC0_RDPRECNT (CXD56_RTC0_BASE + RTC_RDPRECNT) +#define CXD56_RTC0_RTPOSTCNT (CXD56_RTC0_BASE + RTC_RTPOSTCNT) +#define CXD56_RTC0_RTPRECNT (CXD56_RTC0_BASE + RTC_RTPRECNT) +#define CXD56_RTC0_SETALMPOSTCNT(id) (CXD56_RTC0_BASE + RTC_SETALMPOSTCNT(id)) +#define CXD56_RTC0_SETALMPRECNT(id) (CXD56_RTC0_BASE + RTC_SETALMPRECNT(id)) +#define CXD56_RTC0_ALMCLR (CXD56_RTC0_BASE + RTC_ALMCLR) +#define CXD56_RTC0_ALMOUTEN(id) (CXD56_RTC0_BASE + RTC_ALMOUTEN(id)) +#define CXD56_RTC0_ALMFLG (CXD56_RTC0_BASE + RTC_ALMFLG) + +/* Register bit definitions *************************************************************/ + +/* Flag/Clear Register */ + +#define RTCREG_ALM0_FLAG_MASK (1u << 0) +#define RTCREG_ALM1_FLAG_MASK (1u << 1) +#define RTCREG_ALM2_FLAG_MASK (1u << 2) +#define RTCREG_ALM0_ERR_FLAG_MASK (1u << 16) +#define RTCREG_ALM1_ERR_FLAG_MASK (1u << 17) + +#define RTCREG_ALM0_MASK (RTCREG_ALM0_FLAG_MASK | RTCREG_ALM0_ERR_FLAG_MASK) +#define RTCREG_ALM1_MASK (RTCREG_ALM1_FLAG_MASK | RTCREG_ALM1_ERR_FLAG_MASK) +#define RTCREG_ALM2_MASK (RTCREG_ALM2_FLAG_MASK) + +/* Write Request */ + +#define RTCREG_WREQ_BUSYA_MASK (1u << 0) +#define RTCREG_WREQ_BUSYB_MASK (1u << 1) + +/* Alarm Request */ + +#define RTCREG_ASET_BUSY_MASK (1u << 16) + +/* Alarm Enable */ + +#define RTCREG_ALM_EN_MASK (1u << 0) +#define RTCREG_ALM_BUSY_MASK (1u << 8) +#define RTCREG_ALM_DBG_MASK (1u << 15) +#define RTCREG_ALM_ERREN_MASK (1u << 16) +#define RTCREG_ALM_ERRDBG_MASK (1u << 31) + + +#endif /* __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_RTC_H */ diff --git a/arch/arm/src/cxd56xx/hardware/cxd56_spi.h b/arch/arm/src/cxd56xx/hardware/cxd56_spi.h new file mode 100644 index 0000000000..5704310745 --- /dev/null +++ b/arch/arm/src/cxd56xx/hardware/cxd56_spi.h @@ -0,0 +1,211 @@ +/**************************************************************************** + * arch/arm/src/cxd56xx/hardware/cxd56_spi.h + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Copyright 2018 Sony Semiconductor Solutions Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_SPI_H +#define __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 8 frame FIFOs for both transmit and receive */ + +#define CXD56_SPI_FIFOSZ 8 + +/* Register offsets *********************************************************/ + +#define CXD56_SPI_CR0_OFFSET 0x0000 /* Control Register 0 */ +#define CXD56_SPI_CR1_OFFSET 0x0004 /* Control Register 1 */ +#define CXD56_SPI_DR_OFFSET 0x0008 /* Data Register */ +#define CXD56_SPI_SR_OFFSET 0x000c /* Status Register */ +#define CXD56_SPI_CPSR_OFFSET 0x0010 /* Clock Prescale Register */ +#define CXD56_SPI_IMSC_OFFSET 0x0014 /* Interrupt Mask Set and Clear Reg */ +#define CXD56_SPI_RIS_OFFSET 0x0018 /* Raw Interrupt Status Register */ +#define CXD56_SPI_MIS_OFFSET 0x001c /* Masked Interrupt Status Register */ +#define CXD56_SPI_ICR_OFFSET 0x0020 /* Interrupt Clear Register */ +#define CXD56_SPI_DMACR_OFFSET 0x0024 /* DMA Control Register */ +#define CXD56_SPI_CSMODE_OFFSET 0x0090 /* CS control mode */ +#define CXD56_SPI_CS_OFFSET 0x0094 /* CS output */ +#define CXD56_SPI_SLAVETYPE_OFFSET 0x0098 /* Slave type */ + +/* Register addresses *******************************************************/ + +#define CXD56_SPI4_CR0 (CXD56_IMG_SPI_BASE+CXD56_SPI_CR0_OFFSET) +#define CXD56_SPI4_CR1 (CXD56_IMG_SPI_BASE+CXD56_SPI_CR1_OFFSET) +#define CXD56_SPI4_DR (CXD56_IMG_SPI_BASE+CXD56_SPI_DR_OFFSET) +#define CXD56_SPI4_SR (CXD56_IMG_SPI_BASE+CXD56_SPI_SR_OFFSET) +#define CXD56_SPI4_CPSR (CXD56_IMG_SPI_BASE+CXD56_SPI_CPSR_OFFSET) +#define CXD56_SPI4_IMSC (CXD56_IMG_SPI_BASE+CXD56_SPI_IMSC_OFFSET) +#define CXD56_SPI4_RIS (CXD56_IMG_SPI_BASE+CXD56_SPI_RIS_OFFSET) +#define CXD56_SPI4_MIS (CXD56_IMG_SPI_BASE+CXD56_SPI_MIS_OFFSET) +#define CXD56_SPI4_ICR (CXD56_IMG_SPI_BASE+CXD56_SPI_ICR_OFFSET) +#define CXD56_SPI4_DMACR (CXD56_IMG_SPI_BASE+CXD56_SPI_DMACR_OFFSET) + +#define CXD56_SPI5_CR0 (CXD56_IMG_WSPI_BASE+CXD56_SPI_CR0_OFFSET) +#define CXD56_SPI5_CR1 (CXD56_IMG_WSPI_BASE+CXD56_SPI_CR1_OFFSET) +#define CXD56_SPI5_DR (CXD56_IMG_WSPI_BASE+CXD56_SPI_DR_OFFSET) +#define CXD56_SPI5_SR (CXD56_IMG_WSPI_BASE+CXD56_SPI_SR_OFFSET) +#define CXD56_SPI5_CPSR (CXD56_IMG_WSPI_BASE+CXD56_SPI_CPSR_OFFSET) +#define CXD56_SPI5_IMSC (CXD56_IMG_WSPI_BASE+CXD56_SPI_IMSC_OFFSET) +#define CXD56_SPI5_RIS (CXD56_IMG_WSPI_BASE+CXD56_SPI_RIS_OFFSET) +#define CXD56_SPI5_MIS (CXD56_IMG_WSPI_BASE+CXD56_SPI_MIS_OFFSET) +#define CXD56_SPI5_ICR (CXD56_IMG_WSPI_BASE+CXD56_SPI_ICR_OFFSET) +#define CXD56_SPI5_DMACR (CXD56_IMG_WSPI_BASE+CXD56_SPI_DMACR_OFFSET) + +#define CXD56_SPI0_CR0 (CXD56_SPIM_BASE+CXD56_SPI_CR0_OFFSET) +#define CXD56_SPI0_CR1 (CXD56_SPIM_BASE+CXD56_SPI_CR1_OFFSET) +#define CXD56_SPI0_DR (CXD56_SPIM_BASE+CXD56_SPI_DR_OFFSET) +#define CXD56_SPI0_SR (CXD56_SPIM_BASE+CXD56_SPI_SR_OFFSET) +#define CXD56_SPI0_CPSR (CXD56_SPIM_BASE+CXD56_SPI_CPSR_OFFSET) +#define CXD56_SPI0_IMSC (CXD56_SPIM_BASE+CXD56_SPI_IMSC_OFFSET) +#define CXD56_SPI0_RIS (CXD56_SPIM_BASE+CXD56_SPI_RIS_OFFSET) +#define CXD56_SPI0_MIS (CXD56_SPIM_BASE+CXD56_SPI_MIS_OFFSET) +#define CXD56_SPI0_ICR (CXD56_SPIM_BASE+CXD56_SPI_ICR_OFFSET) +#define CXD56_SPI0_DMACR (CXD56_SPIM_BASE+CXD56_SPI_DMACR_OFFSET) +#define CXD56_SPI0_CSMODE (CXD56_SPIM_BASE+CXD56_SPI_CSMODE_OFFSET) +#define CXD56_SPI0_CS (CXD56_SPIM_BASE+CXD56_SPI_CS_OFFSET) +#define CXD56_SPI0_SLAVETYPE (CXD56_SPIM_BASE+CXD56_SPI_SLAVETYPE_OFFSET) + +#define CXD56_SPI3_CR0 (CXD56_SCU_SPI_BASE+CXD56_SPI_CR0_OFFSET) +#define CXD56_SPI3_CR1 (CXD56_SCU_SPI_BASE+CXD56_SPI_CR1_OFFSET) +#define CXD56_SPI3_DR (CXD56_SCU_SPI_BASE+CXD56_SPI_DR_OFFSET) +#define CXD56_SPI3_SR (CXD56_SCU_SPI_BASE+CXD56_SPI_SR_OFFSET) +#define CXD56_SPI3_CPSR (CXD56_SCU_SPI_BASE+CXD56_SPI_CPSR_OFFSET) +#define CXD56_SPI3_IMSC (CXD56_SCU_SPI_BASE+CXD56_SPI_IMSC_OFFSET) +#define CXD56_SPI3_RIS (CXD56_SCU_SPI_BASE+CXD56_SPI_RIS_OFFSET) +#define CXD56_SPI3_MIS (CXD56_SCU_SPI_BASE+CXD56_SPI_MIS_OFFSET) +#define CXD56_SPI3_ICR (CXD56_SCU_SPI_BASE+CXD56_SPI_ICR_OFFSET) +#define CXD56_SPI3_DMACR (CXD56_SCU_SPI_BASE+CXD56_SPI_DMACR_OFFSET) +#define CXD56_SPI3_CSMODE (CXD56_SCU_SPI_BASE+CXD56_SPI_CSMODE_OFFSET) +#define CXD56_SPI3_CS (CXD56_SCU_SPI_BASE+CXD56_SPI_CS_OFFSET) +#define CXD56_SPI3_SLAVETYPE (CXD56_SCU_SPI_BASE+CXD56_SPI_SLAVETYPE_OFFSET) + +/* Register bit definitions *************************************************/ + +/* Control Register 0 */ + +#define SPI_CR0_DSS_SHIFT (0) /* Bits 0-3: DSS Data Size Select */ +#define SPI_CR0_DSS_MASK (15 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_4BIT (3 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_5BIT (4 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_6BIT (5 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_7BIT (6 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_8BIT (7 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_9BIT (8 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_10BIT (9 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_11BIT (10 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_12BIT (11 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_13BIT (12 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_14BIT (13 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_15BIT (14 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_DSS_16BIT (15 << SPI_CR0_DSS_SHIFT) +#define SPI_CR0_FRF_SHIFT (4) /* Bits 4-5: FRF Frame Format */ +#define SPI_CR0_FRF_MASK (3 << SPI_CR0_FRF_SHIFT) +#define SPI_CR0_FRF_SPI (0 << SPI_CR0_FRF_SHIFT) +#define SPI_CR0_FRF_TI (1 << SPI_CR0_FRF_SHIFT) +#define SPI_CR0_FRF_UWIRE (2 << SPI_CR0_FRF_SHIFT) +#define SPI_CR0_CPOL (1 << 6) /* Bit 6: Clock Out Polarity */ +#define SPI_CR0_CPHA (1 << 7) /* Bit 7: Clock Out Phase */ +#define SPI_CR0_SCR_SHIFT (8) /* Bits 8-15: Serial Clock Rate */ +#define SPI_CR0_SCR_MASK (0xff << SPI_CR0_SCR_SHIFT) + /* Bits 8-31: Reserved */ + +/* Control Register 1 */ + +#define SPI_CR1_LBM (1 << 0) /* Bit 0: Loop Back Mode */ +#define SPI_CR1_SSE (1 << 1) /* Bit 1: SPI Enable */ +#define SPI_CR1_MS (1 << 2) /* Bit 2: Master/Slave Mode */ +#define SPI_CR1_SOD (1 << 3) /* Bit 3: Slave Output Disable */ + /* Bits 4-31: Reserved */ + +/* Data Register */ + +#define SPI_DR_MASK (0xffff) /* Bits 0-15: Data */ + /* Bits 16-31: Reserved */ + +/* Status Register */ + +#define SPI_SR_TFE (1 << 0) /* Bit 0: Transmit FIFO Empty */ +#define SPI_SR_TNF (1 << 1) /* Bit 1: Transmit FIFO Not Full */ +#define SPI_SR_RNE (1 << 2) /* Bit 2: Receive FIFO Not Empty */ +#define SPI_SR_RFF (1 << 3) /* Bit 3: Receive FIFO Full */ +#define SPI_SR_BSY (1 << 4) /* Bit 4: Busy */ + /* Bits 5-31: Reserved */ + +/* Clock Prescale Register */ + +#define SPI_CPSR_DVSR_MASK (0xff) /* Bits 0-7: clock = SPI_PCLK/DVSR */ + /* Bits 8-31: Reserved */ + +/* Common format for interrupt control registers: + * + * Interrupt Mask Set and Clear Register (IMSC) + * Raw Interrupt Status Register (RIS) + * Masked Interrupt Status Register (MIS) + * Interrupt Clear Register (ICR) + */ + +#define SPI_INT_ROR (1 << 0) /* Bit 0: RX FIFO overrun */ +#define SPI_INT_RT (1 << 1) /* Bit 1: RX FIFO timeout */ +#define SPI_INT_RX (1 << 2) /* Bit 2: RX FIFO at least half full */ +#define SPI_INT_TX (1 << 3 ) /* Bit 3: TX FIFO at least half empy */ + /* Bits 4-31: Reserved */ + +/* DMA Control Register */ + +#define SPI_DMACR_RXDMAE (1 << 0) /* Bit 0: Receive DMA Enable */ +#define SPI_DMACR_TXDMAE (1 << 1) /* Bit 1: Transmit DMA Enable */ + /* Bits 2-31: Reserved */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* __ARCH_ARM_SRC_CXD56XX_CHIP_CXD56_SPI_H */ diff --git a/configs/spresense/nsh/defconfig b/configs/spresense/nsh/defconfig index 7160448d7f..e9d8f8089a 100644 --- a/configs/spresense/nsh/defconfig +++ b/configs/spresense/nsh/defconfig @@ -35,6 +35,8 @@ CONFIG_PREALLOC_WDOGS=16 CONFIG_RAM_SIZE=1572864 CONFIG_RAM_START=0x0d000000 CONFIG_RR_INTERVAL=200 +CONFIG_RTC=y +CONFIG_RTC_DRIVER=y CONFIG_SCHED_WAITPID=y CONFIG_SDCLONE_DISABLE=y CONFIG_SPI=y diff --git a/configs/spresense/src/cxd56_bringup.c b/configs/spresense/src/cxd56_bringup.c index fa02aacfd2..eb5e19408b 100644 --- a/configs/spresense/src/cxd56_bringup.c +++ b/configs/spresense/src/cxd56_bringup.c @@ -62,6 +62,11 @@ #include "cxd56_gpio.h" #include "cxd56_pinconfig.h" +#ifdef CONFIG_CXD56_RTC +#include +#include "cxd56_rtc.h" +#endif + #ifdef CONFIG_CXD56_CPUFIFO #include "cxd56_cpufifo.h" #endif @@ -161,6 +166,10 @@ int cxd56_bringup(void) wlock.count = 0; up_pm_acquire_wakelock(&wlock); +#ifdef CONFIG_RTC_DRIVER + rtc_initialize(0, cxd56_rtc_lowerhalf()); +#endif + cxd56_uart_initialize(); cxd56_timerisr_initialize(); diff --git a/configs/spresense/usbnsh/defconfig b/configs/spresense/usbnsh/defconfig index 1e6b218df2..da1dd2021e 100644 --- a/configs/spresense/usbnsh/defconfig +++ b/configs/spresense/usbnsh/defconfig @@ -46,7 +46,8 @@ CONFIG_PREALLOC_WDOGS=16 CONFIG_RAM_SIZE=1572864 CONFIG_RAM_START=0x0d000000 CONFIG_RR_INTERVAL=200 -CONFIG_SCHED_HPWORK=y +CONFIG_RTC=y +CONFIG_RTC_DRIVER=y CONFIG_SCHED_WAITPID=y CONFIG_SDCLONE_DISABLE=y CONFIG_SPI=y