alistair23-linux/drivers/staging/tidspbridge/core/wdt.c
Omar Ramirez Luna e16a922a27 staging: tidspbridge: use prepare/unprepare on dsp clocks
This solves runtime failures while trying to enable WDT3 related
functionality on firmware load, however it does affect other clocks
controlled by the driver. Seen on 3.8-rc1.

CCF provides clk_prepare and clk_unprepare for enable and disable
operations respectively, this needs to be called in the correct
order while handling clocks.

Code path to enable/disable dsp clocks can still be reached from an
atomic context, hence we can't use clk_prepare_enable and
clk_disable_unprepare yet.

Signed-off-by: Omar Ramirez Luna <omar.ramirez@copitl.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-07 15:03:56 -08:00

144 lines
3.2 KiB
C

/*
* wdt.c
*
* DSP-BIOS Bridge driver support functions for TI OMAP processors.
*
* IO dispatcher for a shared memory channel driver.
*
* Copyright (C) 2010 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <linux/types.h>
#include <dspbridge/dbdefs.h>
#include <dspbridge/dspdeh.h>
#include <dspbridge/dev.h>
#include <dspbridge/_chnl_sm.h>
#include <dspbridge/wdt.h>
#include <dspbridge/host_os.h>
#define OMAP34XX_WDT3_BASE (0x49000000 + 0x30000)
#define INT_34XX_WDT3_IRQ (36 + NR_IRQS)
static struct dsp_wdt_setting dsp_wdt;
void dsp_wdt_dpc(unsigned long data)
{
struct deh_mgr *deh_mgr;
dev_get_deh_mgr(dev_get_first(), &deh_mgr);
if (deh_mgr)
bridge_deh_notify(deh_mgr, DSP_WDTOVERFLOW, 0);
}
irqreturn_t dsp_wdt_isr(int irq, void *data)
{
u32 value;
/* ack wdt3 interrupt */
value = __raw_readl(dsp_wdt.reg_base + OMAP3_WDT3_ISR_OFFSET);
__raw_writel(value, dsp_wdt.reg_base + OMAP3_WDT3_ISR_OFFSET);
tasklet_schedule(&dsp_wdt.wdt3_tasklet);
return IRQ_HANDLED;
}
int dsp_wdt_init(void)
{
int ret = 0;
dsp_wdt.sm_wdt = NULL;
dsp_wdt.reg_base = ioremap(OMAP34XX_WDT3_BASE, SZ_4K);
if (!dsp_wdt.reg_base)
return -ENOMEM;
tasklet_init(&dsp_wdt.wdt3_tasklet, dsp_wdt_dpc, 0);
dsp_wdt.fclk = clk_get(NULL, "wdt3_fck");
if (!IS_ERR(dsp_wdt.fclk)) {
clk_prepare(dsp_wdt.fclk);
dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
if (IS_ERR(dsp_wdt.iclk)) {
clk_put(dsp_wdt.fclk);
dsp_wdt.fclk = NULL;
ret = -EFAULT;
} else {
clk_prepare(dsp_wdt.iclk);
}
} else
ret = -EFAULT;
if (!ret)
ret = request_irq(INT_34XX_WDT3_IRQ, dsp_wdt_isr, 0,
"dsp_wdt", &dsp_wdt);
/* Disable at this moment, it will be enabled when DSP starts */
if (!ret)
disable_irq(INT_34XX_WDT3_IRQ);
return ret;
}
void dsp_wdt_sm_set(void *data)
{
dsp_wdt.sm_wdt = data;
dsp_wdt.sm_wdt->wdt_overflow = 5; /* in seconds */
}
void dsp_wdt_exit(void)
{
free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt);
tasklet_kill(&dsp_wdt.wdt3_tasklet);
if (dsp_wdt.fclk) {
clk_unprepare(dsp_wdt.fclk);
clk_put(dsp_wdt.fclk);
}
if (dsp_wdt.iclk) {
clk_unprepare(dsp_wdt.iclk);
clk_put(dsp_wdt.iclk);
}
dsp_wdt.fclk = NULL;
dsp_wdt.iclk = NULL;
dsp_wdt.sm_wdt = NULL;
if (dsp_wdt.reg_base)
iounmap(dsp_wdt.reg_base);
dsp_wdt.reg_base = NULL;
}
void dsp_wdt_enable(bool enable)
{
u32 tmp;
static bool wdt_enable;
if (wdt_enable == enable || !dsp_wdt.fclk || !dsp_wdt.iclk)
return;
wdt_enable = enable;
if (enable) {
clk_enable(dsp_wdt.fclk);
clk_enable(dsp_wdt.iclk);
dsp_wdt.sm_wdt->wdt_setclocks = 1;
tmp = __raw_readl(dsp_wdt.reg_base + OMAP3_WDT3_ISR_OFFSET);
__raw_writel(tmp, dsp_wdt.reg_base + OMAP3_WDT3_ISR_OFFSET);
enable_irq(INT_34XX_WDT3_IRQ);
} else {
disable_irq(INT_34XX_WDT3_IRQ);
dsp_wdt.sm_wdt->wdt_setclocks = 0;
clk_disable(dsp_wdt.iclk);
clk_disable(dsp_wdt.fclk);
}
}