1*66011a08SMarek Vasut // SPDX-License-Identifier: GPL-2.0+
2*66011a08SMarek Vasut /*
3*66011a08SMarek Vasut * Designware APB Timer driver
4*66011a08SMarek Vasut *
5*66011a08SMarek Vasut * Copyright (C) 2018 Marek Vasut <marex@denx.de>
6*66011a08SMarek Vasut */
7*66011a08SMarek Vasut
8*66011a08SMarek Vasut #include <common.h>
9*66011a08SMarek Vasut #include <dm.h>
10*66011a08SMarek Vasut #include <clk.h>
11*66011a08SMarek Vasut #include <timer.h>
12*66011a08SMarek Vasut
13*66011a08SMarek Vasut #include <asm/io.h>
14*66011a08SMarek Vasut #include <asm/arch/timer.h>
15*66011a08SMarek Vasut
16*66011a08SMarek Vasut #define DW_APB_LOAD_VAL 0x0
17*66011a08SMarek Vasut #define DW_APB_CURR_VAL 0x4
18*66011a08SMarek Vasut #define DW_APB_CTRL 0x8
19*66011a08SMarek Vasut
20*66011a08SMarek Vasut DECLARE_GLOBAL_DATA_PTR;
21*66011a08SMarek Vasut
22*66011a08SMarek Vasut struct dw_apb_timer_priv {
23*66011a08SMarek Vasut fdt_addr_t regs;
24*66011a08SMarek Vasut };
25*66011a08SMarek Vasut
dw_apb_timer_get_count(struct udevice * dev,u64 * count)26*66011a08SMarek Vasut static int dw_apb_timer_get_count(struct udevice *dev, u64 *count)
27*66011a08SMarek Vasut {
28*66011a08SMarek Vasut struct dw_apb_timer_priv *priv = dev_get_priv(dev);
29*66011a08SMarek Vasut
30*66011a08SMarek Vasut /*
31*66011a08SMarek Vasut * The DW APB counter counts down, but this function
32*66011a08SMarek Vasut * requires the count to be incrementing. Invert the
33*66011a08SMarek Vasut * result.
34*66011a08SMarek Vasut */
35*66011a08SMarek Vasut *count = ~readl(priv->regs + DW_APB_CURR_VAL);
36*66011a08SMarek Vasut
37*66011a08SMarek Vasut return 0;
38*66011a08SMarek Vasut }
39*66011a08SMarek Vasut
dw_apb_timer_probe(struct udevice * dev)40*66011a08SMarek Vasut static int dw_apb_timer_probe(struct udevice *dev)
41*66011a08SMarek Vasut {
42*66011a08SMarek Vasut struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
43*66011a08SMarek Vasut struct dw_apb_timer_priv *priv = dev_get_priv(dev);
44*66011a08SMarek Vasut struct clk clk;
45*66011a08SMarek Vasut int ret;
46*66011a08SMarek Vasut
47*66011a08SMarek Vasut ret = clk_get_by_index(dev, 0, &clk);
48*66011a08SMarek Vasut if (ret)
49*66011a08SMarek Vasut return ret;
50*66011a08SMarek Vasut
51*66011a08SMarek Vasut uc_priv->clock_rate = clk_get_rate(&clk);
52*66011a08SMarek Vasut
53*66011a08SMarek Vasut clk_free(&clk);
54*66011a08SMarek Vasut
55*66011a08SMarek Vasut /* init timer */
56*66011a08SMarek Vasut writel(0xffffffff, priv->regs + DW_APB_LOAD_VAL);
57*66011a08SMarek Vasut writel(0xffffffff, priv->regs + DW_APB_CURR_VAL);
58*66011a08SMarek Vasut setbits_le32(priv->regs + DW_APB_CTRL, 0x3);
59*66011a08SMarek Vasut
60*66011a08SMarek Vasut return 0;
61*66011a08SMarek Vasut }
62*66011a08SMarek Vasut
dw_apb_timer_ofdata_to_platdata(struct udevice * dev)63*66011a08SMarek Vasut static int dw_apb_timer_ofdata_to_platdata(struct udevice *dev)
64*66011a08SMarek Vasut {
65*66011a08SMarek Vasut struct dw_apb_timer_priv *priv = dev_get_priv(dev);
66*66011a08SMarek Vasut
67*66011a08SMarek Vasut priv->regs = dev_read_addr(dev);
68*66011a08SMarek Vasut
69*66011a08SMarek Vasut return 0;
70*66011a08SMarek Vasut }
71*66011a08SMarek Vasut
72*66011a08SMarek Vasut static const struct timer_ops dw_apb_timer_ops = {
73*66011a08SMarek Vasut .get_count = dw_apb_timer_get_count,
74*66011a08SMarek Vasut };
75*66011a08SMarek Vasut
76*66011a08SMarek Vasut static const struct udevice_id dw_apb_timer_ids[] = {
77*66011a08SMarek Vasut { .compatible = "snps,dw-apb-timer" },
78*66011a08SMarek Vasut {}
79*66011a08SMarek Vasut };
80*66011a08SMarek Vasut
81*66011a08SMarek Vasut U_BOOT_DRIVER(dw_apb_timer) = {
82*66011a08SMarek Vasut .name = "dw_apb_timer",
83*66011a08SMarek Vasut .id = UCLASS_TIMER,
84*66011a08SMarek Vasut .ops = &dw_apb_timer_ops,
85*66011a08SMarek Vasut .probe = dw_apb_timer_probe,
86*66011a08SMarek Vasut .of_match = dw_apb_timer_ids,
87*66011a08SMarek Vasut .ofdata_to_platdata = dw_apb_timer_ofdata_to_platdata,
88*66011a08SMarek Vasut .priv_auto_alloc_size = sizeof(struct dw_apb_timer_priv),
89*66011a08SMarek Vasut };
90