Merge git://git.denx.de/u-boot-arc
This replaces legacy arch/arc/lib/timer.c implementation and allows us to describe ARC Timers in Device Tree. Among other things that way we may properly inherit Timer's clock from CPU's clock s they really run synchronously.
This commit is contained in:
commit
5cf618ee60
@ -13,6 +13,9 @@ config ARC
|
||||
select HAVE_PRIVATE_LIBGCC
|
||||
select SUPPORT_OF_CONTROL
|
||||
select ARCH_EARLY_INIT_R
|
||||
select CLK
|
||||
select TIMER
|
||||
select ARC_TIMER
|
||||
|
||||
config ARM
|
||||
bool "ARM architecture"
|
||||
|
@ -118,7 +118,7 @@ config SYS_DCACHE_OFF
|
||||
|
||||
choice
|
||||
prompt "Target select"
|
||||
default TARGET_AXS10X
|
||||
default TARGET_AXS103
|
||||
|
||||
config TARGET_TB100
|
||||
bool "Support tb100"
|
||||
@ -126,8 +126,11 @@ config TARGET_TB100
|
||||
config TARGET_NSIM
|
||||
bool "Support standalone nSIM & Free nSIM"
|
||||
|
||||
config TARGET_AXS10X
|
||||
bool "Support Synopsys Designware SDP board (AXS101 & AXS103)"
|
||||
config TARGET_AXS101
|
||||
bool "Support Synopsys Designware SDP board AXS101"
|
||||
|
||||
config TARGET_AXS103
|
||||
bool "Support Synopsys Designware SDP board AXS103"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
dtb-$(CONFIG_TARGET_AXS10X) += axs10x.dtb
|
||||
dtb-$(CONFIG_TARGET_AXS101) += axs101.dtb
|
||||
dtb-$(CONFIG_TARGET_AXS103) += axs103.dtb
|
||||
dtb-$(CONFIG_TARGET_NSIM) += nsim.dtb
|
||||
dtb-$(CONFIG_TARGET_TB100) += abilis_tb100.dtb
|
||||
|
||||
|
@ -8,13 +8,19 @@
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
aliases {
|
||||
console = &uart0;
|
||||
};
|
||||
|
||||
cpu_card {
|
||||
core_clk: core_clk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <500000000>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@ff100000 {
|
||||
compatible = "snps,dw-apb-uart";
|
||||
reg = <0xff100000 0x1000>;
|
||||
|
19
arch/arc/dts/axc001.dtsi
Normal file
19
arch/arc/dts/axc001.dtsi
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/include/ "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
cpu_card {
|
||||
core_clk: core_clk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <750000000>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
19
arch/arc/dts/axc003.dtsi
Normal file
19
arch/arc/dts/axc003.dtsi
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/include/ "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
cpu_card {
|
||||
core_clk: core_clk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <100000000>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
17
arch/arc/dts/axs101.dts
Normal file
17
arch/arc/dts/axs101.dts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
/dts-v1/;
|
||||
|
||||
/include/ "axc001.dtsi"
|
||||
/include/ "axs10x_mb.dtsi"
|
||||
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
stdout-path = &uart0;
|
||||
};
|
||||
};
|
||||
|
17
arch/arc/dts/axs103.dts
Normal file
17
arch/arc/dts/axs103.dts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
/dts-v1/;
|
||||
|
||||
/include/ "axc003.dtsi"
|
||||
/include/ "axs10x_mb.dtsi"
|
||||
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
stdout-path = &uart0;
|
||||
};
|
||||
};
|
||||
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
/dts-v1/;
|
||||
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
aliases {
|
||||
console = &uart0;
|
||||
};
|
||||
|
||||
clocks {
|
||||
apbclk: apbclk {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <50000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial0@e0022000 {
|
||||
compatible = "snps,dw-apb-uart";
|
||||
reg = <0xe0022000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
};
|
||||
|
||||
ethernet@e0018000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "altr,socfpga-stmmac";
|
||||
reg = < 0xe0018000 0x2000 >;
|
||||
interrupts = < 25 >;
|
||||
interrupt-names = "macirq";
|
||||
phy-mode = "gmii";
|
||||
snps,pbl = < 32 >;
|
||||
clocks = <&apbclk>;
|
||||
clock-names = "stmmaceth";
|
||||
max-speed = <100>;
|
||||
};
|
||||
|
||||
ehci@0xe0040000 {
|
||||
compatible = "generic-ehci";
|
||||
reg = < 0xe0040000 0x100 >;
|
||||
interrupts = < 8 >;
|
||||
};
|
||||
|
||||
ohci@0xe0060000 {
|
||||
compatible = "generic-ohci";
|
||||
reg = < 0xe0060000 0x100 >;
|
||||
interrupts = < 8 >;
|
||||
};
|
||||
};
|
66
arch/arc/dts/axs10x_mb.dtsi
Normal file
66
arch/arc/dts/axs10x_mb.dtsi
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/ {
|
||||
axs10x_mb@e0000000 {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x00000000 0xe0000000 0x10000000>;
|
||||
u-boot,dm-pre-reloc;
|
||||
|
||||
clocks {
|
||||
compatible = "simple-bus";
|
||||
u-boot,dm-pre-reloc;
|
||||
|
||||
apbclk: apbclk {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <50000000>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
uartclk: uartclk {
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <33333333>;
|
||||
#clock-cells = <0>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
};
|
||||
|
||||
ethernet@18000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "altr,socfpga-stmmac";
|
||||
reg = < 0x18000 0x2000 >;
|
||||
interrupts = < 25 >;
|
||||
interrupt-names = "macirq";
|
||||
phy-mode = "gmii";
|
||||
snps,pbl = < 32 >;
|
||||
clocks = <&apbclk>;
|
||||
clock-names = "stmmaceth";
|
||||
max-speed = <100>;
|
||||
};
|
||||
|
||||
ehci@0x40000 {
|
||||
compatible = "generic-ehci";
|
||||
reg = < 0x40000 0x100 >;
|
||||
interrupts = < 8 >;
|
||||
};
|
||||
|
||||
ohci@0x60000 {
|
||||
compatible = "generic-ohci";
|
||||
reg = < 0x60000 0x100 >;
|
||||
interrupts = < 8 >;
|
||||
};
|
||||
|
||||
uart0: serial0@22000 {
|
||||
compatible = "snps,dw-apb-uart";
|
||||
reg = <0x22000 0x100>;
|
||||
clocks = <&uartclk>;
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
};
|
||||
};
|
||||
};
|
@ -8,17 +8,23 @@
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
aliases {
|
||||
console = &arcuart0;
|
||||
};
|
||||
|
||||
cpu_card {
|
||||
core_clk: core_clk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <70000000>;
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
};
|
||||
|
||||
arcuart0: serial@0xc0fc1000 {
|
||||
compatible = "snps,arc-uart";
|
||||
reg = <0xc0fc1000 0x100>;
|
||||
clock-frequency = <80000000>;
|
||||
clock-frequency = <70000000>;
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -9,5 +9,22 @@
|
||||
#size-cells = <1>;
|
||||
chosen { };
|
||||
aliases { };
|
||||
memory { device_type = "memory"; reg = <0 0>; };
|
||||
|
||||
cpu_card {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
u-boot,dm-pre-reloc;
|
||||
|
||||
timer@0 {
|
||||
compatible = "snps,arc-timer";
|
||||
clocks = <&core_clk>;
|
||||
reg = <0 1>;
|
||||
};
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x80000000 0x10000000>; /* 256M */
|
||||
};
|
||||
};
|
||||
|
@ -33,6 +33,10 @@
|
||||
#define ARC_AUX_TIMER0_CTRL 0x22 /* Timer 0 control */
|
||||
#define ARC_AUX_TIMER0_LIMIT 0x23 /* Timer 0 limit */
|
||||
|
||||
#define ARC_AUX_TIMER1_CNT 0x100 /* Timer 1 count */
|
||||
#define ARC_AUX_TIMER1_CTRL 0x101 /* Timer 1 control */
|
||||
#define ARC_AUX_TIMER1_LIMIT 0x102 /* Timer 1 limit */
|
||||
|
||||
#define ARC_AUX_INTR_VEC_BASE 0x25
|
||||
|
||||
/* Data cache related auxiliary registers */
|
||||
|
@ -18,7 +18,6 @@ obj-y += memcmp.o
|
||||
obj-y += memcpy-700.o
|
||||
obj-y += memset.o
|
||||
obj-y += reset.o
|
||||
obj-y += timer.o
|
||||
obj-y += ints_low.o
|
||||
obj-y += init_helpers.o
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/arcregs.h>
|
||||
|
||||
#define NH_MODE (1 << 1) /* Disable timer if CPU is halted */
|
||||
|
||||
int timer_init(void)
|
||||
{
|
||||
write_aux_reg(ARC_AUX_TIMER0_CTRL, NH_MODE);
|
||||
/* Set max value for counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER0_LIMIT, 0xffffffff);
|
||||
/* Set initial count value and restart counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER0_CNT, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long timer_read_counter(void)
|
||||
{
|
||||
return read_aux_reg(ARC_AUX_TIMER0_CNT);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
if TARGET_AXS10X
|
||||
if TARGET_AXS101 || TARGET_AXS103
|
||||
|
||||
config SYS_BOARD
|
||||
default "axs10x"
|
||||
|
@ -1,8 +1,9 @@
|
||||
CONFIG_ARC=y
|
||||
CONFIG_SYS_DCACHE_OFF=y
|
||||
CONFIG_TARGET_AXS101=y
|
||||
CONFIG_SYS_TEXT_BASE=0x81000000
|
||||
CONFIG_SYS_CLK_FREQ=750000000
|
||||
CONFIG_DEFAULT_DEVICE_TREE="axs10x"
|
||||
CONFIG_DEFAULT_DEVICE_TREE="axs101"
|
||||
CONFIG_BOOTDELAY=3
|
||||
CONFIG_BOARD_EARLY_INIT_F=y
|
||||
CONFIG_SYS_PROMPT="AXS# "
|
||||
@ -18,7 +19,6 @@ CONFIG_OF_CONTROL=y
|
||||
CONFIG_OF_EMBED=y
|
||||
CONFIG_NET_RANDOM_ETHADDR=y
|
||||
CONFIG_DM=y
|
||||
CONFIG_CLK=y
|
||||
CONFIG_SYS_I2C_DW=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_DW=y
|
||||
|
@ -2,7 +2,7 @@ CONFIG_ARC=y
|
||||
CONFIG_ISA_ARCV2=y
|
||||
CONFIG_SYS_TEXT_BASE=0x81000000
|
||||
CONFIG_SYS_CLK_FREQ=100000000
|
||||
CONFIG_DEFAULT_DEVICE_TREE="axs10x"
|
||||
CONFIG_DEFAULT_DEVICE_TREE="axs103"
|
||||
CONFIG_BOOTDELAY=3
|
||||
CONFIG_BOARD_EARLY_INIT_F=y
|
||||
CONFIG_SYS_PROMPT="AXS# "
|
||||
@ -18,7 +18,6 @@ CONFIG_OF_CONTROL=y
|
||||
CONFIG_OF_EMBED=y
|
||||
CONFIG_NET_RANDOM_ETHADDR=y
|
||||
CONFIG_DM=y
|
||||
CONFIG_CLK=y
|
||||
CONFIG_SYS_I2C_DW=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_DW=y
|
||||
|
24
doc/device-tree-bindings/timer/arc_timer.txt
Normal file
24
doc/device-tree-bindings/timer/arc_timer.txt
Normal file
@ -0,0 +1,24 @@
|
||||
ARC Timer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "snps,arc-timer".
|
||||
- reg : Specifies timer ID, could be either 0 or 1.
|
||||
- clocks : Specifies clocks that drives the counter.
|
||||
|
||||
Examples:
|
||||
|
||||
timer@0 {
|
||||
compatible = "snps,arc-timer";
|
||||
clocks = <&core_clk>;
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
timer@1 {
|
||||
compatible = "snps,arc-timer";
|
||||
clocks = <&core_clk>;
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
NOTE: if you specify both timers, clocks always should be the same
|
||||
as each timer is driven by the same core clock.
|
@ -65,4 +65,13 @@ config STI_TIMER
|
||||
help
|
||||
Select this to enable a timer for STi devices.
|
||||
|
||||
config ARC_TIMER
|
||||
bool "ARC timer support"
|
||||
depends on TIMER && ARC && CLK
|
||||
help
|
||||
Select this to enable built-in ARC timers.
|
||||
ARC cores may have up to 2 built-in timers: timer0 and timer1,
|
||||
usually at least one of them exists. Either of them is supported
|
||||
in U-Boot.
|
||||
|
||||
endmenu
|
||||
|
@ -11,3 +11,4 @@ obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
|
||||
obj-$(CONFIG_OMAP_TIMER) += omap-timer.o
|
||||
obj-$(CONFIG_AST_TIMER) += ast_timer.o
|
||||
obj-$(CONFIG_STI_TIMER) += sti-timer.o
|
||||
obj-$(CONFIG_ARC_TIMER) += arc_timer.o
|
||||
|
113
drivers/timer/arc_timer.c
Normal file
113
drivers/timer/arc_timer.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Synopsys, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <timer.h>
|
||||
#include <asm/arcregs.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define NH_MODE (1 << 1)
|
||||
|
||||
/*
|
||||
* ARC timer control registers are mapped to auxiliary address space.
|
||||
* There are special ARC asm command to access that addresses.
|
||||
* Therefore we use built-in functions to read from and write to timer
|
||||
* control register.
|
||||
*/
|
||||
|
||||
/* Driver private data. Contains timer id. Could be either 0 or 1. */
|
||||
struct arc_timer_priv {
|
||||
uint timer_id;
|
||||
};
|
||||
|
||||
static int arc_timer_get_count(struct udevice *dev, u64 *count)
|
||||
{
|
||||
u32 val = 0;
|
||||
struct arc_timer_priv *priv = dev_get_priv(dev);
|
||||
|
||||
switch (priv->timer_id) {
|
||||
case 0:
|
||||
val = read_aux_reg(ARC_AUX_TIMER0_CNT);
|
||||
break;
|
||||
case 1:
|
||||
val = read_aux_reg(ARC_AUX_TIMER1_CNT);
|
||||
break;
|
||||
}
|
||||
*count = timer_conv_64(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arc_timer_probe(struct udevice *dev)
|
||||
{
|
||||
int id;
|
||||
struct arc_timer_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Get registers offset and size */
|
||||
id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
|
||||
if (id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (id > 1)
|
||||
return -ENXIO;
|
||||
|
||||
priv->timer_id = (uint)id;
|
||||
|
||||
/*
|
||||
* In ARC core there're special registers (Auxiliary or AUX) in its
|
||||
* separate memory space that are used for accessing some hardware
|
||||
* features of the core. They are not mapped in normal memory space
|
||||
* and also always have the same location regardless core configuration.
|
||||
* Thus to simplify understanding of the programming model we chose to
|
||||
* access AUX regs of Timer0 and Timer1 separately instead of using
|
||||
* offsets from some base address.
|
||||
*/
|
||||
|
||||
switch (priv->timer_id) {
|
||||
case 0:
|
||||
/* Disable timer if CPU is halted */
|
||||
write_aux_reg(ARC_AUX_TIMER0_CTRL, NH_MODE);
|
||||
/* Set max value for counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER0_LIMIT, 0xffffffff);
|
||||
/* Set initial count value and restart counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER0_CNT, 0);
|
||||
break;
|
||||
case 1:
|
||||
/* Disable timer if CPU is halted */
|
||||
write_aux_reg(ARC_AUX_TIMER1_CTRL, NH_MODE);
|
||||
/* Set max value for counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER1_LIMIT, 0xffffffff);
|
||||
/* Set initial count value and restart counter/timer */
|
||||
write_aux_reg(ARC_AUX_TIMER1_CNT, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct timer_ops arc_timer_ops = {
|
||||
.get_count = arc_timer_get_count,
|
||||
};
|
||||
|
||||
static const struct udevice_id arc_timer_ids[] = {
|
||||
{ .compatible = "snps,arc-timer" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(arc_timer) = {
|
||||
.name = "arc_timer",
|
||||
.id = UCLASS_TIMER,
|
||||
.of_match = arc_timer_ids,
|
||||
.probe = arc_timer_probe,
|
||||
.ops = &arc_timer_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
.priv_auto_alloc_size = sizeof(struct arc_timer_priv),
|
||||
};
|
@ -11,8 +11,6 @@
|
||||
/*
|
||||
* CPU configuration
|
||||
*/
|
||||
#define CONFIG_SYS_TIMER_RATE CONFIG_SYS_CLK_FREQ
|
||||
|
||||
#define ARC_FPGA_PERIPHERAL_BASE 0xE0000000
|
||||
#define ARC_APB_PERIPHERAL_BASE 0xF0000000
|
||||
#define ARC_DWMMC_BASE (ARC_FPGA_PERIPHERAL_BASE + 0x15000)
|
||||
|
@ -9,11 +9,6 @@
|
||||
|
||||
#include <linux/sizes.h>
|
||||
|
||||
/*
|
||||
* CPU configuration
|
||||
*/
|
||||
#define CONFIG_SYS_TIMER_RATE CONFIG_SYS_CLK_FREQ
|
||||
|
||||
/*
|
||||
* Memory configuration
|
||||
*/
|
||||
|
@ -9,11 +9,6 @@
|
||||
|
||||
#include <linux/sizes.h>
|
||||
|
||||
/*
|
||||
* CPU configuration
|
||||
*/
|
||||
#define CONFIG_SYS_TIMER_RATE CONFIG_SYS_CLK_FREQ
|
||||
|
||||
/*
|
||||
* Memory configuration
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user