1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/*
* Clocksource using the Low Power Timer found in the Low Power Controller (LPC)
*
* Copyright (C) 2015 STMicroelectronics – All Rights Reserved
*
* Author(s): Francesco Virlinzi <francesco.virlinzi@st.com>
* Ajit Pal Singh <ajitpal.singh@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <dt-bindings/mfd/st-lpc.h>
/* Low Power Timer */
#define LPC_LPT_LSB_OFF 0x400
#define LPC_LPT_MSB_OFF 0x404
#define LPC_LPT_START_OFF 0x408
static struct st_clksrc_ddata {
struct clk *clk;
void __iomem *base;
} ddata;
static void __init st_clksrc_reset(void)
{
writel_relaxed(0, ddata.base + LPC_LPT_START_OFF);
writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF);
writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF);
writel_relaxed(1, ddata.base + LPC_LPT_START_OFF);
}
static int __init st_clksrc_init(void)
{
unsigned long rate;
int ret;
st_clksrc_reset();
rate = clk_get_rate(ddata.clk);
ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF,
"clksrc-st-lpc", rate, 300, 32,
clocksource_mmio_readl_up);
if (ret) {
pr_err("clksrc-st-lpc: Failed to register clocksource\n");
return ret;
}
return 0;
}
static int __init st_clksrc_setup_clk(struct device_node *np)
{
struct clk *clk;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("clksrc-st-lpc: Failed to get LPC clock\n");
return PTR_ERR(clk);
}
if (clk_prepare_enable(clk)) {
pr_err("clksrc-st-lpc: Failed to enable LPC clock\n");
return -EINVAL;
}
if (!clk_get_rate(clk)) {
pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n");
clk_disable_unprepare(clk);
return -EINVAL;
}
ddata.clk = clk;
return 0;
}
static void __init st_clksrc_of_register(struct device_node *np)
{
int ret;
uint32_t mode;
ret = of_property_read_u32(np, "st,lpc-mode", &mode);
if (ret) {
pr_err("clksrc-st-lpc: An LPC mode must be provided\n");
return;
}
/* LPC can either run as a Clocksource or in RTC or WDT mode */
if (mode != ST_LPC_MODE_CLKSRC)
return;
ddata.base = of_iomap(np, 0);
if (!ddata.base) {
pr_err("clksrc-st-lpc: Unable to map iomem\n");
return;
}
if (st_clksrc_setup_clk(np)) {
iounmap(ddata.base);
return;
}
if (st_clksrc_init()) {
clk_disable_unprepare(ddata.clk);
clk_put(ddata.clk);
iounmap(ddata.base);
return;
}
pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n",
clk_get_rate(ddata.clk));
}
CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
|