![Linus Torvalds](/assets/img/avatar_default.png)
This is a large branch that contains a handful of different cleanups: - Fixing up the I/O space remapping on PCI on ARM. This is a series from Rob Herring that restructures how all pci devices allocate I/O space, and it's part of the work to allow multiplatform kernels. - A number of cleanup series for OMAP, moving and removing some headers, sparse irq rework and in general preparation for multiplatform. - Final removal of all non-DT boards for Tegra, it is now device-tree-only! - Removal of a stale platform, nxp4008. It's an old mobile chipset that is no longer in use, and was very likely never really used with a mainline kernel. We have not been able to find anyone interested in keeping it around in the kernel. - Removal of the legacy dmaengine driver on tegra + A handful of other things that I haven't described above. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQaO1fAAoJEIwa5zzehBx3IPgP/jxoO1flVGNVf0reqqyDro/w prZmp8cNVH9uv8xG9n9vawObrMQ8M6jCJ449fEWuAZ58EXrpIPd0kkm/MOmxp8K1 LNs+q2aXxWpD488+b3RK55g3fksqZutTbn3y6HNuCoLG9l8yT/95KX4IIzfEP2Ch 1TCNHdkTbf37nTBOmKN0x1kahGpWDrOkf9ysHQq+DXAGF4uwNwtR194dqz3HbDND hZqRq7qCLn9OwGRGNicPFoB6UcxwZ/+/+u5sX7nqPGoiPofg977mhWk1DFO15EM3 S+A6g0dZ+XLsL+fFtOl4snSmrG5Et6qTOP0/ItQJgTG+5YdCS09ohCWJwRCBHbgj M5arOkyGFdVAlvX7cUux374sMe0AcqUsEmt79mYuBpIE+pBJaRUoCgDcs9FDZeUB U6WcE4AkxMtW7DtmVW+mF4ls9/K6cRXgWMuHCUmt1o3m3Ly9ITT7j+ntXnD9nuYk ndoVLR6Vxk2BzlkD0JEtg7FRAS9Wgo2DBix05qM1Qkut2iIZRhFQlqJQpNbeNdii /3Lg/hqpAVTZKGCd+paegHez61meyFz2PB2IiE0JKANhKHRCWTWRGgKIXkGyCiXk wJ2iRCOlMEpmpJgCBzfI32ER/hnW4s64iDjgksEwz6pEt7xCbhwgmwrpf0H0KsSF rLroHOMqyISd/Ha52Vin =ck1u -----END PGP SIGNATURE----- Merge tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM soc general cleanups from Olof Johansson: "This is a large branch that contains a handful of different cleanups: - Fixing up the I/O space remapping on PCI on ARM. This is a series from Rob Herring that restructures how all pci devices allocate I/O space, and it's part of the work to allow multiplatform kernels. - A number of cleanup series for OMAP, moving and removing some headers, sparse irq rework and in general preparation for multiplatform. - Final removal of all non-DT boards for Tegra, it is now device-tree-only! - Removal of a stale platform, nxp4008. It's an old mobile chipset that is no longer in use, and was very likely never really used with a mainline kernel. We have not been able to find anyone interested in keeping it around in the kernel. - Removal of the legacy dmaengine driver on tegra + A handful of other things that I haven't described above." Fix up some conflicts with the staging tree (and because nxp4008 was removed) * tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (184 commits) ARM: OMAP2+: serial: Change MAX_HSUART_PORTS to 6 ARM: OMAP4: twl-common: Support for additional devices on i2c1 bus ARM: mmp: using for_each_set_bit to simplify the code ARM: tegra: harmony: fix ldo7 regulator-name ARM: OMAP2+: Make omap4-keypad.h local ARM: OMAP2+: Make l4_3xxx.h local ARM: OMAP2+: Make l4_2xxx.h local ARM: OMAP2+: Make l3_3xxx.h local ARM: OMAP2+: Make l3_2xxx.h local ARM: OMAP1: Move irda.h from plat to mach ARM: OMAP2+: Make hdq1w.h local ARM: OMAP2+: Make gpmc-smsc911x.h local ARM: OMAP2+: Make gpmc-smc91x.h local ARM: OMAP1: Move flash.h from plat to mach ARM: OMAP2+: Make debug-devices.h local ARM: OMAP1: Move board-voiceblue.h from plat to mach ARM: OMAP1: Move board-sx1.h from plat to mach ARM: OMAP2+: Make omap-wakeupgen.h local ARM: OMAP2+: Make omap-secure.h local ARM: OMAP2+: Make ctrl_module_wkup_44xx.h local ...
381 lines
8.4 KiB
C
381 lines
8.4 KiB
C
/*
|
|
* clk.c
|
|
*
|
|
* DSP-BIOS Bridge driver support functions for TI OMAP processors.
|
|
*
|
|
* Clock and Timer services.
|
|
*
|
|
* Copyright (C) 2005-2006 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.
|
|
*/
|
|
|
|
#define L4_34XX_BASE 0x48000000
|
|
|
|
#include <linux/types.h>
|
|
|
|
/* ----------------------------------- Host OS */
|
|
#include <dspbridge/host_os.h>
|
|
#include <plat/dmtimer.h>
|
|
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
|
|
|
/* ----------------------------------- DSP/BIOS Bridge */
|
|
#include <dspbridge/dbdefs.h>
|
|
#include <dspbridge/drv.h>
|
|
#include <dspbridge/dev.h>
|
|
#include "_tiomap.h"
|
|
|
|
/* ----------------------------------- This */
|
|
#include <dspbridge/clk.h>
|
|
|
|
/* ----------------------------------- Defines, Data Structures, Typedefs */
|
|
|
|
#define OMAP_SSI_OFFSET 0x58000
|
|
#define OMAP_SSI_SIZE 0x1000
|
|
#define OMAP_SSI_SYSCONFIG_OFFSET 0x10
|
|
|
|
#define SSI_AUTOIDLE (1 << 0)
|
|
#define SSI_SIDLE_SMARTIDLE (2 << 3)
|
|
#define SSI_MIDLE_NOIDLE (1 << 12)
|
|
|
|
/* Clk types requested by the dsp */
|
|
#define IVA2_CLK 0
|
|
#define GPT_CLK 1
|
|
#define WDT_CLK 2
|
|
#define MCBSP_CLK 3
|
|
#define SSI_CLK 4
|
|
|
|
/* Bridge GPT id (1 - 4), DM Timer id (5 - 8) */
|
|
#define DMT_ID(id) ((id) + 4)
|
|
#define DM_TIMER_CLOCKS 4
|
|
|
|
/* Bridge MCBSP id (6 - 10), OMAP Mcbsp id (0 - 4) */
|
|
#define MCBSP_ID(id) ((id) - 6)
|
|
|
|
static struct omap_dm_timer *timer[4];
|
|
|
|
struct clk *iva2_clk;
|
|
|
|
struct dsp_ssi {
|
|
struct clk *sst_fck;
|
|
struct clk *ssr_fck;
|
|
struct clk *ick;
|
|
};
|
|
|
|
static struct dsp_ssi ssi;
|
|
|
|
static u32 dsp_clocks;
|
|
|
|
static inline u32 is_dsp_clk_active(u32 clk, u8 id)
|
|
{
|
|
return clk & (1 << id);
|
|
}
|
|
|
|
static inline void set_dsp_clk_active(u32 *clk, u8 id)
|
|
{
|
|
*clk |= (1 << id);
|
|
}
|
|
|
|
static inline void set_dsp_clk_inactive(u32 *clk, u8 id)
|
|
{
|
|
*clk &= ~(1 << id);
|
|
}
|
|
|
|
static s8 get_clk_type(u8 id)
|
|
{
|
|
s8 type;
|
|
|
|
if (id == DSP_CLK_IVA2)
|
|
type = IVA2_CLK;
|
|
else if (id <= DSP_CLK_GPT8)
|
|
type = GPT_CLK;
|
|
else if (id == DSP_CLK_WDT3)
|
|
type = WDT_CLK;
|
|
else if (id <= DSP_CLK_MCBSP5)
|
|
type = MCBSP_CLK;
|
|
else if (id == DSP_CLK_SSI)
|
|
type = SSI_CLK;
|
|
else
|
|
type = -1;
|
|
|
|
return type;
|
|
}
|
|
|
|
/*
|
|
* ======== dsp_clk_exit ========
|
|
* Purpose:
|
|
* Cleanup CLK module.
|
|
*/
|
|
void dsp_clk_exit(void)
|
|
{
|
|
int i;
|
|
|
|
dsp_clock_disable_all(dsp_clocks);
|
|
|
|
for (i = 0; i < DM_TIMER_CLOCKS; i++)
|
|
omap_dm_timer_free(timer[i]);
|
|
|
|
clk_put(iva2_clk);
|
|
clk_put(ssi.sst_fck);
|
|
clk_put(ssi.ssr_fck);
|
|
clk_put(ssi.ick);
|
|
}
|
|
|
|
/*
|
|
* ======== dsp_clk_init ========
|
|
* Purpose:
|
|
* Initialize CLK module.
|
|
*/
|
|
void dsp_clk_init(void)
|
|
{
|
|
static struct platform_device dspbridge_device;
|
|
int i, id;
|
|
|
|
dspbridge_device.dev.bus = &platform_bus_type;
|
|
|
|
for (i = 0, id = 5; i < DM_TIMER_CLOCKS; i++, id++)
|
|
timer[i] = omap_dm_timer_request_specific(id);
|
|
|
|
iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
|
|
if (IS_ERR(iva2_clk))
|
|
dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk);
|
|
|
|
ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
|
|
ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
|
|
ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
|
|
|
|
if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick))
|
|
dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
|
|
ssi.sst_fck, ssi.ssr_fck, ssi.ick);
|
|
}
|
|
|
|
/**
|
|
* dsp_gpt_wait_overflow - set gpt overflow and wait for fixed timeout
|
|
* @clk_id: GP Timer clock id.
|
|
* @load: Overflow value.
|
|
*
|
|
* Sets an overflow interrupt for the desired GPT waiting for a timeout
|
|
* of 5 msecs for the interrupt to occur.
|
|
*/
|
|
void dsp_gpt_wait_overflow(short int clk_id, unsigned int load)
|
|
{
|
|
struct omap_dm_timer *gpt = timer[clk_id - 1];
|
|
unsigned long timeout;
|
|
|
|
if (!gpt)
|
|
return;
|
|
|
|
/* Enable overflow interrupt */
|
|
omap_dm_timer_set_int_enable(gpt, OMAP_TIMER_INT_OVERFLOW);
|
|
|
|
/*
|
|
* Set counter value to overflow counter after
|
|
* one tick and start timer.
|
|
*/
|
|
omap_dm_timer_set_load_start(gpt, 0, load);
|
|
|
|
/* Wait 80us for timer to overflow */
|
|
udelay(80);
|
|
|
|
timeout = msecs_to_jiffies(5);
|
|
/* Check interrupt status and wait for interrupt */
|
|
while (!(omap_dm_timer_read_status(gpt) & OMAP_TIMER_INT_OVERFLOW)) {
|
|
if (time_is_after_jiffies(timeout)) {
|
|
pr_err("%s: GPTimer interrupt failed\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== dsp_clk_enable ========
|
|
* Purpose:
|
|
* Enable Clock .
|
|
*
|
|
*/
|
|
int dsp_clk_enable(enum dsp_clk_id clk_id)
|
|
{
|
|
int status = 0;
|
|
|
|
if (is_dsp_clk_active(dsp_clocks, clk_id)) {
|
|
dev_err(bridge, "WARN: clock id %d already enabled\n", clk_id);
|
|
goto out;
|
|
}
|
|
|
|
switch (get_clk_type(clk_id)) {
|
|
case IVA2_CLK:
|
|
clk_enable(iva2_clk);
|
|
break;
|
|
case GPT_CLK:
|
|
status = omap_dm_timer_start(timer[clk_id - 1]);
|
|
break;
|
|
#ifdef CONFIG_OMAP_MCBSP
|
|
case MCBSP_CLK:
|
|
omap_mcbsp_request(MCBSP_ID(clk_id));
|
|
omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PAD_SRC);
|
|
break;
|
|
#endif
|
|
case WDT_CLK:
|
|
dev_err(bridge, "ERROR: DSP requested to enable WDT3 clk\n");
|
|
break;
|
|
case SSI_CLK:
|
|
clk_enable(ssi.sst_fck);
|
|
clk_enable(ssi.ssr_fck);
|
|
clk_enable(ssi.ick);
|
|
|
|
/*
|
|
* The SSI module need to configured not to have the Forced
|
|
* idle for master interface. If it is set to forced idle,
|
|
* the SSI module is transitioning to standby thereby causing
|
|
* the client in the DSP hang waiting for the SSI module to
|
|
* be active after enabling the clocks
|
|
*/
|
|
ssi_clk_prepare(true);
|
|
break;
|
|
default:
|
|
dev_err(bridge, "Invalid clock id for enable\n");
|
|
status = -EPERM;
|
|
}
|
|
|
|
if (!status)
|
|
set_dsp_clk_active(&dsp_clocks, clk_id);
|
|
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* dsp_clock_enable_all - Enable clocks used by the DSP
|
|
* @dev_context Driver's device context strucure
|
|
*
|
|
* This function enables all the peripheral clocks that were requested by DSP.
|
|
*/
|
|
u32 dsp_clock_enable_all(u32 dsp_per_clocks)
|
|
{
|
|
u32 clk_id;
|
|
u32 status = -EPERM;
|
|
|
|
for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
|
|
if (is_dsp_clk_active(dsp_per_clocks, clk_id))
|
|
status = dsp_clk_enable(clk_id);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== dsp_clk_disable ========
|
|
* Purpose:
|
|
* Disable the clock.
|
|
*
|
|
*/
|
|
int dsp_clk_disable(enum dsp_clk_id clk_id)
|
|
{
|
|
int status = 0;
|
|
|
|
if (!is_dsp_clk_active(dsp_clocks, clk_id)) {
|
|
dev_err(bridge, "ERR: clock id %d already disabled\n", clk_id);
|
|
goto out;
|
|
}
|
|
|
|
switch (get_clk_type(clk_id)) {
|
|
case IVA2_CLK:
|
|
clk_disable(iva2_clk);
|
|
break;
|
|
case GPT_CLK:
|
|
status = omap_dm_timer_stop(timer[clk_id - 1]);
|
|
break;
|
|
#ifdef CONFIG_OMAP_MCBSP
|
|
case MCBSP_CLK:
|
|
omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PRCM_SRC);
|
|
omap_mcbsp_free(MCBSP_ID(clk_id));
|
|
break;
|
|
#endif
|
|
case WDT_CLK:
|
|
dev_err(bridge, "ERROR: DSP requested to disable WDT3 clk\n");
|
|
break;
|
|
case SSI_CLK:
|
|
ssi_clk_prepare(false);
|
|
ssi_clk_prepare(false);
|
|
clk_disable(ssi.sst_fck);
|
|
clk_disable(ssi.ssr_fck);
|
|
clk_disable(ssi.ick);
|
|
break;
|
|
default:
|
|
dev_err(bridge, "Invalid clock id for disable\n");
|
|
status = -EPERM;
|
|
}
|
|
|
|
if (!status)
|
|
set_dsp_clk_inactive(&dsp_clocks, clk_id);
|
|
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* dsp_clock_disable_all - Disable all active clocks
|
|
* @dev_context Driver's device context structure
|
|
*
|
|
* This function disables all the peripheral clocks that were enabled by DSP.
|
|
* It is meant to be called only when DSP is entering hibernation or when DSP
|
|
* is in error state.
|
|
*/
|
|
u32 dsp_clock_disable_all(u32 dsp_per_clocks)
|
|
{
|
|
u32 clk_id;
|
|
u32 status = -EPERM;
|
|
|
|
for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
|
|
if (is_dsp_clk_active(dsp_per_clocks, clk_id))
|
|
status = dsp_clk_disable(clk_id);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
u32 dsp_clk_get_iva2_rate(void)
|
|
{
|
|
u32 clk_speed_khz;
|
|
|
|
clk_speed_khz = clk_get_rate(iva2_clk);
|
|
clk_speed_khz /= 1000;
|
|
dev_dbg(bridge, "%s: clk speed Khz = %d\n", __func__, clk_speed_khz);
|
|
|
|
return clk_speed_khz;
|
|
}
|
|
|
|
void ssi_clk_prepare(bool FLAG)
|
|
{
|
|
void __iomem *ssi_base;
|
|
unsigned int value;
|
|
|
|
ssi_base = ioremap(L4_34XX_BASE + OMAP_SSI_OFFSET, OMAP_SSI_SIZE);
|
|
if (!ssi_base) {
|
|
pr_err("%s: error, SSI not configured\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (FLAG) {
|
|
/* Set Autoidle, SIDLEMode to smart idle, and MIDLEmode to
|
|
* no idle
|
|
*/
|
|
value = SSI_AUTOIDLE | SSI_SIDLE_SMARTIDLE | SSI_MIDLE_NOIDLE;
|
|
} else {
|
|
/* Set Autoidle, SIDLEMode to forced idle, and MIDLEmode to
|
|
* forced idle
|
|
*/
|
|
value = SSI_AUTOIDLE;
|
|
}
|
|
|
|
__raw_writel(value, ssi_base + OMAP_SSI_SYSCONFIG_OFFSET);
|
|
iounmap(ssi_base);
|
|
}
|
|
|