Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: sun6i: Expose the 32kHz oscillator

The RTC controls the input source of the main 32kHz oscillator in the
system, feeding it to the clock unit too.

By default, this is using an internal, very inaccurate (+/- 30%)
oscillator with a divider to make it roughly around 32kHz. This is however
quite impractical for the RTC, since our time will not be tracked properly.

Since this oscillator is an input of the main clock unit, and since that
clock unit will be probed using CLK_OF_DECLARE, we have to use it as well,
leading to a two stage probe: one to enable the clock, the other one to
enable the RTC.

There is also a slight change in the binding that is required (and should
have been from the beginning), since we'll need a phandle to the external
oscillator used on that board. We support the old binding by not allowing
to switch to the external oscillator and only using the internal one (which
was the previous behaviour) in the case where we're missing that phandle.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

authored by

Maxime Ripard and committed by
Alexandre Belloni
3855c2c3 fb61bb82

+143 -13
+10
Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
··· 8 8 memory mapped region. 9 9 - interrupts : IRQ lines for the RTC alarm 0 and alarm 1, in that order. 10 10 11 + Required properties for new device trees 12 + - clocks : phandle to the 32kHz external oscillator 13 + - clock-output-names : name of the LOSC clock created 14 + - #clock-cells : must be equals to 1. The RTC provides two clocks: the 15 + LOSC and its external output, with index 0 and 1 16 + respectively. 17 + 11 18 Example: 12 19 13 20 rtc: rtc@01f00000 { 14 21 compatible = "allwinner,sun6i-a31-rtc"; 15 22 reg = <0x01f00000 0x54>; 16 23 interrupts = <0 40 4>, <0 41 4>; 24 + clock-output-names = "osc32k"; 25 + clocks = <&ext_osc32k>; 26 + #clock-cells = <1>; 17 27 };
+133 -13
drivers/rtc/rtc-sun6i.c
··· 20 20 * more details. 21 21 */ 22 22 23 + #include <linux/clk.h> 24 + #include <linux/clk-provider.h> 23 25 #include <linux/delay.h> 24 26 #include <linux/err.h> 25 27 #include <linux/fs.h> ··· 35 33 #include <linux/of_device.h> 36 34 #include <linux/platform_device.h> 37 35 #include <linux/rtc.h> 36 + #include <linux/slab.h> 38 37 #include <linux/types.h> 39 38 40 39 /* Control register */ ··· 46 43 #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) 47 44 #define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) 48 45 #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) 46 + 47 + #define SUN6I_LOSC_CLK_PRESCAL 0x0008 49 48 50 49 /* RTC */ 51 50 #define SUN6I_RTC_YMD 0x0010 ··· 122 117 int irq; 123 118 unsigned long alarm; 124 119 120 + struct clk_hw hw; 121 + struct clk_hw *int_osc; 122 + struct clk *losc; 123 + 125 124 spinlock_t lock; 126 125 }; 126 + 127 + static struct sun6i_rtc_dev *sun6i_rtc; 128 + 129 + static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw, 130 + unsigned long parent_rate) 131 + { 132 + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); 133 + u32 val; 134 + 135 + val = readl(rtc->base + SUN6I_LOSC_CTRL); 136 + if (val & SUN6I_LOSC_CTRL_EXT_OSC) 137 + return parent_rate; 138 + 139 + val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL); 140 + val &= GENMASK(4, 0); 141 + 142 + return parent_rate / (val + 1); 143 + } 144 + 145 + static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw) 146 + { 147 + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); 148 + 149 + return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC; 150 + } 151 + 152 + static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index) 153 + { 154 + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); 155 + unsigned long flags; 156 + u32 val; 157 + 158 + if (index > 1) 159 + return -EINVAL; 160 + 161 + spin_lock_irqsave(&rtc->lock, flags); 162 + val = readl(rtc->base + SUN6I_LOSC_CTRL); 163 + val &= ~SUN6I_LOSC_CTRL_EXT_OSC; 164 + val |= SUN6I_LOSC_CTRL_KEY; 165 + val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0; 166 + writel(val, rtc->base + SUN6I_LOSC_CTRL); 167 + spin_unlock_irqrestore(&rtc->lock, flags); 168 + 169 + return 0; 170 + } 171 + 172 + static const struct clk_ops sun6i_rtc_osc_ops = { 173 + .recalc_rate = sun6i_rtc_osc_recalc_rate, 174 + 175 + .get_parent = sun6i_rtc_osc_get_parent, 176 + .set_parent = sun6i_rtc_osc_set_parent, 177 + }; 178 + 179 + static void __init sun6i_rtc_clk_init(struct device_node *node) 180 + { 181 + struct clk_hw_onecell_data *clk_data; 182 + struct sun6i_rtc_dev *rtc; 183 + struct clk_init_data init = { 184 + .ops = &sun6i_rtc_osc_ops, 185 + }; 186 + const char *parents[2]; 187 + 188 + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); 189 + if (!rtc) 190 + return; 191 + spin_lock_init(&rtc->lock); 192 + 193 + clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws), 194 + GFP_KERNEL); 195 + if (!clk_data) 196 + return; 197 + spin_lock_init(&rtc->lock); 198 + 199 + rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); 200 + if (!rtc->base) { 201 + pr_crit("Can't map RTC registers"); 202 + return; 203 + } 204 + 205 + /* Switch to the external, more precise, oscillator */ 206 + writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, 207 + rtc->base + SUN6I_LOSC_CTRL); 208 + 209 + /* Deal with old DTs */ 210 + if (!of_get_property(node, "clocks", NULL)) 211 + return; 212 + 213 + rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, 214 + "rtc-int-osc", 215 + NULL, 0, 216 + 667000, 217 + 300000000); 218 + if (IS_ERR(rtc->int_osc)) { 219 + pr_crit("Couldn't register the internal oscillator\n"); 220 + return; 221 + } 222 + 223 + parents[0] = clk_hw_get_name(rtc->int_osc); 224 + parents[1] = of_clk_get_parent_name(node, 0); 225 + 226 + rtc->hw.init = &init; 227 + 228 + init.parent_names = parents; 229 + init.num_parents = of_clk_get_parent_count(node) + 1; 230 + of_property_read_string(node, "clock-output-names", &init.name); 231 + 232 + rtc->losc = clk_register(NULL, &rtc->hw); 233 + if (IS_ERR(rtc->losc)) { 234 + pr_crit("Couldn't register the LOSC clock\n"); 235 + return; 236 + } 237 + 238 + clk_data->num = 1; 239 + clk_data->hws[0] = &rtc->hw; 240 + of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); 241 + 242 + /* Yes, I know, this is ugly. */ 243 + sun6i_rtc = rtc; 244 + } 245 + CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc", 246 + sun6i_rtc_clk_init); 127 247 128 248 static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) 129 249 { ··· 493 363 494 364 static int sun6i_rtc_probe(struct platform_device *pdev) 495 365 { 496 - struct sun6i_rtc_dev *chip; 497 - struct resource *res; 366 + struct sun6i_rtc_dev *chip = sun6i_rtc; 498 367 int ret; 499 368 500 - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 501 369 if (!chip) 502 - return -ENOMEM; 503 - spin_lock_init(&chip->lock); 370 + return -ENODEV; 504 371 505 372 platform_set_drvdata(pdev, chip); 506 373 chip->dev = &pdev->dev; 507 - 508 - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 509 - chip->base = devm_ioremap_resource(&pdev->dev, res); 510 - if (IS_ERR(chip->base)) 511 - return PTR_ERR(chip->base); 512 374 513 375 chip->irq = platform_get_irq(pdev, 0); 514 376 if (chip->irq < 0) { ··· 541 419 /* disable alarm wakeup */ 542 420 writel(0, chip->base + SUN6I_ALARM_CONFIG); 543 421 544 - /* switch to the external, more precise, oscillator */ 545 - writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, 546 - chip->base + SUN6I_LOSC_CTRL); 422 + clk_prepare_enable(chip->losc); 547 423 548 424 chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, 549 425 &sun6i_rtc_ops, THIS_MODULE);