SAML32: Update some DFLL logic
This commit is contained in:
parent
7000cf8193
commit
8075f8ab84
@ -107,12 +107,18 @@ static inline void sam_xosc32k_config(void);
|
|||||||
static inline void sam_osc32k_config(void);
|
static inline void sam_osc32k_config(void);
|
||||||
#endif
|
#endif
|
||||||
static inline void sam_osc16m_config(void);
|
static inline void sam_osc16m_config(void);
|
||||||
#ifdef BOARD_DFLL_ENABLE
|
#ifdef BOARD_DFLL48M_ENABLE
|
||||||
static inline void sam_dfll48m_config(void);
|
static inline void sam_dfll48m_config(void);
|
||||||
|
static inline void sam_dfll48m_enable(void);
|
||||||
#endif
|
#endif
|
||||||
#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_DFLL_ENABLE) && \
|
#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_DFLL48M_ENABLE) && \
|
||||||
!defined(BOARD_DFLL_OPENLOOP)
|
!defined(BOARD_DFLL48M_OPENLOOP)
|
||||||
static inline void sam_dfll_reference(void);
|
static inline void sam_dfll48m_reference(void);
|
||||||
|
#endif
|
||||||
|
#ifdef BOARD_FDPLL96M_ENABLE
|
||||||
|
static inline void sam_fdpll96m_config(void);
|
||||||
|
static inline void sam_fdpll96m_enable(void);
|
||||||
|
static inline void sam_fdpll96m_reference(void);
|
||||||
#endif
|
#endif
|
||||||
static void sam_gclck_waitsyncbusy(void);
|
static void sam_gclck_waitsyncbusy(void);
|
||||||
static void sam_gclk_config(FAR const struct sam_gclkconfig_s *config);
|
static void sam_gclk_config(FAR const struct sam_gclkconfig_s *config);
|
||||||
@ -751,27 +757,30 @@ static inline void sam_osc16m_config(void)
|
|||||||
* Name: sam_dfll48m_config
|
* Name: sam_dfll48m_config
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Configure the DFLL based on settings in the board.h header file.
|
* Configure the DFLL48M based on settings in the board.h header file.
|
||||||
* Depends on:
|
* Depends on:
|
||||||
*
|
*
|
||||||
* BOARD_DFLL_OPENLOOP - Boolean (defined / not defined)
|
* BOARD_DFLL48M_CLOSEDLOOP - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_TRACKAFTERFINELOCK - Boolean (defined / not defined)
|
* BOARD_DFLL48M_OPENLOOP - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_KEEPLOCKONWAKEUP - Boolean (defined / not defined)
|
* BOARD_DFLL48M_RECOVERY - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_ENABLECHILLCYCLE - Boolean (defined / not defined)
|
* BOARD_DFLL48M_TRACKAFTERFINELOCK - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_QUICKLOCK - Boolean (defined / not defined)
|
* BOARD_DFLL48M_KEEPLOCKONWAKEUP - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_ONDEMAND - Boolean (defined / not defined)
|
* BOARD_DFLL48M_ENABLECHILLCYCLE - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_COARSEVALUE - Value
|
* BOARD_DFLL48M_QUICKLOCK - Boolean (defined / not defined)
|
||||||
* BOARD_DFLL_FINEVALUE - Value
|
* BOARD_DFLL48M_RUNINSTDBY - Boolean (defined / not defined)
|
||||||
|
* BOARD_DFLL48M_ONDEMAND - Boolean (defined / not defined)
|
||||||
|
* BOARD_DFLL48M_COARSEVALUE - Value
|
||||||
|
* BOARD_DFLL48M_FINEVALUE - Value
|
||||||
*
|
*
|
||||||
* Open Loop mode only:
|
* Open Loop mode only:
|
||||||
* BOARD_DFLL_COARSEVALUE - Value
|
* BOARD_DFLL48M_COARSEVALUE - Value
|
||||||
* BOARD_DFLL_FINEVALUE - Value
|
* BOARD_DFLL48M_FINEVALUE - Value
|
||||||
*
|
*
|
||||||
* Closed loop mode only:
|
* Closed loop mode only:
|
||||||
* BOARD_DFLL_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
* BOARD_DFLL48M_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
||||||
* BOARD_DFLL_MULTIPLIER - Value
|
* BOARD_DFLL48M_MULTIPLIER - Value
|
||||||
* BOARD_DFLL_MAXCOARSESTEP - Value
|
* BOARD_DFLL48M_MAXCOARSESTEP - Value
|
||||||
* BOARD_DFLL_MAXFINESTEP - Value
|
* BOARD_DFLL48M_MAXFINESTEP - Value
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* None
|
* None
|
||||||
@ -781,36 +790,66 @@ static inline void sam_osc16m_config(void)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifdef BOARD_DFLL_ENABLE
|
#ifdef BOARD_DFLL48M_ENABLE
|
||||||
static inline void sam_dfll48m_config(void)
|
static inline void sam_dfll48m_config(void)
|
||||||
{
|
{
|
||||||
uint16_t control;
|
uint16_t control;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
|
||||||
|
/* Disable ONDEMAND mode while writing configurations (Errata 9905). This
|
||||||
|
* is probably not necessary on the first time configuration after reset.
|
||||||
|
*/
|
||||||
|
|
||||||
|
control = getreg16(SAM_OSCCTRL_DFLLCTRL);
|
||||||
|
control &= ~(OSCCTRL_DFLLCTRL_ENABLE | OSCCTRL_DFLLCTRL_ONDEMAND);
|
||||||
|
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
||||||
|
|
||||||
|
/* Wait for the DFLL to synchronize */
|
||||||
|
|
||||||
|
while ((getreg32(SAM_OSCCTRL_STATUS) & OSCCTRL_INT_DFLLRDY) == 0);
|
||||||
|
|
||||||
/* Set up the DFLL control register */
|
/* Set up the DFLL control register */
|
||||||
|
|
||||||
control = OSCCTRL_DFLLCTRL_ENABLE; /* Enable the DFLL */
|
control &= ~(OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_STABLE |
|
||||||
|
OSCCTRL_DFLLCTRL_LLAW | OSCCTRL_DFLLCTRL_USBCRM |
|
||||||
|
OSCCTRL_DFLLCTRL_RUNSTDBY | OSCCTRL_DFLLCTRL_CCDIS |
|
||||||
|
OSCCTRL_DFLLCTRL_QLDIS | OSCCTRL_DFLLCTRL_BPLCKC |
|
||||||
|
OSCCTRL_DFLLCTRL_WAITLOCK);
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_OPENLOOP
|
#if defined(BOARD_DFLL48M_CLOSELOOP
|
||||||
control |= OSCCTRL_DFLLCTRL_MODE; /* Closed loop mode */
|
control |= OSCCTRL_DFLLCTRL_MODE; /* Closed loop mode */
|
||||||
|
#if defined(BOARD_DFLL48M_RECOVERY
|
||||||
|
control |= OSCCTRL_DFLLCTRL_USBCRM; /* USB clock recovery mode */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_TRACKAFTERFINELOCK
|
#ifndef BOARD_DFLL48M_TRACKAFTERFINELOCK
|
||||||
control |= OSCCTRL_DFLLCTRL_STABLE; /* FINE calibration fixed after a fine lock */
|
control |= OSCCTRL_DFLLCTRL_STABLE; /* FINE calibration fixed after a fine lock */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_KEEPLOCKONWAKEUP
|
#ifndef BOARD_DFLL48M_KEEPLOCKONWAKEUP
|
||||||
control |= OSCCTRL_DFLLCTRL_LLAW; /* Lose lock after wake */
|
control |= OSCCTRL_DFLLCTRL_LLAW; /* Lose lock after wake */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_ENABLECHILLCYCLE
|
#ifndef BOARD_DFLL48M_RUNINSTDBY
|
||||||
|
control |= OSCCTRL_DFLLCTRL_RUNSTDBY; /* Chill cycle disable */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOARD_DFLL48M_ENABLECHILLCYCLE
|
||||||
control |= OSCCTRL_DFLLCTRL_CCDIS; /* Chill cycle disable */
|
control |= OSCCTRL_DFLLCTRL_CCDIS; /* Chill cycle disable */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_QUICKLOCK
|
#ifndef BOARD_DFLL48M_QUICKLOCK
|
||||||
control |= OSCCTRL_DFLLCTRL_QLDIS; /* Quick lock disable */
|
control |= OSCCTRL_DFLLCTRL_QLDIS; /* Quick lock disable */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOARD_DFLL48M_BPLCKC
|
||||||
|
control |= OSCCTRL_DFLLCTRL_BPLCKC; /* Bypass coarse clock */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOARD_DFLL48M_WAITLOCK
|
||||||
|
control |= OSCCTRL_DFLLCTRL_WAITLOCK; /* Wait lock */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Then enable the DFLL (with ONDEMAND set to zero). */
|
/* Then enable the DFLL (with ONDEMAND set to zero). */
|
||||||
|
|
||||||
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
||||||
@ -821,10 +860,10 @@ static inline void sam_dfll48m_config(void)
|
|||||||
|
|
||||||
/* Set up the open loop mode multiplier register */
|
/* Set up the open loop mode multiplier register */
|
||||||
|
|
||||||
#ifndef BOARD_DFLL_OPENLOOP
|
#ifndef BOARD_DFLL48M_OPENLOOP
|
||||||
regval = OSCCTRL_DFLLMUL_CSTEP(BOARD_DFLL_MAXCOARSESTEP) |
|
regval = OSCCTRL_DFLLMUL_CSTEP(BOARD_DFLL48M_MAXCOARSESTEP) |
|
||||||
OSCCTRL_DFLLMUL_FSTEP(BOARD_DFLL_MAXFINESTEP) |
|
OSCCTRL_DFLLMUL_FSTEP(BOARD_DFLL48M_MAXFINESTEP) |
|
||||||
OSCCTRL_DFLLMUL_MUL(BOARD_DFLL_MULTIPLIER);
|
OSCCTRL_DFLLMUL_MUL(BOARD_DFLL48M_MULTIPLIER);
|
||||||
putreg32(regval, SAM_OSCCTRL_DFLLMUL);
|
putreg32(regval, SAM_OSCCTRL_DFLLMUL);
|
||||||
#else
|
#else
|
||||||
putreg32(0, SAM_OSCCTRL_DFLLMUL);
|
putreg32(0, SAM_OSCCTRL_DFLLMUL);
|
||||||
@ -832,29 +871,19 @@ static inline void sam_dfll48m_config(void)
|
|||||||
|
|
||||||
/* Set up the DFLL value register */
|
/* Set up the DFLL value register */
|
||||||
|
|
||||||
regval = OSCCTRL_DFLLVAL_COARSE(BOARD_DFLL_COARSEVALUE) |
|
regval = OSCCTRL_DFLLVAL_COARSE(BOARD_DFLL48M_COARSEVALUE) |
|
||||||
OSCCTRL_DFLLVAL_FINE(BOARD_DFLL_FINEVALUE);
|
OSCCTRL_DFLLVAL_FINE(BOARD_DFLL48M_FINEVALUE);
|
||||||
putreg32(regval, SAM_OSCCTRL_DFLLVAL);
|
putreg32(regval, SAM_OSCCTRL_DFLLVAL);
|
||||||
|
|
||||||
/* Finally, set the state of the ONDEMAND bit if necessary */
|
|
||||||
|
|
||||||
#ifdef BOARD_DFLL_ONDEMAND
|
|
||||||
control |= OSCCTRL_DFLLCTRL_ONDEMAND; /* On demand control */
|
|
||||||
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define sam_dfll48m_config()
|
# define sam_dfll48m_config()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_dfll_reference
|
* Name: sam_dfll48m_enable
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Enable DFLL reference clock if in closed loop mode.
|
* Enable the DFLL48M.
|
||||||
* Depends on:
|
|
||||||
*
|
|
||||||
* BOARD_DFLL_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* None
|
* None
|
||||||
@ -864,9 +893,53 @@ static inline void sam_dfll48m_config(void)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_DFLL_ENABLE) && \
|
#ifdef BOARD_DFLL48M_ENABLE
|
||||||
!defined(BOARD_DFLL_OPENLOOP)
|
static inline void sam_dfll48m_enable(void)
|
||||||
static inline void sam_dfll_reference(void)
|
{
|
||||||
|
uint16_t control;
|
||||||
|
uint32_t regval;
|
||||||
|
|
||||||
|
/* Enable the DFLL48M (with ONDEMAND still set to zero). */
|
||||||
|
|
||||||
|
control = getreg16(SAM_OSCCTRL_DFLLCTRL);
|
||||||
|
control |= OSCCTRL_DFLLCTRL_ENABLE; /* Enable the DFLL */
|
||||||
|
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
||||||
|
|
||||||
|
/* Wait for the DFLL to synchronize */
|
||||||
|
|
||||||
|
while ((getreg32(SAM_OSCCTRL_STATUS) & OSCCTRL_INT_DFLLRDY) == 0);
|
||||||
|
|
||||||
|
/* Finally, set the state of the ONDEMAND bit if necessary */
|
||||||
|
|
||||||
|
#ifdef BOARD_DFLL48M_ONDEMAND
|
||||||
|
control |= OSCCTRL_DFLLCTRL_ONDEMAND; /* On demand control */
|
||||||
|
putreg16(control, SAM_OSCCTRL_DFLLCTRL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define sam_dfll48m_enable()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_dfll48m_reference
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Enable DFLL reference clock if in closed loop mode.
|
||||||
|
* Depends on:
|
||||||
|
*
|
||||||
|
* BOARD_DFLL48M_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_DFLL48M_ENABLE) && \
|
||||||
|
!defined(BOARD_DFLL48M_OPENLOOP)
|
||||||
|
static inline void sam_dfll48m_reference(void)
|
||||||
{
|
{
|
||||||
uint16_t regval;
|
uint16_t regval;
|
||||||
|
|
||||||
@ -885,7 +958,7 @@ static inline void sam_dfll_reference(void)
|
|||||||
* NOTE: We could enable write lock here to prevent further modification
|
* NOTE: We could enable write lock here to prevent further modification
|
||||||
*/
|
*/
|
||||||
|
|
||||||
regval = (BOARD_DFLL_SRCGCLKGEN | GCLK_CLKCTRL_ID_DFLL48M);
|
regval = (BOARD_DFLL48M_SRCGCLKGEN | GCLK_CLKCTRL_ID_DFLL48M);
|
||||||
putreg16(regval, SAM_GCLK_CLKCTRL);
|
putreg16(regval, SAM_GCLK_CLKCTRL);
|
||||||
|
|
||||||
/* Enable the DFLL reference clock */
|
/* Enable the DFLL reference clock */
|
||||||
@ -901,7 +974,98 @@ static inline void sam_dfll_reference(void)
|
|||||||
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) == 0);
|
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) == 0);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define sam_dfll_reference()
|
# define sam_dfll48m_reference()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_fdpll96m_config
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Configure the DFLL based on settings in the board.h header file.
|
||||||
|
* Depends on:
|
||||||
|
*
|
||||||
|
* BOARD_FDPLL96M_OPENLOOP - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_TRACKAFTERFINELOCK - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_KEEPLOCKONWAKEUP - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_ENABLECHILLCYCLE - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_QUICKLOCK - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_ONDEMAND - Boolean (defined / not defined)
|
||||||
|
* BOARD_FDPLL96M_COARSEVALUE - Value
|
||||||
|
* BOARD_FDPLL96M_FINEVALUE - Value
|
||||||
|
*
|
||||||
|
* Open Loop mode only:
|
||||||
|
* BOARD_FDPLL96M_COARSEVALUE - Value
|
||||||
|
* BOARD_FDPLL96M_FINEVALUE - Value
|
||||||
|
*
|
||||||
|
* Closed loop mode only:
|
||||||
|
* BOARD_FDPLL96M_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
||||||
|
* BOARD_FDPLL96M_MULTIPLIER - Value
|
||||||
|
* BOARD_FDPLL96M_MAXCOARSESTEP - Value
|
||||||
|
* BOARD_FDPLL96M_MAXFINESTEP - Value
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef BOARD_FDPLL96M_ENABLE
|
||||||
|
static inline void sam_fdpll96m_config(void)
|
||||||
|
{
|
||||||
|
#error Missing logic
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define sam_fdpll96m_config()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_fdpll96m_enable
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Enable the FDPLL96M.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef BOARD_FDPLL96M_ENABLE
|
||||||
|
static inline void sam_fdpll96m_enable(void)
|
||||||
|
{
|
||||||
|
#error Missing logic
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define sam_fdpll96m_enable()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_fdpll96m_reference
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Enable FDPLL96M reference clock.
|
||||||
|
* Depends on:
|
||||||
|
*
|
||||||
|
* BOARD_FDPLL96M_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_FDPLL96M_ENABLE)
|
||||||
|
static inline void sam_fdpll96m_reference(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define sam_fdpll96m_enable()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -1105,7 +1269,11 @@ static inline void sam_config_gclks(void)
|
|||||||
|
|
||||||
/* Enable DFLL reference clock if the DFLL is enabled in closed loop mode */
|
/* Enable DFLL reference clock if the DFLL is enabled in closed loop mode */
|
||||||
|
|
||||||
sam_dfll_reference();
|
sam_dfll48m_reference();
|
||||||
|
|
||||||
|
/* Enable FDPLL reference clock if the DFLL is enabled */
|
||||||
|
|
||||||
|
sam_fdpll96m_reference();
|
||||||
|
|
||||||
/* Configure the GCLK_MAIN last as it may depend on the DFLL or other
|
/* Configure the GCLK_MAIN last as it may depend on the DFLL or other
|
||||||
* generators
|
* generators
|
||||||
@ -1231,6 +1399,14 @@ void sam_clockconfig(void)
|
|||||||
|
|
||||||
sam_config_gclks();
|
sam_config_gclks();
|
||||||
|
|
||||||
|
/* Enable DFLL48M */
|
||||||
|
|
||||||
|
sam_dfll48m_enable();
|
||||||
|
|
||||||
|
/* Enable FDPLL96M */
|
||||||
|
|
||||||
|
sam_fdpll96m_enable();
|
||||||
|
|
||||||
/* Set CPU and BUS clock dividers */
|
/* Set CPU and BUS clock dividers */
|
||||||
|
|
||||||
sam_dividers();
|
sam_dividers();
|
||||||
|
Loading…
Reference in New Issue
Block a user