1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * RTC driver for the Armada 38x Marvell SoCs 4 * 5 * Copyright (C) 2021 Marek Beh��n <kabel@kernel.org> 6 * 7 * Based on Linux' driver by Gregory Clement and Marvell 8 */ 9 10#include <asm/io.h> 11#include <dm.h> 12#include <linux/delay.h> 13#include <rtc.h> 14 15#define RTC_STATUS 0x0 16#define RTC_TIME 0xC 17#define RTC_CONF_TEST 0x1C 18 19/* Armada38x SoC registers */ 20#define RTC_38X_BRIDGE_TIMING_CTL 0x0 21#define RTC_38X_PERIOD_OFFS 0 22#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) 23#define RTC_38X_READ_DELAY_OFFS 26 24#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) 25 26#define SAMPLE_NR 100 27 28struct armada38x_rtc { 29 void __iomem *regs; 30 void __iomem *regs_soc; 31}; 32 33/* 34 * According to Erratum RES-3124064 we have to do some configuration in MBUS. 35 * To read an RTC register we need to read it 100 times and return the most 36 * frequent value. 37 * To write an RTC register we need to write 2x zero into STATUS register, 38 * followed by the proper write. Linux adds an 5 us delay after this, so we do 39 * it here as well. 40 */ 41static void update_38x_mbus_timing_params(struct armada38x_rtc *rtc) 42{ 43 u32 reg; 44 45 reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); 46 reg &= ~RTC_38X_PERIOD_MASK; 47 reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ 48 reg &= ~RTC_38X_READ_DELAY_MASK; 49 reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ 50 writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); 51} 52 53static void armada38x_rtc_write(u32 val, struct armada38x_rtc *rtc, u8 reg) 54{ 55 writel(0, rtc->regs + RTC_STATUS); 56 writel(0, rtc->regs + RTC_STATUS); 57 writel(val, rtc->regs + reg); 58 udelay(5); 59} 60 61static u32 armada38x_rtc_read(struct armada38x_rtc *rtc, u8 reg) 62{ 63 u8 counts[SAMPLE_NR], max_idx; 64 u32 samples[SAMPLE_NR], max; 65 int i, j, last; 66 67 for (i = 0, last = 0; i < SAMPLE_NR; ++i) { 68 u32 sample = readl(rtc->regs + reg); 69 70 /* find if this value was already read */ 71 for (j = 0; j < last; ++j) { 72 if (samples[j] == sample) 73 break; 74 } 75 76 if (j < last) { 77 /* if yes, increment count */ 78 ++counts[j]; 79 } else { 80 /* if not, add */ 81 samples[last] = sample; 82 counts[last] = 1; 83 ++last; 84 } 85 } 86 87 /* finally find the sample that was read the most */ 88 max = 0; 89 max_idx = 0; 90 91 for (i = 0; i < last; ++i) { 92 if (counts[i] > max) { 93 max = counts[i]; 94 max_idx = i; 95 } 96 } 97 98 return samples[max_idx]; 99} 100 101static int armada38x_rtc_get(struct udevice *dev, struct rtc_time *tm) 102{ 103 struct armada38x_rtc *rtc = dev_get_priv(dev); 104 u32 time; 105 106 time = armada38x_rtc_read(rtc, RTC_TIME); 107 108 rtc_to_tm(time, tm); 109 110 return 0; 111} 112 113static int armada38x_rtc_reset(struct udevice *dev) 114{ 115 struct armada38x_rtc *rtc = dev_get_priv(dev); 116 u32 reg; 117 118 reg = armada38x_rtc_read(rtc, RTC_CONF_TEST); 119 120 if (reg & 0xff) { 121 armada38x_rtc_write(0, rtc, RTC_CONF_TEST); 122 mdelay(500); 123 armada38x_rtc_write(0, rtc, RTC_TIME); 124 armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS); 125 } 126 127 return 0; 128} 129 130static int armada38x_rtc_set(struct udevice *dev, const struct rtc_time *tm) 131{ 132 struct armada38x_rtc *rtc = dev_get_priv(dev); 133 unsigned long time; 134 135 time = rtc_mktime(tm); 136 137 if (time > U32_MAX) 138 printf("%s: requested time to set will overflow\n", dev->name); 139 140 armada38x_rtc_reset(dev); 141 armada38x_rtc_write(time, rtc, RTC_TIME); 142 143 return 0; 144} 145 146static int armada38x_probe(struct udevice *dev) 147{ 148 struct armada38x_rtc *rtc = dev_get_priv(dev); 149 150 rtc->regs = dev_remap_addr_name(dev, "rtc"); 151 if (!rtc->regs) 152 goto err; 153 154 rtc->regs_soc = dev_remap_addr_name(dev, "rtc-soc"); 155 if (!rtc->regs_soc) 156 goto err; 157 158 update_38x_mbus_timing_params(rtc); 159 160 return 0; 161err: 162 printf("%s: io address missing\n", dev->name); 163 return -ENODEV; 164} 165 166static const struct rtc_ops armada38x_rtc_ops = { 167 .get = armada38x_rtc_get, 168 .set = armada38x_rtc_set, 169 .reset = armada38x_rtc_reset, 170}; 171 172static const struct udevice_id armada38x_rtc_ids[] = { 173 { .compatible = "marvell,armada-380-rtc", .data = 0 }, 174 { } 175}; 176 177U_BOOT_DRIVER(rtc_armada38x) = { 178 .name = "rtc-armada38x", 179 .id = UCLASS_RTC, 180 .of_match = armada38x_rtc_ids, 181 .probe = armada38x_probe, 182 .priv_auto = sizeof(struct armada38x_rtc), 183 .ops = &armada38x_rtc_ops, 184}; 185