nuttx/arch/arm/src/cxd56xx/cxd56_scu.c
SPRESENSE d560ce65ff cxd56xx: spresense: Add some improvements and fix bugs for Spresense board
- Add new functions of GNSS
- Support the lower PWM frequency
- Add CONFIG_CPUFREQ_RELEASE_LOCK
- Add high speed ADC support
- Add HPADC input gain configuration
- Add eMMC device
- Frame buffer support
- Fix SD/GNSS/sensor drivers not worked
- Build errors
- Fix nxstyle issues
2020-07-28 09:13:05 +02:00

3554 lines
86 KiB
C

/****************************************************************************
* arch/arm/src/cxd56xx/cxd56_scu.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 <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <nuttx/irq.h>
#include <nuttx/semaphore.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <arch/chip/scu.h>
#include "chip.h"
#include "arm_arch.h"
#include "cxd56_scufifo.h"
#include "cxd56_clock.h"
#include "cxd56_adc.h"
#ifdef CONFIG_CXD56_UDMAC
#include "cxd56_udmac.h"
#include <arch/chip/pm.h>
#endif
#include "hardware/cxd56_scu.h"
#include "hardware/cxd56_scuseq.h"
#include "hardware/cxd56_scufifo.h"
/* SCU firmware (iSoP) binary */
#ifdef CONFIG_CXD56_HPADC0_HIGHSPEED
#include "hardware/cxd5602_isop_hadc0_highspeed.h"
#else
#include "hardware/cxd5602_isop.h"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_CXD56_SCU_DEBUG_ERR
#define scuerr(fmt, ...) syslog(LOG_ERR, fmt, ## __VA_ARGS__)
#else
#define scuerr(x, ...)
#endif
#ifdef CONFIG_CXD56_SCU_DEBUG_WARN
#define scuwarn(fmt, ...) syslog(LOG_WARN, fmt, ## __VA_ARGS__)
#else
#define scuwarn(x, ...)
#endif
#ifdef CONFIG_CXD56_SCU_DEBUG_INFO
#define scuinfo(fmt, ...) syslog(LOG_INFO, fmt, ## __VA_ARGS__)
#else
#define scuinfo(x, ...)
#endif
/* Sequencer has 128 instruction area (16bits/inst)
* We allocate statically 12 instructions for each sequencers.
*/
#define INSTRUCTION_PER_SEQ 12
/* Sequencer request code */
#define REQ_DONE 3
#define REQ_SLEEP 4
#define REQ_STOP 5
/* Disable decimation by set 15 to ratio (N bit field) */
#define DECIMATION_OFF 15
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct ev_notify_s
{
int signo; /* Signal number */
int pid; /* Target PID */
struct scuev_arg_s *arg; /* Event argument */
struct scufifo_s *fifo; /* Reverse reference to FIFO */
};
struct wm_notify_s
{
int signo; /* Signal number */
int pid; /* Target PID */
struct scutimestamp_s *ts; /* Event argument */
struct scufifo_s *fifo; /* Reverse reference to FIFO */
};
/* SCU FIFO management structure */
struct scufifo_s
{
int8_t wid; /* FIFO ID (Write side) */
int8_t rid; /* FIFO ID (Read side) */
uint8_t type; /* Normal or Decimator */
int8_t mid; /* Combined MATHFUNC */
uint16_t start; /* Start of FIFO memory */
uint16_t size; /* Size of FIFO memory */
uint32_t interval; /* Sample interval used for timestamp calculation */
uint16_t adjust; /* Adjustment value used for timestamp calculation */
#ifdef CONFIG_CXD56_UDMAC
DMA_HANDLE dma; /* DMA for reading sensing data */
sem_t dmawait; /* Wait semaphore for DMA complete */
int dmaresult; /* DMA result */
#endif
};
/* Sequencer */
struct seq_s
{
int8_t id; /* Sequencer ID */
uint8_t type; /* Normal or Decimator */
uint8_t bustype; /* I2C[01] or SPI or ADC */
int8_t active; /* Indicate active status.
* When active, any APIs will be returned state
* error.
*/
uint8_t sample; /* Bytes per sample */
uint8_t rate; /* Sampling rate must be 2^rate (rate = 0 - 9) */
int8_t nr_fifos; /* For decimation, max 3 decimators can be used. */
struct scufifo_s *fifo; /* Owned FIFO (normal only) */
};
/* Decimation FIFO */
struct decimation_fifo_s
{
struct scufifo_s *fifo; /* Pointer to decimation FIFO */
uint8_t ratio; /* Decimation ratio */
uint8_t leveladj; /* Output data level adjustment */
uint8_t forcethrough; /* Force through */
};
/* Decimator, sub class of sequencer */
struct decimator_s
{
struct seq_s seq; /* Inherit, must be top of the structure */
struct decimation_fifo_s dfifo[3]; /* Decimation FIFO */
};
struct cxd56_scudev_s
{
sem_t syncwait; /* Semaphore for synchronize with SCU firmware */
sem_t syncexc; /* Semaphore for exclusive access to sync */
/* SCU hardware resource management bitmaps (1 = allocated) */
uint8_t decimators; /* Bitmap for decimators */
uint8_t sequencers; /* Bitmap for normal sequencers */
uint8_t mathfuncs; /* Bitmap for MATHFUNC */
uint8_t oneshot; /* Bitmap for Oneshots */
sem_t oneshotwait[3]; /* Semaphore for wait oneshot sequence is done */
#ifndef CONFIG_DISABLE_SIGNAL
struct ev_notify_s event[3]; /* MATHFUNC event notify */
struct wm_notify_s wm[14]; /* Watermark notify */
#endif
int currentreq;
};
struct coeff_addr_s
{
uint32_t s;
uint32_t c0;
uint32_t c1;
uint32_t c2;
uint32_t c3;
uint32_t c4;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* SCU hardware resource management *****************************************/
static int findzeroandset(FAR uint8_t *bitmap, int nbits);
static void bitmapclear(FAR uint8_t *bitmap, int bit);
static inline int8_t deci_alloc(void);
static inline void deci_free(int8_t sid);
static inline int8_t mathf_alloc(void);
static inline void mathf_free(int8_t mid);
static int8_t seq_alloc(void);
static inline void seq_free(int8_t sid);
static inline int8_t oneshot_alloc(void);
static inline void oneshot_free(int8_t tid);
static int seq_semtake(sem_t *id);
static void seq_semgive(sem_t *id);
static void seq_fifosetactive(FAR struct seq_s *seq, int fifoid);
static void seq_fifosetinactive(FAR struct seq_s *seq, int fifoid);
static int seq_fifoisactive(FAR struct seq_s *seq, int fifoid);
static int seq_isactive(FAR struct seq_s *seq);
/* Sequencer control ********************************************************/
static FAR struct seq_s *seq_new(void);
static FAR struct seq_s *deci_new(void);
static void seq_inhibitrequest(int req, bool set);
static void seq_sync(FAR struct seq_s *seq, int req);
static void seq_setproperty(int sid, int slave, int dest,
int offset, int len);
static void seq_setinst(int sid, int offset, int len);
static void seq_setbus(int sid, int bustype);
static void seq_setdataformat(int sid, int start, int size, int swap,
int sample);
static void seq_setmathwritevecelenum(int sid, uint8_t elem);
static void seq_setsignconversion(int sid, int sign);
static void seq_setstartmode(int sid, int mode);
static void seq_setstartinterval(int sid, int interval);
static void seq_setstartphase(int sid, int phase);
static void seq_startseq(int sid);
static void seq_stopseq(int sid);
static int seq_setadjustment(FAR struct seq_s *seq,
struct adjust_xyz_s *adj);
static int seq_setfilter(FAR struct scufifo_s *fifo, int pos,
struct iir_filter_s iir[2]);
static int seq_seteventnotifier(FAR struct scufifo_s *fifo,
struct scuev_notify_s *ev);
static void seq_offsetgainenable(int sid, bool enable);
static int seq_start(FAR struct seq_s *seq, int fifoid);
static int seq_stop(FAR struct seq_s *seq, int fifoid);
static int seq_setsamplingrate(FAR struct seq_s *seq, uint8_t samplingrate);
static int seq_fifoinit(FAR struct seq_s *seq, int fifoid, uint16_t fsize);
static void seq_fifofree(FAR struct scufifo_s *fifo);
static inline struct scufifo_s *seq_getfifo(FAR struct seq_s *seq,
int fifoid);
static void seq_setdecimation(int wid, uint8_t ratio, uint8_t leveladj,
uint8_t forcethrough);
static int seq_setwatermark(FAR struct seq_s *seq, int fifoid,
FAR struct scufifo_wm_s *wm);
#ifndef CONFIG_DISABLE_SIGNAL
static void convert_firsttimestamp(struct scutimestamp_s *tm,
uint16_t interval, uint16_t sample,
uint16_t adjust);
static void latest_timestamp(struct scufifo_s *fifo, uint32_t interval,
struct scutimestamp_s *tm, uint16_t *samples);
static void seq_gettimestamp(struct scufifo_s *fifo,
struct scutimestamp_s *tm);
#endif
static int seq_oneshot(int bustype, int slave, FAR uint16_t *inst,
uint32_t nr_insts, FAR uint8_t *buffer, int len);
static void seq_setfifomode(FAR struct seq_s *seq, int fifoid, int enable);
#ifdef CONFIG_CXD56_UDMAC
static void seq_fifodmadone(DMA_HANDLE handle, uint8_t status, void *arg);
#endif
static uint16_t seq_remakeinstruction(int bustype, uint16_t inst);
/* Mathfunction *************************************************************/
static void mathf_enable(int8_t mid, uint8_t wid);
static void mathf_disable(int8_t mid);
static inline void mathf_set_coeff(uint32_t caddr,
FAR struct iir_coeff_s *c);
static void mathf_setiirfilter(int mid, int n,
FAR struct iir_filter_s *filter);
/* Interrupt handlers *******************************************************/
static int seq_scuirqhandler(int irq, FAR void *context, FAR void *arg);
static void seq_handlefifointr(FAR struct cxd56_scudev_s *priv,
uint32_t intr);
static void seq_handlemathfintr(FAR struct cxd56_scudev_s *priv,
uint32_t intr);
static void seq_handleoneshot(FAR struct cxd56_scudev_s *priv,
uint32_t intr);
static void seq_handleisopdoneintr(FAR struct cxd56_scudev_s *priv,
uint32_t intr);
/****************************************************************************
* Private Data
****************************************************************************/
struct cxd56_scudev_s g_scudev;
/****************************************************************************
* Public Data
****************************************************************************/
/* SCU firmware (iSoP) */
extern const unsigned long scu_isopprog_array[];
extern const unsigned long sizeof_scu_isopprog_array;
/* XXX: Convert coefficiencies register address. */
#define __CADDR(m, n) \
{ \
.s = SCU_MATHFUNC_PARAM_##m##_##n, \
.c0 = SCU_MATHFUNC_PARAM_C0_##m##_##n##_MSB, \
.c1 = SCU_MATHFUNC_PARAM_C1_##m##_##n##_MSB, \
.c2 = SCU_MATHFUNC_PARAM_C2_##m##_##n##_MSB, \
.c3 = SCU_MATHFUNC_PARAM_C3_##m##_##n##_MSB, \
.c4 = SCU_MATHFUNC_PARAM_C4_##m##_##n##_MSB, \
}
static const struct coeff_addr_s g_caddrs[3][2] =
{
{
__CADDR(0, 0),
__CADDR(0, 1)
},
{
__CADDR(1, 0),
__CADDR(1, 1)
},
{
__CADDR(2, 0),
__CADDR(2, 1)
}
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: seq_semtake
****************************************************************************/
static int seq_semtake(sem_t *id)
{
return nxsem_wait_uninterruptible(id);
}
/****************************************************************************
* Name: seq_semgive
****************************************************************************/
static void seq_semgive(sem_t *id)
{
nxsem_post(id);
}
/****************************************************************************
* Name: seq_fifosetactive
****************************************************************************/
static void seq_fifosetactive(FAR struct seq_s *seq, int fifoid)
{
irqstate_t flags = enter_critical_section();
seq->active |= 1 << fifoid;
leave_critical_section(flags);
}
/****************************************************************************
* Name: seq_fifosetinactive
****************************************************************************/
static void seq_fifosetinactive(FAR struct seq_s *seq, int fifoid)
{
irqstate_t flags = enter_critical_section();
seq->active &= ~(1 << fifoid);
leave_critical_section(flags);
}
/****************************************************************************
* Name: seq_fifoisactive
****************************************************************************/
static int seq_fifoisactive(FAR struct seq_s *seq, int fifoid)
{
irqstate_t flags = enter_critical_section();
int8_t active = seq->active;
leave_critical_section(flags);
return active & (1 << fifoid);
}
/****************************************************************************
* Name: seq_isactive
****************************************************************************/
static int seq_isactive(FAR struct seq_s *seq)
{
irqstate_t flags = enter_critical_section();
int8_t active = seq->active;
leave_critical_section(flags);
return active;
}
/****************************************************************************
* Name: findzeroandset
*
* Description:
* Find zero bit from LSB and set when found.
*
* Input Parameters:
* bitmap - Bitmap for searching
* nbits - Size of bitmap in bits
*
* Returned value:
* Bit number of set 1. -1 is not found.
*
****************************************************************************/
static int findzeroandset(FAR uint8_t *bitmap, int nbits)
{
int i;
irqstate_t flags;
flags = enter_critical_section();
for (i = 0; i < nbits; i++)
{
if (!(*bitmap & (1 << i)))
{
*bitmap |= 1 << i;
leave_critical_section(flags);
return i;
}
}
leave_critical_section(flags);
return -1;
}
/****************************************************************************
* Name: bitmapclear
*
* Description:
* Common atomic bit clear
*
****************************************************************************/
static void bitmapclear(FAR uint8_t *bitmap, int bit)
{
irqstate_t flags = enter_critical_section();
*bitmap &= ~(1 << bit);
leave_critical_section(flags);
}
/****************************************************************************
* Name: deci_alloc
*
* Description:
* Allocate sequencer for decimation.
*
* Returned Value:
* Sequencer ID is returned on success. -1 is returned on failure.
*
****************************************************************************/
static inline int8_t deci_alloc(void)
{
int8_t ret;
ret = findzeroandset(&g_scudev.decimators, 2);
if (ret >= 0)
{
/* Wakeup SCU anyway if sequencer allocated successfully */
seq_inhibitrequest(REQ_SLEEP, true);
}
return ret;
}
/****************************************************************************
* Name: deci_free
*
* Description:
* Free sequencer for decimation.
*
****************************************************************************/
static void deci_free(int8_t sid)
{
DEBUGASSERT(sid == 0 || sid == 1);
bitmapclear(&g_scudev.decimators, sid);
if (!g_scudev.decimators && !g_scudev.sequencers)
{
/* SCU goes sleep when no sequencers running */
seq_inhibitrequest(REQ_SLEEP, false);
}
}
/****************************************************************************
* Name: mathf_alloc
*
* Description:
* Allocate MATHFUNC filter. MATHFUNC is max 3.
*
* Returned Value:
* Allocated MATHFUNC ID is returned on success. -1 is returned on failure.
*
****************************************************************************/
static inline int8_t mathf_alloc(void)
{
return findzeroandset(&g_scudev.mathfuncs, 3);
}
/****************************************************************************
* Name: mathf_free
*
* Description:
* Free MATHFUNC
*
****************************************************************************/
static inline void mathf_free(int8_t mid)
{
DEBUGASSERT(mid >= 0 && mid < 3);
bitmapclear(&g_scudev.mathfuncs, mid);
}
/****************************************************************************
* Name: seq_alloc
*
* Description:
* Allocate sequencer
*
* Returned Value:
* Allocated sequencer ID is returned on success.
* -1 is returned on failure.
*
****************************************************************************/
static int8_t seq_alloc(void)
{
int8_t sid = findzeroandset(&g_scudev.sequencers, 8);
if (sid < 0)
{
return -1;
}
/* Wakeup SCU anyway if sequencer allocated successfully */
seq_inhibitrequest(REQ_SLEEP, true);
return sid + 2;
}
/****************************************************************************
* Name: seq_free
*
* Description:
* Free sequencer
*
****************************************************************************/
static void seq_free(int8_t sid)
{
DEBUGASSERT(sid >= 2 && sid < 10);
bitmapclear(&g_scudev.sequencers, sid - 2);
if (!g_scudev.decimators && !g_scudev.sequencers)
{
/* SCU goes sleep when no sequencers running */
seq_inhibitrequest(REQ_SLEEP, false);
}
}
/****************************************************************************
* Name: oneshot_alloc
*
* Description:
* Allocate oneshot sequencer
*
* Returned Value:
* Oneshot ID
*
****************************************************************************/
static inline int8_t oneshot_alloc(void)
{
return findzeroandset(&g_scudev.oneshot, 3);
}
/****************************************************************************
* Name: oneshot_free
*
* Description:
* Free oneshot sequencer
*
* Input Parameters:
* tid - Oneshot ID (0-2)
*
****************************************************************************/
static inline void oneshot_free(int8_t tid)
{
DEBUGASSERT(tid >= 0 && tid < 3);
bitmapclear(&g_scudev.oneshot, tid);
}
/****************************************************************************
* Name: seq_inhibitrequest
*
* Description:
* Get the contents of the SPI register at offset
* Holy terrible SEQ_ACCESS_INHIBIT register's bit field name is
* different from actual function.
*
* Input Parameters:
* req - REQ_DONE, REQ_SLEEP, REQ_STOP
* inhibit - false (release) or true (set)
*
* Returned Value:
* The contents of the 32-bit register
*
****************************************************************************/
static void seq_inhibitrequest(int req, bool set)
{
uint32_t val;
val = getreg32(SCU_SEQ_ACCESS_INHIBIT) & ~(1 << req);
if (set)
{
val |= 1 << req;
}
putreg32(val, SCU_SEQ_ACCESS_INHIBIT);
}
/****************************************************************************
* Name: seq_setdecimation
*
* Description:
* Set decimation configurations
*
* Input Parameters:
* wid - FIFO ID (write side)
* ratio - Decimation ratio
* leveladj - Output data level adjustment (2^n)
* forcethrough - Bypassing decimation calculation (not same as ratio x1)
*
****************************************************************************/
static void seq_setdecimation(int wid, uint8_t ratio, uint8_t leveladj,
uint8_t forcethrough)
{
uint32_t val;
uint32_t addr;
int shift;
shift = 8 * (wid & 3);
addr = wid < 4 ? SCU_DECIMATION_PARAM0 : SCU_DECIMATION_PARAM1;
val = getreg32(addr) & ~(0xff << shift);
val |= (ratio & 0xf) << shift; /* N */
val |= (leveladj & 0x3) << (shift + 4); /* LEVEL_ADJ */
val |= (forcethrough & 1) << (shift + 7);
putreg32(val, addr);
}
/****************************************************************************
* Name: seq_setproperty
*
* Description:
* Set sequencer property parameters
*
* Input Parameters:
* sid - Sequender ID
* slave - Slave select (SPI) or address (I2C)
* dest - Output buffer (1 - 3, oneshot only)
* offset - Instruction start offset
* len - Instruction length
*
****************************************************************************/
static void seq_setproperty(int sid, int slave, int out, int offset, int len)
{
uint32_t val;
val = slave & 0x3ff;
val |= (out & 0x3) << 12;
val |= (offset & 0x7f) << 16;
val |= (len & 0xf) << 24;
putreg32(val, SCUSEQ_PROPERTY(sid));
}
/****************************************************************************
* Name: seq_setinst
*
* Description:
* Set instruction and its length to sequencer property
*
* Input Parameters:
* sid - Sequender ID
* offset - Instruction start offset
* len - Instruction length
*
****************************************************************************/
static void seq_setinst(int sid, int offset, int len)
{
uint32_t val;
val = getreg32(SCUSEQ_PROPERTY(sid)) & ~((0xf << 24) | (0x7f << 16));
val |= (offset & 0x7f) << 16;
val |= (len & 0xf) << 24;
putreg32(val, SCUSEQ_PROPERTY(sid));
}
/****************************************************************************
* Name: seq_setbus
*
* Description:
* Set source bus type for sequencer
*
* Input Parameters:
* sid - Sequencer ID
* bustype - Bus type (SCU_BUS_*)
*
****************************************************************************/
static void seq_setbus(int sid, int bustype)
{
uint32_t val;
uint32_t mask;
uint32_t bit;
bit = 1 << sid;
mask = bit | bit << 10 | bit << 20;
switch (bustype)
{
case SCU_BUS_I2C0:
{
bit <<= 10;
}
break;
case SCU_BUS_I2C1:
{
bit <<= 20;
}
break;
case SCU_BUS_SPI:
break;
case SCU_BUS_LPADC0:
case SCU_BUS_LPADC1:
case SCU_BUS_LPADC2:
case SCU_BUS_LPADC3:
case SCU_BUS_HPADC0:
case SCU_BUS_HPADC1:
default:
return;
}
val = getreg32(SCUSEQ_SRC_SEL) & ~mask;
putreg32(val | bit, SCUSEQ_SRC_SEL);
}
/****************************************************************************
* Name: seq_setdataformat
*
* Description:
* Reading sensor data format
*
* Input Parameters:
* sid - Sequencer ID
* start - Sensor data start offset in sequencer picked
* bps - Bytes per sample
* swap - Wwap bytes
* elem - Number of elements in sample
*
****************************************************************************/
static void seq_setdataformat(int sid, int start,
int bps, int swap, int elem)
{
uint32_t val;
val = start & 0xf;
val |= (bps & 0xf) << 4;
val |= (swap & 0x1) << 16;
val |= (elem & 0x3) << 28;
putreg32(val, SCUSEQ_OUT_FORMAT(sid));
}
/****************************************************************************
* Name: seq_setmathwritevecelenum
*
* Description:
* Set number of elements in sample
* (MATH_WRITE_VEC_ELE_NUM field in SEQ_OUT_FORMAT_n)
*
* Input Parameters:
* sid - Sequencer ID
* elem - Number of elements in sample
*
****************************************************************************/
static void seq_setmathwritevecelenum(int sid, uint8_t elem)
{
uint32_t val;
DEBUGASSERT(elem > 0 && elem <= 3);
val = getreg32(SCUSEQ_OUT_FORMAT(sid));
val = (val & ~(0x3 << 28)) | ((elem - 1) & 0x3) << 28;
putreg32(val, SCUSEQ_OUT_FORMAT(sid));
}
/****************************************************************************
* Name: seq_setsignconversion
*
* Description:
* Enable or disable sensor data sign conversion feature for preprocessing.
*
* Input Parameters:
* sid - Sequencer ID
* sign - 0 to no conversion, 1 to convert unsigned to signed
*
****************************************************************************/
static void seq_setsignconversion(int sid, int sign)
{
uint32_t val;
int shift = sid < 2 ? sid * 4 : sid + 6;
val = getreg32(SCU_UNSIGNED_TO_SIGNED);
val &= ~(1 << shift);
putreg32(val | (sign << shift), SCU_UNSIGNED_TO_SIGNED);
}
/****************************************************************************
* Name: seq_setstartmode
*
* Description:
* Set sequencer start mode
*
* Input Parameters:
* sid - Sequencer ID
* mode - Start mode
*
****************************************************************************/
static void seq_setstartmode(int sid, int mode)
{
uint32_t val;
uint32_t shift;
shift = sid * 2;
val = getreg32(SCU_START_MODE1) & ~(3 << shift);
putreg32(val | (mode << shift), SCU_START_MODE1);
}
/****************************************************************************
* Name: seq_setstartinterval
*
* Description:
* Set sequencer interval
*
* Input Parameters:
* sid - Sequencer ID
* interval - Interval
*
****************************************************************************/
static void seq_setstartinterval(int sid, int interval)
{
uint32_t addr = SCU_START_INTERVAL3_0 + (sid & 0xc);
uint32_t val;
uint32_t shift;
shift = (sid & 3) * 8;
val = getreg32(addr) & ~(0xf << shift);
putreg32(val | (interval << shift), addr);
}
/****************************************************************************
* Name: seq_setstartphase
*
* Description:
* Set sequencer start phase.
* This function always set to zero right now. I don't know what this
* parameter affects for.
*
* Input Parameters:
* sid - Sequencer ID
* phase - Phase value
*
****************************************************************************/
static void seq_setstartphase(int sid, int phase)
{
uint32_t addr = SCU_START_PHASE1_0 + ((sid & 0xe) << 1);
uint32_t val;
uint32_t shift;
shift = (sid & 1) * 16;
val = getreg32(addr) & ~(0x1ff << shift);
putreg32(val | (phase << shift), addr);
}
/****************************************************************************
* Name: seq_startseq
*
* Description:
* Start sequencer
*
* Input Parameters:
* sid - Sequencer ID
*
****************************************************************************/
static void seq_startseq(int sid)
{
uint32_t val;
val = getreg32(SCU_START_MODE0);
putreg32(val | (1 << sid), SCU_START_MODE0);
}
/****************************************************************************
* Name: seq_stopseq
*
* Description:
* Stop sequencer
*
* Input Parameters:
* sid - Sequencer ID
*
****************************************************************************/
static void seq_stopseq(int sid)
{
uint32_t val;
val = getreg32(SCU_START_MODE0);
putreg32(val & ~(1 << sid), SCU_START_MODE0);
}
/****************************************************************************
* Name: seq_oneshot
*
* Description:
* Perform one shot feature, this function can be use for any register
* access to connected sensors. (e.g. configure power mode)
* This function use empty sequencer temporarily, other than allocated
* sequencer to the sensor device driver. Thus, this function fails when
* all of normal sequencers are allocated.
*
* Input Parameters:
* bustype - One of SCU_BUS_SPI, SCU_BUS_I2C0 and SCU_BUS_I2C1
* slave - Slave select (SPI) or address (I2C)
* inst - Pointer to series of read/write instruction array
* nr_insts - Number of instructions
* buffer - Receive data buffer
* len - Length of receive data buffer
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int seq_oneshot(int bustype, int slave, FAR uint16_t *inst,
uint32_t nr_insts, FAR uint8_t *buffer, int len)
{
struct cxd56_scudev_s *priv = &g_scudev;
irqstate_t flags;
int sid;
int tid;
int istart;
int i;
int ret = OK;
uint16_t lastinst;
ASSERT(bustype == SCU_BUS_SPI || bustype == SCU_BUS_I2C0 ||
bustype == SCU_BUS_I2C1);
if (nr_insts > INSTRUCTION_PER_SEQ)
{
return -EINVAL;
}
/* If buffer is non NULL, check receive length is valid */
if (buffer && (len <= 0 || len > 16))
{
return -EINVAL;
}
flags = enter_critical_section();
sid = seq_alloc();
if (sid < 0)
{
leave_critical_section(flags);
return -ENOENT;
}
tid = oneshot_alloc();
if (tid < 0)
{
seq_free(sid);
leave_critical_section(flags);
return -ENOENT;
}
leave_critical_section(flags);
/* Remake last instruction, if needed. */
lastinst = seq_remakeinstruction(bustype, inst[nr_insts - 1]);
/* Copy sequencer instruction */
istart = sid * INSTRUCTION_PER_SEQ;
for (i = 0; i < nr_insts - 1; i++)
{
putreg16(inst[i], SCUSEQ_INSTRUCTION(istart + i));
}
putreg16(lastinst, SCUSEQ_INSTRUCTION(istart + nr_insts - 1));
/* Setup sequencer as oneshot mode
* Oneshot mode is special function, so can not be use to MATHFUNC,
* sign conversion and offset/gain adjustment.
*/
seq_setbus(sid, bustype);
seq_setproperty(sid, slave, tid + 1, istart, nr_insts - 1);
seq_setsignconversion(sid, 0);
seq_offsetgainenable(sid, false);
seq_setdataformat(sid, 0, buffer ? len - 1 : 0, 0, 0);
/* Mode set "Stop" */
seq_setstartmode(sid, 0);
/* Set fixed value interval (0xa) for one shot sequencer */
seq_setstartinterval(sid, 0xa);
/* Enable interrupt for one shot sequencer */
putreg32(1 << (tid + 24), SCU_INT_ENABLE_MAIN);
scuinfo("Sequencer start.\n");
/* Start sequencer as one shot mode */
seq_startseq(sid);
putreg32(1 << sid, SCU_SINGLE_EXE);
/* Wait for one shot is done */
seq_semtake(&priv->oneshotwait[tid]);
/* Disable interrupt for one shot sequencer */
putreg32(1 << (tid + 24), SCU_INT_DISABLE_MAIN);
scuinfo("Sequencer done.\n");
if (buffer)
{
/* Copy sequencer output results to user buffer.
* XXX: Sequencer output RAM offset is differ from document.
*/
memcpy(buffer, (void *)(SCUSEQ_RAM_OUT_DATA0 + (0x10 * tid)), len);
}
/* Stop sequencer */
seq_stopseq(sid);
/* Destroy oneshot resources */
flags = enter_critical_section();
oneshot_free(tid);
seq_free(sid);
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Name: seq_enableoffsetgain
*
* Description:
* Enable offset/gain preprocessing
*
****************************************************************************/
static void seq_offsetgainenable(int sid, bool enable)
{
uint32_t val;
int n = sid < 2 ? sid * 4 : sid + 6;
val = getreg32(SCU_OFST_GAIN_EN);
if (enable)
{
val |= 1 << n;
}
else
{
val &= ~(1 << n);
}
putreg32(val, SCU_OFST_GAIN_EN);
}
/****************************************************************************
* Name: seq_start
*
* Description:
* Start sequencer. This function is for SCUIOC_START.
*
* Input Parameters:
* seq - An instance of sequencer
* fifoid - FIFO ID (0-2, decimator only)
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int seq_start(FAR struct seq_s *seq, int fifoid)
{
struct scufifo_s *fifo;
uint32_t interval;
DEBUGASSERT(seq);
DEBUGASSERT(fifoid >= 0 && fifoid < 3);
if (seq->type & SEQ_TYPE_DECI)
{
struct decimator_s *deci = (struct decimator_s *)seq;
struct decimation_fifo_s *dfifo = &deci->dfifo[fifoid];
/* Avoid confilict for DECIMATION_PARAM register access with SCU */
if (seq_isactive(seq))
{
seq_sync(&deci->seq, REQ_STOP);
}
seq_setdecimation(dfifo->fifo->wid, dfifo->ratio, dfifo->leveladj,
dfifo->forcethrough);
fifo = dfifo->fifo;
interval = dfifo->ratio + seq->rate;
/* Prevent SCU freeze by frequently REQ_STOP */
if (seq_isactive(seq))
{
up_udelay(100);
}
}
else
{
fifo = seq->fifo;
interval = seq->rate;
}
if (!fifo)
{
return -EPERM;
}
if (!seq_isactive(seq))
{
if (!(seq->bustype & 0x10))
{
/* Calculate timestamp interval prediv * (2 ^ interval) */
fifo->interval = CONFIG_CXD56_SCU_PREDIV * (1 << interval);
fifo->adjust = 0;
/* Go sequencer */
seq_setstartmode(seq->id, 1);
seq_setstartinterval(seq->id, seq->rate);
seq_setstartphase(seq->id, 1);
seq_startseq(seq->id);
}
else
{
uint32_t mask;
uint32_t val;
/* Calculate timestamp interval for ADC */
cxd56_adc_getinterval(seq->bustype,
&fifo->interval,
&fifo->adjust);
/* Enable ADC */
mask = 0x1 << (24 + (seq->bustype & 0x7));
val = getreg32(SCUSEQ_ADC_PROPERTY) & ~mask;
val |= (0x1 << (24 + (seq->bustype & 0x7)));
putreg32(val, SCUSEQ_ADC_PROPERTY);
}
}
seq_fifosetactive(seq, fifoid);
return 0;
}
/****************************************************************************
* Name: seq_stop
*
* Description:
* Stop sequencer (external API)
*
****************************************************************************/
static int seq_stop(FAR struct seq_s *seq, int fifoid)
{
struct scufifo_s *fifo;
uint32_t val;
DEBUGASSERT(seq);
DEBUGASSERT(fifoid >= 0 && fifoid < 3);
fifo = seq_getfifo(seq, fifoid);
if (fifo == NULL)
{
scuerr("missing FIFO\n");
return -EPERM;
}
if (!seq_isactive(seq))
{
return OK;
}
val = getreg32(SCUFIFO_R_CTRL1(fifo->rid));
putreg32(val & ~(1 << 24), SCUFIFO_R_CTRL1(fifo->rid));
if (seq->type & SEQ_TYPE_DECI)
{
seq_sync(seq, REQ_STOP);
/* Set ratio to 15 to disable decimator */
seq_setdecimation(fifo->wid, DECIMATION_OFF, 0, 0);
/* Prevent SCU freeze by frequently REQ_STOP */
up_udelay(100);
}
seq_fifosetinactive(seq, fifoid);
/* If all of FIFOs are stopped, then stop sequencer. */
if (!seq_isactive(seq))
{
if (!(seq->bustype & 0x10))
{
seq_setstartmode(seq->id, 0);
seq_stopseq(seq->id);
/* Wait for stop done */
seq_sync(seq, REQ_DONE);
}
else
{
/* Disable ADC */
uint32_t mask = 0x1 << (24 + (seq->bustype & 0x7));
val = getreg32(SCUSEQ_ADC_PROPERTY) & ~mask;
putreg32(val, SCUSEQ_ADC_PROPERTY);
}
}
return 0;
}
/****************************************************************************
* Name: mathf_enable
*
* Description:
* Enable MATHFUNC to specified FIFO
*
* Input Parameters:
* mid - MATHFUNC ID
* wid - FIFO ID (Write side)
*
****************************************************************************/
static void mathf_enable(int8_t mid, uint8_t wid)
{
uint32_t val;
val = getreg32(SCU_MATHFUNC_SEL);
val &= ~(0xf << (mid * 8));
val |= 1 << ((mid * 8) + 4);
putreg32(val | (wid << (mid * 8)), SCU_MATHFUNC_SEL);
putreg32(1 << mid, SCU_MATHFUNC_CLR);
}
/****************************************************************************
* Name: mathf_disable
*
* Description:
* Disable MATHFUNC
*
* Input Parameters:
* mid - MATHFUNC ID
*
****************************************************************************/
static void mathf_disable(int8_t mid)
{
uint32_t val;
val = getreg32(SCU_MATHFUNC_SEL);
val &= ~(1 << ((mid * 8) + 4));
putreg32(val, SCU_MATHFUNC_SEL);
}
/****************************************************************************
* Name: mathf_set_coeff
*
* Description:
* Set 1 coefficiency value
*
****************************************************************************/
static inline void mathf_set_coeff(uint32_t caddr, FAR struct iir_coeff_s *c)
{
putreg32(c->h, caddr);
putreg32((c->l & 0x3) << 30, caddr + 4);
}
/****************************************************************************
* Name: mathf_setiirfilter
*
* Description:
* Set series of coefficiencies for 2 IIR filters
*
* Input Parameters:
* mid - MATHFUNC ID
* n - Filter ID
* filter - IIR filter setting
*
****************************************************************************/
static void mathf_setiirfilter(int mid, int n,
FAR struct iir_filter_s *filter)
{
const struct coeff_addr_s *caddr;
caddr = g_caddrs[mid];
putreg32((filter->ishift & 0x7) | ((filter->oshift & 0x7) << 8),
caddr[0].s);
mathf_set_coeff(caddr[n].c0, &filter->coeff[0]);
mathf_set_coeff(caddr[n].c1, &filter->coeff[1]);
mathf_set_coeff(caddr[n].c2, &filter->coeff[2]);
mathf_set_coeff(caddr[n].c3, &filter->coeff[3]);
mathf_set_coeff(caddr[n].c4, &filter->coeff[4]);
}
/****************************************************************************
* Name: seq_setsamplingrate
*
* Description:
* Set sequencer sampling rate
*
* Input Parameters:
* seq - An instance of sequencer
* samplingrate - Sampling rate. Sampling rate is based on
* 32768 Hz / predivider.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int seq_setsamplingrate(FAR struct seq_s *seq, uint8_t samplingrate)
{
DEBUGASSERT(seq);
if (samplingrate > 9)
{
return -EINVAL;
}
seq->rate = samplingrate;
return OK;
}
/****************************************************************************
* Name: seq_sync
*
* Description:
* Send specific request to SCU firmware
*
* Input Parameters:
* seq - An instance of sequencer
* req - Request code for SCU firmware (REQ_*)
*
****************************************************************************/
static void seq_sync(FAR struct seq_s *seq, int req)
{
FAR struct cxd56_scudev_s *priv = &g_scudev;
seq_semtake(&priv->syncexc);
/* Save current request */
priv->currentreq = req;
putreg32(1 << 27, SCU_INT_CLEAR_MAIN);
putreg32(1 << 27, SCU_INT_ENABLE_MAIN);
putreg32(seq->id & 0xf, SCUSEQ_SYNCRO_CPU2ISOP);
/* Send request to SCU */
seq_inhibitrequest(req, true);
/* Wait for interrupt from SCU firmware */
seq_semtake(&priv->syncwait);
priv->currentreq = 0;
seq_semgive(&priv->syncexc);
}
/****************************************************************************
* Name: seq_handlefifintr
*
* Description:
* Handle FIFO overrun error
*
****************************************************************************/
static void seq_handlefifointr(FAR struct cxd56_scudev_s *priv,
uint32_t intr)
{
uint32_t bit;
int i;
#ifndef CONFIG_DISABLE_SIGNAL
struct wm_notify_s *notify;
union sigval value;
#endif
if ((intr & 0x007ffe00) == 0)
{
return;
}
/* Each FIFO almost full interrupt cause are starts with 9 */
for (i = 0, bit = 1 << 9; i < 14; i++, bit <<= 1)
{
if (intr & bit)
{
/* Clear interrupt */
putreg32(bit, SCU_INT_CLEAR_MAIN);
#ifndef CONFIG_DISABLE_SIGNAL
notify = &priv->wm[i];
if (notify->ts)
{
seq_gettimestamp(notify->fifo, notify->ts);
}
DEBUGASSERT(notify->pid != 0);
value.sival_ptr = notify->ts;
sigqueue(notify->pid, notify->signo, value);
#endif
}
}
}
/****************************************************************************
* Name: seq_handlemathfintr
*
* Description:
* Handle MATHFUNC rise and fall event interrupts
*
****************************************************************************/
static void seq_handlemathfintr(FAR struct cxd56_scudev_s *priv,
uint32_t intr)
{
int i;
uint32_t bit;
uint32_t rise;
uint32_t fall;
#ifndef CONFIG_DISABLE_SIGNAL
struct ev_notify_s *notify;
int detected = 0;
#endif
rise = (intr >> 6) & 0x7;
fall = (intr >> 28) & 0x7;
if (rise == 0 && fall == 0)
{
return;
}
for (i = 0, bit = 1; i < 3; i++, bit <<= 1)
{
#ifndef CONFIG_DISABLE_SIGNAL
notify = &priv->event[i];
#endif
/* Detect rise event */
if (rise & bit)
{
putreg32(bit << 6, SCU_INT_CLEAR_MAIN);
#ifndef CONFIG_DISABLE_SIGNAL
/* Get rise event occurred timestamp */
if (notify->arg)
{
notify->arg->ts.sec =
getreg32(SCU_EVENT_TIMESTAMP0_R_MSB + (i * 8));
notify->arg->ts.tick =
getreg32(SCU_EVENT_TIMESTAMP0_R_LSB + (i * 8));
notify->arg->type = SCU_EV_RISE;
}
detected = 1;
#endif
}
/* Detect fall event */
if (fall & bit)
{
putreg32(bit << 28, SCU_INT_CLEAR_MAIN);
#ifndef CONFIG_DISABLE_SIGNAL
/* Get fall event occurred timestamp */
if (notify->arg)
{
notify->arg->ts.sec =
getreg32(SCU_EVENT_TIMESTAMP0_F_MSB + (i * 8));
notify->arg->ts.tick =
getreg32(SCU_EVENT_TIMESTAMP0_F_LSB + (i * 8));
notify->arg->type = SCU_EV_FALL;
}
detected = 1;
#endif
}
#ifndef CONFIG_DISABLE_SIGNAL
if (detected)
{
union sigval value;
DEBUGASSERT(notify->pid != 0);
value.sival_ptr = notify->arg;
sigqueue(notify->pid, notify->signo, value);
detected = 0;
}
#endif
}
}
/****************************************************************************
* Name: seq_handleoneshot
*
* Description:
* Handle one shot sequencer done interrupt
*
****************************************************************************/
static void seq_handleoneshot(FAR struct cxd56_scudev_s *priv, uint32_t intr)
{
uint32_t bit;
int i;
if ((intr & 0x07000000) == 0)
{
return;
}
for (i = 0, bit = 1 << 24; i < 3; i++, bit <<= 1)
{
if (intr & bit)
{
putreg32(bit, SCU_INT_CLEAR_MAIN);
seq_semgive(&priv->oneshotwait[i]);
}
}
}
/****************************************************************************
* Name: seq_handleisopdoneintr
*
* Description:
* Handle interrupts response against request for SCU firmware
*
****************************************************************************/
static void seq_handleisopdoneintr(FAR struct cxd56_scudev_s *priv,
uint32_t intr)
{
/* Detect ISOP3 as done or stop. */
if (intr & (1 << 27))
{
DEBUGASSERT(priv->currentreq != 0);
seq_inhibitrequest(priv->currentreq, false);
putreg32(1 << 27, SCU_INT_DISABLE_MAIN);
putreg32(1 << 27, SCU_INT_CLEAR_MAIN);
seq_semgive(&priv->syncwait);
}
}
/****************************************************************************
* Name: seq_scuirqhandler
*
* Description:
* Root interrupt handler for many SCU interrupts
*
****************************************************************************/
static int seq_scuirqhandler(int irq, FAR void *context, FAR void *arg)
{
FAR struct cxd56_scudev_s *priv = arg;
uint32_t intr;
uint32_t ierr0;
uint32_t ierr1;
uint32_t ierr2;
int i;
intr = getreg32(SCU_INT_MASKED_STT_MAIN);
ierr0 = getreg32(SCU_INT_MASKED_STT_ERR_0);
ierr1 = getreg32(SCU_INT_MASKED_STT_ERR_1);
ierr2 = getreg32(SCU_INT_MASKED_STT_ERR_2);
seq_handlefifointr(priv, intr);
seq_handlemathfintr(priv, intr);
seq_handleoneshot(priv, intr);
seq_handleisopdoneintr(priv, intr);
/* Detect all FIFO overrun errors */
if (ierr0 != 0)
{
scuerr("err0: %08x\n", ierr0);
ierr0 = (ierr0 >> 9) & 0x3fff;
for (i = 0; i < 14; i++)
{
if ((ierr0 & (0x1 << i)) != 0)
{
/* Clear FIFO overrun error */
putreg32(getreg32(SCUFIFO_R_CTRL1(i)) | SCUFIFO_OVERRUNCLR,
SCUFIFO_R_CTRL1(i));
}
}
}
/**
* Detect all FIFO underrun errors
* This error may not happened because check reading bytes at seq_read().
* Thus, it is a program error when ERR1 detected.
*/
if (ierr1 != 0)
{
scuerr("err1: %08x\n", ierr1);
ierr1 = (ierr1 >> 9) & 0x3fff;
for (i = 0; i < 14; i++)
{
if ((ierr1 & (0x1 << i)) != 0)
{
/* Clear FIFO underrun error */
putreg32(getreg32(SCUFIFO_R_CTRL1(i)) | SCUFIFO_UNDERRUNCLR,
SCUFIFO_R_CTRL1(i));
}
}
}
/* Detect sequencer error */
if (ierr2 != 0)
{
scuerr("err2: %08x\n", ierr2);
ierr2 &= 0x03ff;
for (i = 0; i < 10; i++)
{
if (ierr2 & (1 << i))
{
seq_stopseq(i);
}
}
putreg32(0x03ff, SCU_INT_CLEAR_ERR_2);
}
return 0;
}
/****************************************************************************
* Name: seq_new
*
* Description:
* Create new sequencer instance
*
* Returned Value:
* Created sequencer instance or NULL
*
****************************************************************************/
static FAR struct seq_s *seq_new(void)
{
FAR struct seq_s *seq;
int sid;
irqstate_t flags;
flags = enter_critical_section();
sid = seq_alloc();
if (sid < 0)
{
/* Sequencer is full */
leave_critical_section(flags);
return NULL;
}
leave_critical_section(flags);
seq = (FAR struct seq_s *)kmm_malloc(sizeof(struct seq_s));
if (!seq)
{
seq_free(sid);
return NULL;
}
memset(seq, 0, sizeof(struct seq_s));
seq->id = sid;
return seq;
}
/****************************************************************************
* Name: deci_new
*
* Description:
* Create new decimator instance
*
* Returned Value:
* Created decimator instance or NULL
*
****************************************************************************/
static FAR struct seq_s *deci_new(void)
{
FAR struct decimator_s *deci;
int sid;
irqstate_t flags;
flags = enter_critical_section();
sid = deci_alloc();
if (sid < 0)
{
/* Sequencer is full */
leave_critical_section(flags);
return NULL;
}
leave_critical_section(flags);
deci = (FAR struct decimator_s *)kmm_malloc(sizeof(struct decimator_s));
if (!deci)
{
deci_free(sid);
return NULL;
}
memset(deci, 0, sizeof(struct decimator_s));
deci->seq.id = sid;
return &deci->seq;
}
/****************************************************************************
* Name: seq_fifoinit
*
* Description:
* Initialize sequencer output FIFO
*
* Input Parameters:
* seq - Sequencer instance
* fifoid - FIFO ID (decimator only)
* fsize - Allocate FIFO memory size
*
****************************************************************************/
static int seq_fifoinit(FAR struct seq_s *seq, int fifoid, uint16_t fsize)
{
FAR struct scufifo_s *fifo;
FAR struct decimator_s *deci = (FAR struct decimator_s *)seq;
int wid;
int rid;
uint32_t val;
uint32_t mask;
uint16_t start;
uint16_t samples;
DEBUGASSERT(seq);
DEBUGASSERT(fifoid >= 0 && fifoid < 3);
if (seq->type & SEQ_TYPE_DECI)
{
if (deci->dfifo[fifoid].fifo)
{
return OK;
}
}
else
{
if (seq->fifo)
{
/* FIFO already initialized */
return OK;
}
}
fifo = (FAR struct scufifo_s *)kmm_malloc(sizeof(struct scufifo_s));
if (!fifo)
{
return -ENOMEM;
}
memset(fifo, 0, sizeof(struct scufifo_s));
/* Setup FIFO, normal FIFO wid and rid are based on 8 and 4 respectively. */
start = scufifo_memalloc(fsize);
if (start == FIFOMEM_INVALID)
{
kmm_free(fifo);
return -ENOMEM;
}
/**
* FIFO IDs (* is unavailable)
* D = Decimation FIFO
* N = Normal FIFO
*
* sid wid name rid name
* 0 0 D0_W0*
* 1 D0_W1 0 D0_R1
* 2 D0_W2 1 D0_R2
* 3 D0_W3 12 D0_R3
* 1 4 D1_W0*
* 5 D1_W1 2 D1_R1
* 6 D1_W2 3 D1_R2
* 7 D1_W3 13 D1_R3
*
* 2 8 N0_W 4 N0_R1
* 3 9 N1_W 5 N0_R1
* 4 10 N2_W 6 N0_R1
* 5 11 N3_W 7 N0_R1
* 6 12 N4_W 8 N0_R1
* 7 13 N5_W 9 N0_R1
* 8 14 N6_W 10 N0_R1
* 9 15 N7_W 11 N0_R1
*/
if (seq->type & SEQ_TYPE_DECI)
{
wid = fifoid + 1 + (seq->id * 4);
if (fifoid < 2)
{
rid = fifoid + (seq->id * 2);
}
else
{
rid = seq->id + 12;
}
if (!seq_isactive(seq))
{
/* Additionally, reset first stage of decimation logic
* when other decimator not used.
*/
putreg32(1 << (seq->id * 4), SCU_DEC_CLR);
}
/* Reset decimator */
putreg32(1 << wid, SCU_DEC_CLR);
}
else
{
wid = seq->id + 6;
rid = seq->id + 2;
}
/* Set FIFO start offset and its size */
DEBUGASSERT(seq->sample);
samples = fsize / seq->sample;
putreg32((uint32_t)start << 16 | (samples - 1), SCUFIFO_W_CTRL0(wid));
/* Set 1 sample size and FIFO reset */
val = seq->sample - 1;
val |= (1 << 16);
putreg32(val, SCUFIFO_W_CTRL1(wid));
/* Set watermark */
putreg32(0xffff, SCUFIFO_R_CTRL0(rid));
/* Perform enable FIFO outlet, reset and clear error */
val = (1 << 24) | (1 << 16) | (1 << 8) | 3;
putreg32(val, SCUFIFO_R_CTRL1(rid));
/* Clear write event control */
putreg32(0, SCUSEQ_FIFOWREVNTCTRL(wid));
/* Save instance data */
fifo->start = start;
fifo->size = fsize;
fifo->wid = wid;
fifo->rid = rid;
fifo->mid = -1;
#ifdef CONFIG_CXD56_UDMAC
/* Allocate DMA for read sensing data */
fifo->dma = cxd56_udmachannel();
/* Initialize DMA done wait semaphore */
nxsem_init(&fifo->dmawait, 0, 0);
nxsem_set_protocol(&fifo->dmawait, SEM_PRIO_NONE);
fifo->dmaresult = -1;
#endif
if (seq->type & SEQ_TYPE_DECI)
{
FAR struct decimation_fifo_s *dec = &deci->dfifo[fifoid];
dec->fifo = fifo;
dec->ratio = 0;
dec->leveladj = 0;
dec->forcethrough = 1;
}
else
{
seq->fifo = fifo;
}
if (seq->bustype & 0x10)
{
/* Set wid to ADC_PROPERTY */
mask = 0xf << (4 * (seq->bustype & 0x7));
val = getreg32(SCUSEQ_ADC_PROPERTY) & ~mask;
val |= (wid << (4 * (seq->bustype & 0x7)));
putreg32(val, SCUSEQ_ADC_PROPERTY);
}
return OK;
}
/****************************************************************************
* Name: seq_fifofree
*
* Description:
* Free sequencer output FIFO
*
* Input Parameters:
* fifo - An instance of FIFO
*
****************************************************************************/
static void seq_fifofree(FAR struct scufifo_s *fifo)
{
int wid;
int rid;
int mid;
if (!fifo)
{
return;
}
#ifdef CONFIG_CXD56_UDMAC
/* Free DMA */
cxd56_udmafree(fifo->dma);
#endif
wid = fifo->wid;
rid = fifo->rid;
mid = fifo->mid;
if (mid >= 0 && mid < 3)
{
putreg32(3, SCUSEQ_FIFOWREVNTCTRL(wid));
putreg32(0, SCU_EVENT_INTR_ENABLE(mid));
putreg32(0x9 << mid, SCU_FIFO_WRITE_CTRL);
putreg32(1 << mid, SCU_EVENT_STT);
putreg32((1 << (wid + 28)) | (1 << (wid + 6)), SCU_INT_DISABLE_MAIN);
mathf_disable(mid);
mathf_free(mid);
}
/* Disable watermark interrupt anyway */
putreg32(1 << (rid + 9), SCU_INT_DISABLE_MAIN);
/* Make sure want to be FIFO disabled */
putreg32(0, SCUFIFO_R_CTRL1(rid));
scufifo_memfree(fifo->start);
#ifdef CONFIG_CXD56_UDMAC
nxsem_destroy(&fifo->dmawait);
#endif
kmm_free(fifo);
}
/****************************************************************************
* Name: seq_getfifo
*
* Description:
* Utility for ease of obtaining FIFO instans from sequencer.
*
* Input Parameters:
* seq - An instance of sequencer
* fifoid - FIFO ID (decimator only)
*
****************************************************************************/
static inline struct scufifo_s *seq_getfifo(FAR struct seq_s *seq,
int fifoid)
{
DEBUGASSERT(fifoid >= 0 && fifoid < 3);
if (seq->type & SEQ_TYPE_DECI)
{
struct decimator_s *deci = (struct decimator_s *)seq;
return deci->dfifo[fifoid].fifo;
}
else
{
return seq->fifo;
}
}
/****************************************************************************
* Name: seq_setadjustment
*
* Description:
* Set parameters for offset/gain adjustment, preprocessing of sequencer.
*
* Input Parameters:
* seq - An instance of sequencer
* adj - offset/gain adjustment parameter by 3 axis
*
****************************************************************************/
static int seq_setadjustment(FAR struct seq_s *seq,
FAR struct adjust_xyz_s *adj)
{
int sid;
uint32_t val;
DEBUGASSERT(seq);
/* offset/gain parameters can be set when sequencer not running */
if (seq_isactive(seq))
{
return -EBUSY;
}
sid = seq->id;
/* Set offset/gain adjustment parameters for each 3 axis data */
val = adj->x.offset | ((uint32_t)adj->x.gain << 16);
putreg32(val, SCUSEQ_MATH_PROC_OFST_GAIN_X(sid));
val = adj->y.offset | ((uint32_t)adj->y.gain << 16);
putreg32(val, SCUSEQ_MATH_PROC_OFST_GAIN_Y(sid));
val = adj->z.offset | ((uint32_t)adj->z.gain << 16);
putreg32(val, SCUSEQ_MATH_PROC_OFST_GAIN_Z(sid));
return OK;
}
/****************************************************************************
* Name: seq_setfilter
*
* Description:
* Set MATHFUNC IIR filter feature
*
* Input Parameters:
* fifo - An instance of FIFO
* pos - Where to IIR filter inserted
* iir - IIR filter coefficiencies
*
****************************************************************************/
static int seq_setfilter(FAR struct scufifo_s *fifo, int pos,
FAR struct iir_filter_s iir[2])
{
int mid;
DEBUGASSERT(fifo);
if (fifo->mid < 0)
{
mid = mathf_alloc();
if (mid < 0)
{
return -ENOENT;
}
fifo->mid = mid;
mathf_enable(mid, fifo->wid);
}
else
{
mid = fifo->mid;
}
putreg32(pos, SCU_MATHFUNC_POS(mid));
mathf_setiirfilter(mid, 0, &iir[0]);
mathf_setiirfilter(mid, 1, &iir[1]);
return OK;
}
/****************************************************************************
* Name: seq_seteventnotifier
*
* Description:
* Set event notifier
*
* Input Parameters:
* fifo - An instance of FIFO
* ev - Event notify setting
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int seq_seteventnotifier(FAR struct scufifo_s *fifo,
FAR struct scuev_notify_s *ev)
{
struct cxd56_scudev_s *priv = &g_scudev;
uint32_t val;
uint32_t thresh;
uint32_t count0;
uint32_t count1;
uint32_t delaysample;
int riseint;
int fallint;
int mid;
#ifndef CONFIG_DISABLE_SIGNAL
irqstate_t flags;
#endif
DEBUGASSERT(fifo && ev);
/* Math Function must be assigned first. */
if (fifo->mid < 0)
{
return -ENOENT;
}
mid = fifo->mid;
#ifndef CONFIG_DISABLE_SIGNAL
/* Save signal number and target PID */
flags = enter_critical_section();
priv->event[mid].signo = ev->signo;
priv->event[mid].pid = getpid();
priv->event[mid].arg = ev->arg;
priv->event[mid].fifo = fifo;
leave_critical_section(flags);
#endif
thresh = count0 = count1 = delaysample = 0;
riseint = fallint = 0;
thresh = ev->rise.threshold;
count0 = ev->rise.count0;
count1 = ev->rise.count1;
delaysample = ev->rise.delaysamples;
thresh |= ev->fall.threshold << 16;
count0 |= ev->fall.count0 << 16;
count1 |= ev->fall.count1 << 16;
delaysample |= ev->fall.delaysamples << 16;
if (ev->ctrl & SCU_EV_RISE_EN)
{
riseint = 1;
}
if (ev->ctrl & SCU_EV_FALL_EN)
{
fallint = 1;
}
putreg32(thresh, SCU_EVENT_PARAM_THRESH(mid));
putreg32(count0, SCU_EVENT_PARAM_COUNT0(mid));
putreg32(count1, SCU_EVENT_PARAM_COUNT1(mid));
putreg32(delaysample, SCU_EVENT_PARAM_DELAY_SAMPLE(mid));
/* Enable interrupt */
putreg32(fallint << 8 | riseint, SCU_EVENT_INTR_ENABLE(mid));
val = (ev->ctrl & SCU_EV_OUTMASK) >> SCU_EV_OUTSHIFT;
val |= (mid & 0x3) << 4;
val |= (ev->ctrl & SCU_EV_WRITESAMPLEMASK) << 8;
val |= ev->ctrl & SCU_EV_OUT8BITS ? 1 << 24 : 0;
putreg32(val, SCUSEQ_FIFOWREVNTCTRL(fifo->wid));
val = riseint << (mid + 6);
val |= fallint << (mid + 28);
putreg32(val, SCU_INT_ENABLE_MAIN);
putreg32(1 << mid, SCU_EVENT_STT);
return OK;
}
#ifndef CONFIG_DISABLE_SIGNAL
/****************************************************************************
* Name: seq_setwatermark
*
* Description:
* Set watermark value for specified FIFO
*
****************************************************************************/
static int seq_setwatermark(FAR struct seq_s *seq, int fifoid,
FAR struct scufifo_wm_s *wm)
{
FAR struct cxd56_scudev_s *priv = &g_scudev;
struct scufifo_s *fifo = seq_getfifo(seq, fifoid);
struct wm_notify_s *notify;
int rid;
irqstate_t flags;
DEBUGASSERT(fifo && wm);
if (wm->watermark == 0)
{
return -EINVAL;
}
rid = fifo->rid;
notify = &priv->wm[rid];
flags = enter_critical_section();
notify->signo = wm->signo;
notify->pid = getpid();
notify->ts = wm->ts;
notify->fifo = fifo;
/* Set watermark */
putreg32(wm->watermark, SCUFIFO_R_CTRL0(rid));
/* Enable FIFO almost full interrupt */
putreg32(1 << (rid + 9), SCU_INT_ENABLE_MAIN);
leave_critical_section(flags);
scuinfo("watermark = %04x, PID = %d, signo = %d\n", wm->watermark,
notify->pid, notify->signo);
return OK;
}
/****************************************************************************
* Name: convert_firsttimestamp
*
* Description:
*
****************************************************************************/
static void convert_firsttimestamp(struct scutimestamp_s *tm,
uint16_t interval,
uint16_t sample, uint16_t adjust)
{
uint32_t delta;
uint32_t tick;
uint16_t mod;
if (sample == 0 || interval == 0)
return;
mod = sample & 0x7;
if (adjust && mod)
{
delta = interval * (sample + (8 - mod));
if (adjust == 1)
{
if (mod <= 2)
{
delta -= 2;
}
else if (mod <= 5)
{
delta -= 1;
}
}
}
else
{
delta = interval * sample;
}
tick = tm->tick;
if (tick < delta)
{
tick += 0x80000000u - delta;
tm->tick = (tick & 0x00007fff);
tm->sec = tm->sec + (tick >> 15) - (0x80000000u >> 15);
}
else
{
tm->tick = tm->tick - delta;
}
}
/****************************************************************************
* Name: latest_timestamp
*
* Description:
* Get latest timestamp when data stored into FIFO.
*
****************************************************************************/
static void latest_timestamp(struct scufifo_s *fifo, uint32_t interval,
struct scutimestamp_s *tm, uint16_t *samples)
{
struct scutimestamp_s pre;
struct scutimestamp_s post;
uint16_t pres;
uint16_t posts;
int rid = fifo->rid;
int wid = fifo->wid;
/* Get number of samples and timestamp twice for adapt roll over */
pres = getreg32(SCUFIFO_R_STATUS0(rid)) & 0xffff;
pre.sec = getreg32(SCUFIFO_W_TIMESTAMP0(wid));
pre.tick = getreg16(SCUFIFO_W_TIMESTAMP1(wid));
posts = getreg32(SCUFIFO_R_STATUS0(rid)) & 0xffff;
post.sec = getreg32(SCUFIFO_W_TIMESTAMP0(wid));
post.tick = getreg16(SCUFIFO_W_TIMESTAMP1(wid));
if (pres == posts)
{
*samples = pres;
tm->sec = pre.sec;
tm->tick = pre.tick;
}
else
{
*samples = posts;
tm->sec = post.sec;
tm->tick = post.tick;
if (tm->tick >= interval)
{
tm->tick -= interval;
}
else
{
tm->sec -= 1;
tm->tick += 0x8000 - interval;
}
}
}
/****************************************************************************
* Name: gettimestamp
*
* Description:
* Get timestamp of head of FIFO sensing data.
*
****************************************************************************/
static void seq_gettimestamp(struct scufifo_s *fifo,
struct scutimestamp_s *tm)
{
uint16_t sample;
uint16_t adjust = fifo->adjust;
uint32_t interval = fifo->interval;
latest_timestamp(fifo, interval, tm, &sample);
convert_firsttimestamp(tm, interval, sample, adjust);
}
#else
#define seq_setwatermark(seq, fifoid, wm) (-ENOSYS)
#endif
/****************************************************************************
* Name: seq_setfifomode
*
* Description:
* Enable or disable FIFO overwrite mode.
*
****************************************************************************/
static void seq_setfifomode(FAR struct seq_s *seq, int fifoid, int enable)
{
FAR struct scufifo_s *fifo = seq_getfifo(seq, fifoid);
uint32_t val;
irqstate_t flags;
#ifndef CONFIG_DISABLE_SIGNAL
FAR struct cxd56_scudev_s *priv = &g_scudev;
FAR struct wm_notify_s *notify = &priv->wm[fifo->rid];
bool iswtmk = false;
#endif
DEBUGASSERT(fifo);
scuinfo("FIFO mode %d wid %d\n", enable, fifo->wid);
#ifndef CONFIG_DISABLE_SIGNAL
if (notify->ts)
{
iswtmk = true;
}
#endif
flags = enter_critical_section();
/* control SCU FIFO overwrite */
val = getreg32(SCUFIFO_W_CTRL1(fifo->wid));
val &= ~(0x1 << 4);
if (enable)
{
val |= (0x1 << 4);
}
putreg32(val, SCUFIFO_W_CTRL1(fifo->wid));
if (enable)
{
/* disable overrun err interrupt */
val = 0x1 << (fifo->rid + 9);
putreg32(val, SCU_INT_DISABLE_ERR_0);
#ifndef CONFIG_DISABLE_SIGNAL
/* disable almostfull interrupt */
if (iswtmk)
{
putreg32(val, SCU_INT_DISABLE_MAIN);
}
#endif
}
else
{
/* clear overrun err */
putreg32(getreg32(SCUFIFO_R_CTRL1(fifo->rid)) | SCUFIFO_OVERRUNCLR,
SCUFIFO_R_CTRL1(fifo->rid));
/* enable overrun err interrupt */
val = 0x1 << (fifo->rid + 9);
putreg32(val, SCU_INT_ENABLE_ERR_0);
#ifndef CONFIG_DISABLE_SIGNAL
/* enable almostfull interrupt */
if (iswtmk)
{
val = 0x1 << (fifo->rid + 9);
putreg32(val, SCU_INT_ENABLE_MAIN);
}
#endif
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: seq_remakeinstruction
*
* Description:
* Workaround for the sequencer last instruction issue.
* If the last instruction is i2c write, clear TERMINATE bit.
*
****************************************************************************/
static uint16_t seq_remakeinstruction(int bustype, uint16_t inst)
{
uint16_t ret = inst;
if (bustype == SCU_BUS_I2C0 || bustype == SCU_BUS_I2C1)
{
/* When the instruction is write, clear TERMINATE bit */
if (!(ret & (1 << 8)))
{
ret &= ~SCU_INST_TERM;
}
}
return ret;
}
/****************************************************************************
* Name: scu_hwinit
****************************************************************************/
static void scu_hwinit(void)
{
int i;
/* Request don't sleep */
seq_inhibitrequest(REQ_SLEEP, true);
/* Disable all interrupts */
putreg32(0xffffffff, SCU_INT_DISABLE_MAIN);
putreg32(0xffffffff, SCU_INT_CLEAR_MAIN);
putreg32(0x0, SCU_LEVEL_SEL_MAIN);
for (i = 0; i < 10; i++)
{
putreg32(0x08000000, SCUSEQ_MATH_PROC_OFST_GAIN_X(i));
putreg32(0x08000000, SCUSEQ_MATH_PROC_OFST_GAIN_Y(i));
putreg32(0x08000000, SCUSEQ_MATH_PROC_OFST_GAIN_Z(i));
}
putreg32(0, SCU_OFST_GAIN_EN);
putreg32(0x0f0f0f00, SCU_DECIMATION_PARAM0);
putreg32(0x0f0f0f00, SCU_DECIMATION_PARAM1);
putreg32(0xff, SCU_DEC_CLR);
for (i = 0; i < 3; i++)
{
putreg32(0, SCU_EVENT_INTR_ENABLE(i));
}
/* Reset MATHFUNC */
putreg32(0, SCU_MATHFUNC_SEL);
putreg32(0x7, SCU_MATHFUNC_CLR);
/* Clear MATHFUNC EVENT... */
putreg32(0x3f, SCU_FIFO_WRITE_CTRL);
putreg32(0x7, SCU_EVENT_STT);
/* Reset all of FIFOs I/O */
for (i = 0; i < 16; i++)
{
putreg32(3, SCUSEQ_FIFOWREVNTCTRL(i));
putreg32(0, SCUFIFO_W_CTRL0(i));
putreg32(0x00010000, SCUFIFO_W_CTRL1(i));
putreg32(0, SCUFIFO_W_CTRL1(i));
}
for (i = 0; i < 14; i++)
{
putreg32(0x00010003, SCUFIFO_R_CTRL1(i));
putreg32(0, SCUFIFO_R_CTRL1(i));
}
/* Decimation FIFO selection.
* All of decimation FIFO I/O linked to each IDs.
*/
putreg32(0x00e400e4, SCUFIFO_DECI_PARTITION_SEL);
/* Enable SPI and I2C[01] interrupts */
putreg32(7, SCU_INT_ENABLE_MAIN);
/* Wait for sequencer ready */
while (!(getreg32(SCU_SEQ_ENABLE_ALL) & 2));
putreg32(((CONFIG_CXD56_SCU_PREDIV - 1) << 8), SCU_START_CTRL_COMMON);
putreg32(1, SCU_SEQ_ENABLE_ALL);
/* Enable SCU sleep */
seq_inhibitrequest(REQ_SLEEP, false);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: scu_spitransfer
****************************************************************************/
int scu_spitransfer(int slavesel, FAR uint16_t *inst, uint32_t nr_insts,
FAR uint8_t *buffer, int len)
{
return seq_oneshot(SCU_BUS_SPI, slavesel, inst, nr_insts, buffer, len);
}
/****************************************************************************
* Name: scu_i2ctransfer
****************************************************************************/
int scu_i2ctransfer(int port, int slave, FAR uint16_t *inst,
uint32_t nr_insts, FAR uint8_t *buffer, int len)
{
int bustype;
if (port == 0)
{
bustype = SCU_BUS_I2C0;
}
else if (port == 1)
{
bustype = SCU_BUS_I2C1;
}
else
{
return -ENODEV;
}
return seq_oneshot(bustype, slave, inst, nr_insts, buffer, len);
}
/****************************************************************************
* Name: seq_open
*
* Description:
* Open sequencer. This API should be call from board initialize or from
* drivers.
*
* Input Parameters:
* type - A type of sequencer (SEQ_TYPE_NORMAL or SEQ_TYPE_DECI)
* bustype - A type of bus under SCU (SCU_BUS_I2C0, SCU_BUS_I2C1 or
* SCU_BUS_SPI)
*
****************************************************************************/
FAR struct seq_s *seq_open(int type, int bustype)
{
FAR struct seq_s *seq;
/* Check bustype is valid */
switch (bustype)
{
case SCU_BUS_SPI:
case SCU_BUS_I2C0:
case SCU_BUS_I2C1:
case SCU_BUS_LPADC0:
case SCU_BUS_LPADC1:
case SCU_BUS_LPADC2:
case SCU_BUS_LPADC3:
case SCU_BUS_HPADC0:
case SCU_BUS_HPADC1:
break;
default:
return NULL;
}
if (type == SEQ_TYPE_DECI)
{
seq = deci_new();
}
else if (type == SEQ_TYPE_NORMAL)
{
seq = seq_new();
}
else
{
return NULL;
}
if (!seq)
{
return NULL;
}
seq->bustype = bustype;
seq->type = type;
seq_setbus(seq->id, bustype);
/* Disable all of data preprocessing */
seq_offsetgainenable(seq->id, false);
seq_setsignconversion(seq->id, 0);
return seq;
}
/****************************************************************************
* Name: seq_setinstruction
*
* Description:
* Set instruction set array for sequencer execution.
*
* Input Parameters:
* seq - An instance of sequencer
* inst - Pointer of instruction array
* nr_insts - Number of instructions
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int seq_setinstruction(FAR struct seq_s *seq, const uint16_t *inst,
uint16_t nr_insts)
{
int istart;
int i;
uint16_t lastinst;
if (nr_insts > INSTRUCTION_PER_SEQ)
{
return -EINVAL;
}
/* Remake last instruction, if needed. */
lastinst = seq_remakeinstruction(seq->bustype, *(inst + nr_insts - 1));
/* Copy cyclic running instruction */
istart = seq->id * INSTRUCTION_PER_SEQ;
for (i = 0; i < nr_insts - 1; i++)
{
putreg16(inst[i], SCUSEQ_INSTRUCTION(istart + i));
}
putreg16(lastinst, SCUSEQ_INSTRUCTION(istart + nr_insts - 1));
/* Set instruction parameters */
seq_setinst(seq->id, istart, nr_insts - 1);
return OK;
}
/****************************************************************************
* Name: seq_setbytespersample
*
* Description:
* Set sample data format
*
* Input Parameters:
* seq - An instance of sequencer
* sample - Size of sample (e.g. 16 bit 3 axis data = 6)
* offset - Start offset of sampling data
* elemsize - Size of 1 element (e.g. 16 bit 3 axis data = 2)
* swapbyte - Enable/Disable byte swapping if available
*
****************************************************************************/
void seq_setsample(FAR struct seq_s *seq, uint8_t sample, uint8_t offset,
uint8_t elemsize, bool swapbyte)
{
DEBUGASSERT(seq);
seq->sample = sample;
seq_setdataformat(seq->id, offset, sample - 1, swapbyte ? 1 : 0, elemsize);
}
/****************************************************************************
* Name: seq_setaddress
*
* Description:
* Set slave ID or address
*
* Input Parameters:
* seq - An instance of sequencer
* slave_addr - In SPI, slave select ID. In I2C, bus address.
*
****************************************************************************/
void seq_setaddress(FAR struct seq_s *seq, uint32_t slave_addr)
{
seq_setproperty(seq->id, slave_addr, 0, 0, 0);
}
#ifdef CONFIG_CXD56_UDMAC
/****************************************************************************
* Name: seq_fifodmadone
*
* Description:
* Callback function for receive DMA done
*
****************************************************************************/
static void seq_fifodmadone(DMA_HANDLE handle, uint8_t status, void *arg)
{
struct scufifo_s *fifo = (struct scufifo_s *)arg;
fifo->dmaresult = status;
seq_semgive(&fifo->dmawait);
}
#else
/****************************************************************************
* Name: seq_read8
****************************************************************************/
static inline void seq_read8(uint32_t addr, FAR uint8_t *buffer, int length)
{
int i;
for (i = 0; i < length; i++)
{
*buffer++ = getreg8(addr);
}
}
/****************************************************************************
* Name: seq_read16
****************************************************************************/
static inline void seq_read16(uint32_t addr,
FAR uint16_t *buffer,
int length)
{
int i;
for (i = 0; i < length / 2; i++)
{
*buffer++ = getreg16(addr);
}
}
/****************************************************************************
* Name: seq_read32
****************************************************************************/
static inline void seq_read32(uint32_t addr,
FAR uint32_t *buffer,
int length)
{
int i;
for (i = 0; i < length / 4; i++)
{
*buffer++ = getreg32(addr);
}
}
#endif
/****************************************************************************
* Name: seq_read
*
* Description:
* Read sensor data from sequencer FIFO
*
* Input Parameters:
* seq - An instance of sequencer
* fifoid - FIFO ID (decimator only)
* buffer - Pointer to data receive buffer
* length - Length of buffer
*
* Returned Value:
* Number of bytes of read data
*
****************************************************************************/
int seq_read(FAR struct seq_s *seq, int fifoid, FAR char *buffer, int length)
{
struct scufifo_s *fifo;
uint32_t outlet;
int avail;
#ifdef CONFIG_CXD56_UDMAC
dma_config_t config;
uint32_t dstbuf;
char *dst;
int maxlen = 1024;
int dmalen;
int rest;
int need_wakelock = 0;
struct pm_cpu_wakelock_s wlock;
wlock.info = PM_CPUWAKELOCK_TAG('S', 'C', 0);
wlock.count = 0;
#else
int i;
#endif
#ifdef CONFIG_CXD56_SCU_DEBUG
uint32_t status;
#endif
DEBUGASSERT(seq);
DEBUGASSERT(fifoid >= 0 && fifoid < 3);
fifo = seq_getfifo(seq, fifoid);
DEBUGASSERT(fifo);
outlet = SCUFIFO_FIFO_DATA(fifo->rid);
avail = getreg32(SCUFIFO_R_STATUS0(fifo->rid));
scuinfo("Available %d samples\n", avail);
#ifdef CONFIG_CXD56_SCU_DEBUG
status = getreg32(SCUFIFO_R_STATUS1(fifo->rid));
scuinfo("Status: %08x\n", status);
#endif
avail *= seq->sample;
length = MIN(avail, length);
if (length == 0)
{
return 0;
}
#ifdef CONFIG_CXD56_UDMAC
/* Get sensor data from FIFO by uDMAC (PL230) */
/* TODO: Check DMA transfer limit or restart DMA to get all data. */
config.channel_cfg = CXD56_UDMA_SINGLE;
if (buffer != NULL)
{
config.channel_cfg |= CXD56_UDMA_MEMINCR;
dst = buffer;
}
else
{
config.channel_cfg |= CXD56_UDMA_NOINCR;
dst = (char *)&dstbuf;
}
if (length & 1)
{
config.channel_cfg |= CXD56_UDMA_XFERSIZE_BYTE;
}
else if (length & 2)
{
config.channel_cfg |= CXD56_UDMA_XFERSIZE_HWORD;
maxlen = 2048;
}
else
{
config.channel_cfg |= CXD56_UDMA_XFERSIZE_WORD;
maxlen = 4096;
}
if (((uint32_t)dst >= CXD56_RAM_BASE) &&
((uint32_t)dst <= (CXD56_RAM_BASE + CXD56_RAM_SIZE)))
{
need_wakelock = 1;
up_pm_acquire_wakelock(&wlock);
}
rest = length;
while (rest > 0)
{
dmalen = MIN(rest, maxlen);
cxd56_rxudmasetup(fifo->dma, outlet, (uintptr_t)dst, dmalen, config);
cxd56_udmastart(fifo->dma, seq_fifodmadone, fifo);
/* Wait for DMA is done */
seq_semtake(&fifo->dmawait);
if (fifo->dmaresult)
{
/* ERROR */
length = length - rest;
break;
}
dst += dmalen;
rest -= dmalen;
}
if (need_wakelock)
{
up_pm_release_wakelock(&wlock);
}
#else
/* Get sensor data from FIFO by PIO */
if (length & 1)
{
if (buffer != NULL)
{
seq_read8(outlet, (FAR uint8_t *)buffer, length);
}
else
{
for (i = 0; i < length; i++)
{
getreg8(outlet);
}
}
}
else if (length & 2)
{
if (buffer != NULL)
{
seq_read16(outlet, (FAR uint16_t *)buffer, length);
}
else
{
for (i = 0; i < length / 2; i++)
{
getreg16(outlet);
}
}
}
else
{
if (buffer != NULL)
{
seq_read32(outlet, (FAR uint32_t *)buffer, length);
}
else
{
for (i = 0; i < length / 4; i++)
{
getreg32(outlet);
}
}
}
#endif
return length;
}
/****************************************************************************
* Name: seq_ioctl
*
* Description:
* Lower ioctl handler. SCU supported driver must call this API from it's
* ioctl().
*
* Input Parameters:
* seq - An instance of sequencer
* fifoid - FIFO ID (0 - 3, decimator only)
* cmd - SCU ioctl command (SCUIOC_*)
* arg - Arguments for cmd
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int seq_ioctl(FAR struct seq_s *seq, int fifoid, int cmd, unsigned long arg)
{
int ret = OK;
if (fifoid < 0 || fifoid > 2)
{
set_errno(-EINVAL);
return -1;
}
scuinfo("cmd = %04x, arg = %08x\n", cmd, arg);
switch (cmd)
{
/* Set FIFO memory and initialize it. Arg: uint16_t */
case SCUIOC_SETFIFO:
{
ret = seq_fifoinit(seq, fifoid, (uint16_t)arg);
}
break;
/* Free FIFO memory. Arg: none */
case SCUIOC_FREEFIFO:
{
struct decimator_s *deci = (struct decimator_s *)seq;
FAR struct scufifo_s *fifo;
/* Check sequencer already stopped. */
if (seq_fifoisactive(seq, fifoid))
{
ret = -EBUSY;
break;
}
if (seq->type & SEQ_TYPE_DECI)
{
fifo = deci->dfifo[fifoid].fifo;
deci->dfifo[fifoid].fifo = NULL;
}
else
{
fifo = seq->fifo;
seq->fifo = NULL;
}
seq_fifofree(fifo);
}
break;
/* Set sampling rate. Arg: uint8_t */
case SCUIOC_SETSAMPLE:
{
ret = seq_setsamplingrate(seq, arg);
}
break;
/**
* Enable/disable sign conversion feature
* Arg: unsigned long, 0 = off, other = on
*/
case SCUIOC_SETSIGNCONV:
{
seq_setsignconversion(seq->id, arg);
}
break;
/**
* Enable offset/gain adjustment preprocessing.
* Arg: Pointer of adjust_xyz_t
* If arg is null, just enable offset/gain (use current setting value).
*/
case SCUIOC_SETOGADJUST:
{
if (arg)
{
FAR struct adjust_xyz_s *p =
(FAR struct adjust_xyz_s *)(uintptr_t)arg;
ret = seq_setadjustment(seq, p);
if (!ret)
{
seq_offsetgainenable(seq->id, true);
}
}
else
{
seq_offsetgainenable(seq->id, true);
}
}
break;
/**
* Disable offset/gain adjustment preprocessing.
* Arg: None
*/
case SCUIOC_CLROGADJUST:
{
seq_offsetgainenable(seq->id, false);
}
break;
/**
* Set IIR filter position and coefficiencies.
* Arg: Pointer of struct math_filter_s
*/
case SCUIOC_SETFILTER:
{
FAR struct math_filter_s *f =
(FAR struct math_filter_s *)(uintptr_t)arg;
FAR struct scufifo_s *fifo = seq_getfifo(seq, fifoid);
if (seq_fifoisactive(seq, fifoid))
{
ret = -EBUSY;
break;
}
if (seq->type & SEQ_TYPE_DECI)
{
FAR struct decimator_s *dec = (struct decimator_s *)seq;
fifo = dec->dfifo[fifoid].fifo;
}
ret = seq_setfilter(fifo, f->pos, f->filter);
}
break;
/**
* Set event notifier
* Arg: Pointer of struct scuev_notify_s
*/
case SCUIOC_SETNOTIFY:
{
FAR struct scuev_notify_s *en =
(FAR struct scuev_notify_s *)(uintptr_t)arg;
FAR struct scufifo_s *fifo = seq_getfifo(seq, fifoid);
if (seq_fifoisactive(seq, fifoid))
{
ret = -EBUSY;
break;
}
if (seq->type & SEQ_TYPE_DECI)
{
FAR struct decimator_s *dec = (struct decimator_s *)seq;
fifo = dec->dfifo[fifoid].fifo;
}
ret = seq_seteventnotifier(fifo, en);
}
break;
/**
* Set number of elements per sample for mathfunc
* Arg: uint8_t
*/
case SCUIOC_SETELEMENTS:
{
seq_setmathwritevecelenum(seq->id, (uint8_t)arg);
}
break;
/**
* Set decimation parameters
* Arg: Pointer of struct decimation_s
*/
case SCUIOC_SETDECIMATION:
{
FAR struct decimation_s *d =
(FAR struct decimation_s *)(uintptr_t)arg;
FAR struct decimator_s *deci = (FAR struct decimator_s *)seq;
FAR struct decimation_fifo_s *dfifo = &deci->dfifo[fifoid];
if (!(seq->type & SEQ_TYPE_DECI))
{
ret = -EINVAL;
break;
}
/**
* Now only save decimation parameters because decimation parameter
* cannot be set while sequencer running.
*/
dfifo->ratio = d->ratio;
dfifo->leveladj = d->leveladj;
dfifo->forcethrough = d->forcethrough;
}
break;
/**
* Set FIFO watermark
* Arg: Pointer of struct scufifo_wm_s
*/
case SCUIOC_SETWATERMARK:
{
FAR struct scufifo_wm_s *wm =
(FAR struct scufifo_wm_s *)(uintptr_t)arg;
ret = seq_setwatermark(seq, fifoid, wm);
}
break;
/* Sequencer start */
case SCUIOC_START:
{
ret = seq_start(seq, fifoid);
}
break;
/* Sequencer stop */
case SCUIOC_STOP:
{
seq_stop(seq, fifoid);
}
break;
/* Set FIFO overwrite mode */
case SCUIOC_SETFIFOMODE:
{
seq_setfifomode(seq, fifoid, arg);
}
break;
/* Delete FIFO data */
case SCUIOC_DELFIFODATA:
{
ret = seq_read(seq, fifoid, NULL, (uint16_t)arg);
}
break;
default:
scuerr("Unrecognized cmd: %d\n", cmd);
ret = -EIO;
break;
}
if (ret < 0)
{
set_errno(-ret);
}
return ret;
}
/****************************************************************************
* Name: seq_close
*
* Description:
* Close sequencer
*
* Input Parameters:
* seq - An instance of sequencer
*
****************************************************************************/
void seq_close(FAR struct seq_s *seq)
{
irqstate_t flags;
DEBUGASSERT(seq);
if (seq->type & SEQ_TYPE_DECI)
{
FAR struct decimator_s *deci = (FAR struct decimator_s *)seq;
int i;
flags = enter_critical_section();
deci_free(seq->id);
leave_critical_section(flags);
for (i = 0; i < 3; i++)
{
seq_fifofree(deci->dfifo[i].fifo);
deci->dfifo[i].fifo = NULL;
}
}
else
{
flags = enter_critical_section();
seq_free(seq->id);
leave_critical_section(flags);
seq_fifofree(seq->fifo);
seq->fifo = NULL;
}
kmm_free(seq);
}
/****************************************************************************
* Name: scu_initialize
****************************************************************************/
void scu_initialize(void)
{
struct cxd56_scudev_s *priv = &g_scudev;
int i;
#ifdef CONFIG_CXD56_UDMAC
cxd56_udmainitialize();
#endif
memset(priv, 0, sizeof(struct cxd56_scudev_s));
nxsem_init(&priv->syncexc, 0, 1);
nxsem_init(&priv->syncwait, 0, 0);
nxsem_set_protocol(&priv->syncwait, SEM_PRIO_NONE);
for (i = 0; i < 3; i++)
{
nxsem_init(&priv->oneshotwait[i], 0, 0);
nxsem_set_protocol(&priv->oneshotwait[i], SEM_PRIO_NONE);
}
scufifo_initialize();
/**
* If SCU clock has been already enabled, keep SCU running without loading
* and reset of SCU firmware.
*/
if (false == cxd56_scuseq_clock_is_enabled())
{
/**
* Enable SCU clock. This process must do before loading firmware
* because SCU instruction RAM is not accessible.
*/
cxd56_scuseq_clock_enable();
/* Load firmware & clear data RAM */
memcpy((void *)CXD56_SCU_SEQ_IRAM_BASE, scu_isopprog_array,
sizeof_scu_isopprog_array);
memset((void *)CXD56_SCU_SEQ_DRAM_BASE, 0, 0x324);
/* Release SCU reset to bring up SCU firmware */
cxd56_scuseq_release_reset();
/* Initialize SCU registers */
scu_hwinit();
}
/* Enable error interrupt */
putreg32(0x007ffe00, SCU_INT_ENABLE_ERR_0);
putreg32(0x03ff, SCU_INT_ENABLE_ERR_2);
/* Enable SCU IRQ */
irq_attach(CXD56_IRQ_SCU_3, seq_scuirqhandler, &g_scudev);
up_enable_irq(CXD56_IRQ_SCU_3);
}
/****************************************************************************
* Name: scu_uninitialize
****************************************************************************/
void scu_uninitialize(void)
{
struct cxd56_scudev_s *priv = &g_scudev;
int i;
/* Request don't sleep */
seq_inhibitrequest(REQ_SLEEP, true);
up_disable_irq(CXD56_IRQ_SCU_3);
cxd56_scuseq_clock_disable();
nxsem_destroy(&priv->syncwait);
nxsem_destroy(&priv->syncexc);
for (i = 0; i < 3; i++)
{
nxsem_destroy(&priv->oneshotwait[i]);
}
}