Skip to content

Commit e28edc5

Browse files
sumanannaandersson
authored andcommitted
remoteproc/omap: Request a timer(s) for remoteproc usage
The remote processors in OMAP4+ SoCs are equipped with internal timers, like the internal SysTick timer in a Cortex M3/M4 NVIC or the CTM timer within Unicache in IPU & DSP. However, these timers are gated when the processor subsystem clock is gated, making them rather difficult to use as OS tick sources. They will not be able to wakeup the processor from any processor-sleep induced clock-gating states. This can be avoided by using an external timer as the tick source, which can be controlled independently by the OMAP remoteproc driver code, but still allowing the processor subsystem clock to be auto-gated when the remoteproc cores are idle. This patch adds the support for OMAP remote processors to request timer(s) to be used by the remoteproc. The timers are enabled and disabled in line with the enabling/disabling of the remoteproc. The timer data is not mandatory if the advanced device management features are not required. The core timer functionality is provided by the OMAP DMTimer clocksource driver, which does not export any API. The logic is implemented through the timer device's platform data ops. The OMAP remoteproc driver mainly requires ops to request/free a dmtimer, and to start/stop a timer. The split ops helps in controlling the timer state without having to request and release a timer everytime it needs to use the timer. NOTE: If the gptimer is already in use by the time IPU and/or DSP are loaded, the processors will fail to boot. Signed-off-by: Suman Anna <[email protected]> Signed-off-by: Tero Kristo <[email protected]> Reviewed-by: Andrew F. Davis <[email protected]> Acked-by: Mathieu Poirier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 8135d1d commit e28edc5

File tree

1 file changed

+290
-1
lines changed

1 file changed

+290
-1
lines changed

drivers/remoteproc/omap_remoteproc.c

Lines changed: 290 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include <linux/regmap.h>
2727
#include <linux/mfd/syscon.h>
2828
#include <linux/reset.h>
29+
#include <clocksource/timer-ti-dm.h>
30+
31+
#include <linux/platform_data/dmtimer-omap.h>
2932

3033
#include "omap_remoteproc.h"
3134
#include "remoteproc_internal.h"
@@ -57,13 +60,25 @@ struct omap_rproc_mem {
5760
size_t size;
5861
};
5962

63+
/**
64+
* struct omap_rproc_timer - data structure for a timer used by a omap rproc
65+
* @odt: timer pointer
66+
* @timer_ops: OMAP dmtimer ops for @odt timer
67+
*/
68+
struct omap_rproc_timer {
69+
struct omap_dm_timer *odt;
70+
const struct omap_dm_timer_ops *timer_ops;
71+
};
72+
6073
/**
6174
* struct omap_rproc - omap remote processor state
6275
* @mbox: mailbox channel handle
6376
* @client: mailbox client to request the mailbox channel
6477
* @boot_data: boot data structure for setting processor boot address
6578
* @mem: internal memory regions data
6679
* @num_mems: number of internal memory regions
80+
* @num_timers: number of rproc timer(s)
81+
* @timers: timer(s) info used by rproc
6782
* @rproc: rproc handle
6883
* @reset: reset handle
6984
*/
@@ -73,6 +88,8 @@ struct omap_rproc {
7388
struct omap_rproc_boot_data *boot_data;
7489
struct omap_rproc_mem *mem;
7590
int num_mems;
91+
int num_timers;
92+
struct omap_rproc_timer *timers;
7693
struct rproc *rproc;
7794
struct reset_control *reset;
7895
};
@@ -97,6 +114,231 @@ struct omap_rproc_dev_data {
97114
const struct omap_rproc_mem_data *mems;
98115
};
99116

