]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2016 Synopsys, Inc. All rights reserved. | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <errno.h> | |
10 | #include <timer.h> | |
11 | #include <asm/arcregs.h> | |
12 | #include <asm/io.h> | |
13 | ||
14 | DECLARE_GLOBAL_DATA_PTR; | |
15 | ||
16 | #define NH_MODE (1 << 1) | |
17 | ||
18 | /* | |
19 | * ARC timer control registers are mapped to auxiliary address space. | |
20 | * There are special ARC asm command to access that addresses. | |
21 | * Therefore we use built-in functions to read from and write to timer | |
22 | * control register. | |
23 | */ | |
24 | ||
25 | /* Driver private data. Contains timer id. Could be either 0 or 1. */ | |
26 | struct arc_timer_priv { | |
27 | uint timer_id; | |
28 | }; | |
29 | ||
30 | static int arc_timer_get_count(struct udevice *dev, u64 *count) | |
31 | { | |
32 | u32 val = 0; | |
33 | struct arc_timer_priv *priv = dev_get_priv(dev); | |
34 | ||
35 | switch (priv->timer_id) { | |
36 | case 0: | |
37 | val = read_aux_reg(ARC_AUX_TIMER0_CNT); | |
38 | break; | |
39 | case 1: | |
40 | val = read_aux_reg(ARC_AUX_TIMER1_CNT); | |
41 | break; | |
42 | } | |
43 | *count = timer_conv_64(val); | |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | static int arc_timer_probe(struct udevice *dev) | |
49 | { | |
50 | int id; | |
51 | struct arc_timer_priv *priv = dev_get_priv(dev); | |
52 | ||
53 | /* Get registers offset and size */ | |
54 | id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1); | |
55 | if (id < 0) | |
56 | return -EINVAL; | |
57 | ||
58 | if (id > 1) | |
59 | return -ENXIO; | |
60 | ||
61 | priv->timer_id = (uint)id; | |
62 | ||
63 | /* | |
64 | * In ARC core there're special registers (Auxiliary or AUX) in its | |
65 | * separate memory space that are used for accessing some hardware | |
66 | * features of the core. They are not mapped in normal memory space | |
67 | * and also always have the same location regardless core configuration. | |
68 | * Thus to simplify understanding of the programming model we chose to | |
69 | * access AUX regs of Timer0 and Timer1 separately instead of using | |
70 | * offsets from some base address. | |
71 | */ | |
72 | ||
73 | switch (priv->timer_id) { | |
74 | case 0: | |
75 | /* Disable timer if CPU is halted */ | |
76 | write_aux_reg(ARC_AUX_TIMER0_CTRL, NH_MODE); | |
77 | /* Set max value for counter/timer */ | |
78 | write_aux_reg(ARC_AUX_TIMER0_LIMIT, 0xffffffff); | |
79 | /* Set initial count value and restart counter/timer */ | |
80 | write_aux_reg(ARC_AUX_TIMER0_CNT, 0); | |
81 | break; | |
82 | case 1: | |
83 | /* Disable timer if CPU is halted */ | |
84 | write_aux_reg(ARC_AUX_TIMER1_CTRL, NH_MODE); | |
85 | /* Set max value for counter/timer */ | |
86 | write_aux_reg(ARC_AUX_TIMER1_LIMIT, 0xffffffff); | |
87 | /* Set initial count value and restart counter/timer */ | |
88 | write_aux_reg(ARC_AUX_TIMER1_CNT, 0); | |
89 | break; | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | ||
96 | static const struct timer_ops arc_timer_ops = { | |
97 | .get_count = arc_timer_get_count, | |
98 | }; | |
99 | ||
100 | static const struct udevice_id arc_timer_ids[] = { | |
101 | { .compatible = "snps,arc-timer" }, | |
102 | {} | |
103 | }; | |
104 | ||
105 | U_BOOT_DRIVER(arc_timer) = { | |
106 | .name = "arc_timer", | |
107 | .id = UCLASS_TIMER, | |
108 | .of_match = arc_timer_ids, | |
109 | .probe = arc_timer_probe, | |
110 | .ops = &arc_timer_ops, | |
111 | .flags = DM_FLAG_PRE_RELOC, | |
112 | .priv_auto_alloc_size = sizeof(struct arc_timer_priv), | |
113 | }; |