1 /* 2 * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <dm/lists.h> 10 #include <dm/device-internal.h> 11 #include <errno.h> 12 #include <timer.h> 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 /* 17 * Implement a timer uclass to work with lib/time.c. The timer is usually 18 * a 32/64 bits free-running up counter. The get_rate() method is used to get 19 * the input clock frequency of the timer. The get_count() method is used 20 * to get the current 64 bits count value. If the hardware is counting down, 21 * the value should be inversed inside the method. There may be no real 22 * tick, and no timer interrupt. 23 */ 24 25 int timer_get_count(struct udevice *dev, u64 *count) 26 { 27 const struct timer_ops *ops = device_get_ops(dev); 28 29 if (!ops->get_count) 30 return -ENOSYS; 31 32 return ops->get_count(dev, count); 33 } 34 35 unsigned long timer_get_rate(struct udevice *dev) 36 { 37 struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); 38 39 return uc_priv->clock_rate; 40 } 41 42 static int timer_pre_probe(struct udevice *dev) 43 { 44 struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); 45 46 uc_priv->clock_rate = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 47 "clock-frequency", 0); 48 49 return 0; 50 } 51 52 static int timer_post_probe(struct udevice *dev) 53 { 54 struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); 55 56 if (!uc_priv->clock_rate) 57 return -EINVAL; 58 59 return 0; 60 } 61 62 u64 timer_conv_64(u32 count) 63 { 64 /* increment tbh if tbl has rolled over */ 65 if (count < gd->timebase_l) 66 gd->timebase_h++; 67 gd->timebase_l = count; 68 return ((u64)gd->timebase_h << 32) | gd->timebase_l; 69 } 70 71 int notrace dm_timer_init(void) 72 { 73 const void *blob = gd->fdt_blob; 74 struct udevice *dev = NULL; 75 int node; 76 int ret; 77 78 if (gd->timer) 79 return 0; 80 81 /* Check for a chosen timer to be used for tick */ 82 node = fdtdec_get_chosen_node(blob, "tick-timer"); 83 if (node < 0) { 84 /* No chosen timer, trying first available timer */ 85 ret = uclass_first_device(UCLASS_TIMER, &dev); 86 if (ret) 87 return ret; 88 if (!dev) 89 return -ENODEV; 90 } else { 91 if (uclass_get_device_by_of_offset(UCLASS_TIMER, node, &dev)) { 92 /* 93 * If the timer is not marked to be bound before 94 * relocation, bind it anyway. 95 */ 96 if (node > 0 && 97 !lists_bind_fdt(gd->dm_root, blob, node, &dev)) { 98 ret = device_probe(dev); 99 if (ret) 100 return ret; 101 } 102 } 103 } 104 105 if (dev) { 106 gd->timer = dev; 107 return 0; 108 } 109 110 return -ENODEV; 111 } 112 113 UCLASS_DRIVER(timer) = { 114 .id = UCLASS_TIMER, 115 .name = "timer", 116 .pre_probe = timer_pre_probe, 117 .flags = DM_UC_FLAG_SEQ_ALIAS, 118 .post_probe = timer_post_probe, 119 .per_device_auto_alloc_size = sizeof(struct timer_dev_priv), 120 }; 121