OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | commit f94ffbc2c2a4128c4412bb483d0807722dfb682b |
2 | Author: Russell King <rmk+kernel@armlinux.org.uk> |
||
3 | Date: Fri Sep 29 11:23:31 2017 +0100 |
||
4 | |||
5 | rtc: armada38x: add support for trimming the RTC |
||
6 | |||
7 | Add support for trimming the RTC using the offset mechanism. This RTC |
||
8 | supports two modes: low update mode and high update mode. Low update |
||
9 | mode has finer precision than high update mode, so we use the low mode |
||
10 | where possible. |
||
11 | |||
12 | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
||
13 | Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> |
||
14 | |||
15 | --- a/drivers/rtc/rtc-armada38x.c |
||
16 | +++ b/drivers/rtc/rtc-armada38x.c |
||
17 | @@ -28,6 +28,8 @@ |
||
18 | #define RTC_IRQ_AL_EN BIT(0) |
||
19 | #define RTC_IRQ_FREQ_EN BIT(1) |
||
20 | #define RTC_IRQ_FREQ_1HZ BIT(2) |
||
21 | +#define RTC_CCR 0x18 |
||
22 | +#define RTC_CCR_MODE BIT(15) |
||
23 | |||
24 | #define RTC_TIME 0xC |
||
25 | #define RTC_ALARM1 0x10 |
||
26 | @@ -343,18 +345,117 @@ static irqreturn_t armada38x_rtc_alarm_i |
||
27 | return IRQ_HANDLED; |
||
28 | } |
||
29 | |||
30 | +/* |
||
31 | + * The information given in the Armada 388 functional spec is complex. |
||
32 | + * They give two different formulas for calculating the offset value, |
||
33 | + * but when considering "Offset" as an 8-bit signed integer, they both |
||
34 | + * reduce down to (we shall rename "Offset" as "val" here): |
||
35 | + * |
||
36 | + * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 |
||
37 | + * |
||
38 | + * Converting to time, f = 1/t: |
||
39 | + * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 |
||
40 | + * |
||
41 | + * => t_measured / t_ideal = val * resolution + 1 |
||
42 | + * |
||
43 | + * "offset" in the RTC interface is defined as: |
||
44 | + * t = t0 * (1 + offset * 1e-9) |
||
45 | + * where t is the desired period, t0 is the measured period with a zero |
||
46 | + * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, |
||
47 | + * offset = (t_ideal / t_measured - 1) / 1e-9 |
||
48 | + * |
||
49 | + * => t_ideal / t_measured = offset * 1e-9 + 1 |
||
50 | + * |
||
51 | + * so: |
||
52 | + * |
||
53 | + * offset * 1e-9 + 1 = 1 / (val * resolution + 1) |
||
54 | + * |
||
55 | + * We want "resolution" to be an integer, so resolution = R * 1e-9, giving |
||
56 | + * offset = 1e18 / (val * R + 1e9) - 1e9 |
||
57 | + * val = (1e18 / (offset + 1e9) - 1e9) / R |
||
58 | + * with a common transformation: |
||
59 | + * f(x) = 1e18 / (x + 1e9) - 1e9 |
||
60 | + * offset = f(val * R) |
||
61 | + * val = f(offset) / R |
||
62 | + * |
||
63 | + * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). |
||
64 | + */ |
||
65 | +static long armada38x_ppb_convert(long ppb) |
||
66 | +{ |
||
67 | + long div = ppb + 1000000000L; |
||
68 | + |
||
69 | + return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; |
||
70 | +} |
||
71 | + |
||
72 | +static int armada38x_rtc_read_offset(struct device *dev, long *offset) |
||
73 | +{ |
||
74 | + struct armada38x_rtc *rtc = dev_get_drvdata(dev); |
||
75 | + unsigned long ccr, flags; |
||
76 | + long ppb_cor; |
||
77 | + |
||
78 | + spin_lock_irqsave(&rtc->lock, flags); |
||
79 | + ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); |
||
80 | + spin_unlock_irqrestore(&rtc->lock, flags); |
||
81 | + |
||
82 | + ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; |
||
83 | + /* ppb_cor + 1000000000L can never be zero */ |
||
84 | + *offset = armada38x_ppb_convert(ppb_cor); |
||
85 | + |
||
86 | + return 0; |
||
87 | +} |
||
88 | + |
||
89 | +static int armada38x_rtc_set_offset(struct device *dev, long offset) |
||
90 | +{ |
||
91 | + struct armada38x_rtc *rtc = dev_get_drvdata(dev); |
||
92 | + unsigned long ccr = 0; |
||
93 | + long ppb_cor, off; |
||
94 | + |
||
95 | + /* |
||
96 | + * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we |
||
97 | + * need to clamp the input. This equates to -484270 .. 488558. |
||
98 | + * Not only is this to stop out of range "off" but also to |
||
99 | + * avoid the division by zero in armada38x_ppb_convert(). |
||
100 | + */ |
||
101 | + offset = clamp(offset, -484270L, 488558L); |
||
102 | + |
||
103 | + ppb_cor = armada38x_ppb_convert(offset); |
||
104 | + |
||
105 | + /* |
||
106 | + * Use low update mode where possible, which gives a better |
||
107 | + * resolution of correction. |
||
108 | + */ |
||
109 | + off = DIV_ROUND_CLOSEST(ppb_cor, 954); |
||
110 | + if (off > 127 || off < -128) { |
||
111 | + ccr = RTC_CCR_MODE; |
||
112 | + off = DIV_ROUND_CLOSEST(ppb_cor, 3815); |
||
113 | + } |
||
114 | + |
||
115 | + /* |
||
116 | + * Armada 388 requires a bit pattern in bits 14..8 depending on |
||
117 | + * the sign bit: { 0, ~S, S, S, S, S, S } |
||
118 | + */ |
||
119 | + ccr |= (off & 0x3fff) ^ 0x2000; |
||
120 | + rtc_delayed_write(ccr, rtc, RTC_CCR); |
||
121 | + |
||
122 | + return 0; |
||
123 | +} |
||
124 | + |
||
125 | static const struct rtc_class_ops armada38x_rtc_ops = { |
||
126 | .read_time = armada38x_rtc_read_time, |
||
127 | .set_time = armada38x_rtc_set_time, |
||
128 | .read_alarm = armada38x_rtc_read_alarm, |
||
129 | .set_alarm = armada38x_rtc_set_alarm, |
||
130 | .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, |
||
131 | + .read_offset = armada38x_rtc_read_offset, |
||
132 | + .set_offset = armada38x_rtc_set_offset, |
||
133 | }; |
||
134 | |||
135 | static const struct rtc_class_ops armada38x_rtc_ops_noirq = { |
||
136 | .read_time = armada38x_rtc_read_time, |
||
137 | .set_time = armada38x_rtc_set_time, |
||
138 | .read_alarm = armada38x_rtc_read_alarm, |
||
139 | + .read_offset = armada38x_rtc_read_offset, |
||
140 | + .set_offset = armada38x_rtc_set_offset, |
||
141 | }; |
||
142 | |||
143 | static const struct armada38x_rtc_data armada38x_data = { |