117+
/**
118+
* omap_rproc_request_timer() - request a timer for a remoteproc
119+
* @dev: device requesting the timer
120+
* @np: device node pointer to the desired timer
121+
* @timer: handle to a struct omap_rproc_timer to return the timer handle
122+
*
123+
* This helper function is used primarily to request a timer associated with
124+
* a remoteproc. The returned handle is stored in the .odt field of the
125+
* @timer structure passed in, and is used to invoke other timer specific
126+
* ops (like starting a timer either during device initialization or during
127+
* a resume operation, or for stopping/freeing a timer).
128+
*
129+
* Return: 0 on success, otherwise an appropriate failure
130+
*/
131+
static int omap_rproc_request_timer(struct device *dev, struct device_node *np,
132+
struct omap_rproc_timer *timer)
133+
{
134+
int ret;
135+
136+
timer->odt = timer->timer_ops->request_by_node(np);
137+
if (!timer->odt) {
138+
dev_err(dev, "request for timer node %p failed\n", np);
139+
return -EBUSY;
140+
}
141+
142+
ret = timer->timer_ops->set_source(timer->odt, OMAP_TIMER_SRC_SYS_CLK);
143+
if (ret) {
144+
dev_err(dev, "error setting OMAP_TIMER_SRC_SYS_CLK as source for timer node %p\n",
145+
np);
146+
timer->timer_ops->free(timer->odt);
147+
return ret;
148+
}
149+
150+
/* clean counter, remoteproc code will set the value */
151+
timer->timer_ops->set_load(timer->odt, 0, 0);
152+
153+
return 0;
154+
}
155+
156+
/**
157+
* omap_rproc_start_timer() - start a timer for a remoteproc
158+
* @timer: handle to a OMAP rproc timer
159+
*
160+
* This helper function is used to start a timer associated with a remoteproc,
161+
* obtained using the request_timer ops. The helper function needs to be
162+
* invoked by the driver to start the timer (during device initialization)
163+
* or to just resume the timer.
164+
*
165+
* Return: 0 on success, otherwise a failure as returned by DMTimer ops
166+
*/
167+
static inline int omap_rproc_start_timer(struct omap_rproc_timer *timer)
168+
{
169+
return timer->timer_ops->start(timer->odt);
170+
}
171+
172+
/**
173+
* omap_rproc_stop_timer() - stop a timer for a remoteproc
174+
* @timer: handle to a OMAP rproc timer
175+
*
176+
* This helper function is used to disable a timer associated with a
177+
* remoteproc, and needs to be called either during a device shutdown
178+
* or suspend operation. The separate helper function allows the driver
179+
* to just stop a timer without having to release the timer during a
180+
* suspend operation.
181+
*
182+
* Return: 0 on success, otherwise a failure as returned by DMTimer ops
183+
*/
184+
static inline int omap_rproc_stop_timer(struct omap_rproc_timer *timer)
185+
{
186+
return timer->timer_ops->stop(timer->odt);
187+
}
188+
189+
/**
190+
* omap_rproc_release_timer() - release a timer for a remoteproc
191+
* @timer: handle to a OMAP rproc timer
192+
*
193+
* This helper function is used primarily to release a timer associated
194+
* with a remoteproc. The dmtimer will be available for other clients to
195+
* use once released.
196+
*
197+
* Return: 0 on success, otherwise a failure as returned by DMTimer ops
198+
*/
199+
static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer)
200+
{
201+
return timer->timer_ops->free(timer->odt);
202+
}
203+
204+
/**
205+
* omap_rproc_enable_timers() - enable the timers for a remoteproc
206+
* @rproc: handle of a remote processor
207+
* @configure: boolean flag used to acquire and configure the timer handle
208+
*
209+
* This function is used primarily to enable the timers associated with
210+
* a remoteproc. The configure flag is provided to allow the driver to
211+
* to either acquire and start a timer (during device initialization) or
212+
* to just start a timer (during a resume operation).
213+
*
214+
* Return: 0 on success, otherwise an appropriate failure
215+
*/
216+
static int omap_rproc_enable_timers(struct rproc *rproc, bool configure)
217+
{
218+
int i;
219+
int ret = 0;
220+
struct platform_device *tpdev;
221+
struct dmtimer_platform_data *tpdata;
222+
const struct omap_dm_timer_ops *timer_ops;
223+
struct omap_rproc *oproc = rproc->priv;
224+
struct omap_rproc_timer *timers = oproc->timers;
225+
struct device *dev = rproc->dev.parent;
226+
struct device_node *np = NULL;
227+
228+
if (!oproc->num_timers)
229+
return 0;
230+
231+
if (!configure)
232+
goto start_timers;
233+
234+
for (i = 0; i < oproc->num_timers; i++) {
235+
np = of_parse_phandle(dev->of_node, "ti,timers", i);
236+
if (!np) {
237+
ret = -ENXIO;
238+
dev_err(dev, "device node lookup for timer at index %d failed: %d\n",
239+
i, ret);
240+
goto free_timers;
241+
}
242+
243+
tpdev = of_find_device_by_node(np);
244+
if (!tpdev) {
245+
ret = -ENODEV;
246+
dev_err(dev, "could not get timer platform device\n");
247+
goto put_node;
248+
}
249+
250+
tpdata = dev_get_platdata(&tpdev->dev);
251+
put_device(&tpdev->dev);
252+
if (!tpdata) {
253+
ret = -EINVAL;
254+
dev_err(dev, "dmtimer pdata structure NULL\n");
255+
goto put_node;
256+
}
257+
258+
timer_ops = tpdata->timer_ops;
259+
if (!timer_ops || !timer_ops->request_by_node ||
260+
!timer_ops->set_source || !timer_ops->set_load ||
261+
!timer_ops->free || !timer_ops->start ||
262+
!timer_ops->stop) {
263+
ret = -EINVAL;
264+
dev_err(dev, "device does not have required timer ops\n");
265+
goto put_node;
266+
}
267+
268+
timers[i].timer_ops = timer_ops;
269+
ret = omap_rproc_request_timer(dev, np, &timers[i]);
270+
if (ret) {
271+
dev_err(dev, "request for timer %p failed: %d\n", np,
272+
ret);
273+
goto put_node;
274+
}
275+
of_node_put(np);
276+
}
277+
278+
start_timers:
279+
for (i = 0; i < oproc->num_timers; i++) {
280+
ret = omap_rproc_start_timer(&timers[i]);
281+
if (ret) {
282+
dev_err(dev, "start timer %p failed failed: %d\n", np,
283+
ret);
284+
break;
285+
}
286+
}
287+
if (ret) {
288+
while (i >= 0) {
289+
omap_rproc_stop_timer(&timers[i]);
290+
i--;
291+
}
292+
goto put_node;
293+
}
294+
return 0;
295+
296+
put_node:
297+
if (configure)
298+
of_node_put(np);
299+
free_timers:
300+
while (i--) {
301+
omap_rproc_release_timer(&timers[i]);
302+
timers[i].odt = NULL;
303+
timers[i].timer_ops = NULL;
304+
}
305+
306+
return ret;
307+
}
308+
309+
/**
310+
* omap_rproc_disable_timers() - disable the timers for a remoteproc
311+
* @rproc: handle of a remote processor
312+
* @configure: boolean flag used to release the timer handle
313+
*
314+
* This function is used primarily to disable the timers associated with
315+
* a remoteproc. The configure flag is provided to allow the driver to
316+
* to either stop and release a timer (during device shutdown) or to just
317+
* stop a timer (during a suspend operation).
318+
*
319+
* Return: 0 on success or no timers
320+
*/
321+
static int omap_rproc_disable_timers(struct rproc *rproc, bool configure)
322+
{
323+
int i;
324+
struct omap_rproc *oproc = rproc->priv;
325+
struct omap_rproc_timer *timers = oproc->timers;
326+
327+
if (!oproc->num_timers)
328+
return 0;
329+
330+
for (i = 0; i < oproc->num_timers; i++) {
331+
omap_rproc_stop_timer(&timers[i]);
332+
if (configure) {
333+
omap_rproc_release_timer(&timers[i]);
334+
timers[i].odt = NULL;
335+
timers[i].timer_ops = NULL;
336+
}
337+
}
338+
339+
return 0;
340+
}
341+
100342
/**
101343
* omap_rproc_mbox_callback() - inbound mailbox message handler
102344
* @client: mailbox client pointer used for requesting the mailbox channel
@@ -232,14 +474,22 @@ static int omap_rproc_start(struct rproc *rproc)
232474
goto put_mbox;
233475
}
234476

477+
ret = omap_rproc_enable_timers(rproc, true);
478+
if (ret) {
479+
dev_err(dev, "omap_rproc_enable_timers failed: %d\n", ret);
480+
goto put_mbox;
481+
}
482+
235483
ret = reset_control_deassert(oproc->reset);
236484
if (ret) {
237485
dev_err(dev, "reset control deassert failed: %d\n", ret);
238-
goto put_mbox;
486+
goto disable_timers;
239487
}
240488

241489
return 0;
242490

491+
disable_timers:
492+
omap_rproc_disable_timers(rproc, true);
243493
put_mbox:
244494
mbox_free_channel(oproc->mbox);
245495
return ret;
@@ -255,6 +505,10 @@ static int omap_rproc_stop(struct rproc *rproc)
255505
if (ret)
256506
return ret;
257507

508+
ret = omap_rproc_disable_timers(rproc, true);
509+
if (ret)
510+
return ret;
511+
258512
mbox_free_channel(oproc->mbox);
259513

260514
return 0;
@@ -482,6 +736,37 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev,
482736
return 0;
483737
}
484738

739+
static int omap_rproc_of_get_timers(struct platform_device *pdev,
740+
struct rproc *rproc)
741+
{
742+
struct device_node *np = pdev->dev.of_node;
743+
struct omap_rproc *oproc = rproc->priv;
744+
struct device *dev = &pdev->dev;
745+
746+
/*
747+
* Timer nodes are directly used in client nodes as phandles, so
748+
* retrieve the count using appropriate size
749+
*/
750+
oproc->num_timers = of_count_phandle_with_args(np, "ti,timers", NULL);
751+
if (oproc->num_timers <= 0) {
752+
dev_dbg(dev, "device does not have timers, status = %d\n",
753+
oproc->num_timers);
754+
oproc->num_timers = 0;
755+
}
756+
757+
if (oproc->num_timers) {
758+
oproc->timers = devm_kcalloc(dev, oproc->num_timers,
759+
sizeof(*oproc->timers),
760+
GFP_KERNEL);
761+
if (!oproc->timers)
762+
return -ENOMEM;
763+
764+
dev_dbg(dev, "device has %d tick timers\n", oproc->num_timers);
765+
}
766+
767+
return 0;
768+
}
769+
485770
static int omap_rproc_probe(struct platform_device *pdev)
486771
{
487772
struct device_node *np = pdev->dev.of_node;
@@ -529,6 +814,10 @@ static int omap_rproc_probe(struct platform_device *pdev)
529814
if (ret)
530815
goto free_rproc;
531816

817+
ret = omap_rproc_of_get_timers(pdev, rproc);
818+
if (ret)
819+
goto free_rproc;
820+
532821
ret = of_reserved_mem_device_init(&pdev->dev);
533822
if (ret) {
534823
dev_warn(&pdev->dev, "device does not have specific CMA pool.\n");

0 commit comments

Comments
 (0)