* [PATCH v2 05/21] ARM: tegra: clock: Disable clocks left on by bootloader
From: Colin Cross @ 2011-02-21 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTim8i5vvizBViqFCyq9i+7Uo2E1BrNRf_gtnMb-v@mail.gmail.com>
On Sun, Feb 20, 2011 at 4:40 PM, Olof Johansson <olof@lixom.net> wrote:
> Hi,
>
> On Sat, Feb 19, 2011 at 2:25 PM, Colin Cross <ccross@android.com> wrote:
>> Adds CONFIG_TEGRA_DISABLE_BOOTLOADER_CLOCKS that iterates
>> through all clocks, disabling any for which the refcount
>> is 0 but the clock init detected the bootloader left the
>> clock on.
>
>
> I would argue that any kernel functionality that relies on clocks
> being left on my bootloader is buggy, and/or should at least have
> those clocks enabled in the static clock tables for the boards in
> question, since it creates an undocumented dependency on firmware.
>
> I would prefer if it disabled the clocks by default (after warning),
> but that there was a runtime way to override while debugging (i.e. add
> a bootarg 'keep_fwclocks' or similar to keep the firmware-enabled
> clocks running and just warn about them).
That was the plan, but since many of the drivers are missing I didn't
want to enable it by default yet. However, it turns out the problems
I was seeing at boot from this patch were actually due to a bug in the
clock code causing the cpu clock to be disabled for clocks that have
no disable. I will drop this patch, and post a separate series after
these that fixes the bug and turns off clocks by default, with a
runtime argument like you suggested.
^ permalink raw reply
* bug in arm_kprobe_decode_insn
From: Nicolas Pitre @ 2011-02-21 3:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110220182437.GA24789@joi.lan>
On Sun, 20 Feb 2011, Marcin Slusarz wrote:
> Hi
>
> In arch/arm/kernel/kprobes-decode.c there's a function arm_kprobe_decode_insn
> which does:
>
> } else if ((insn & 0x0e000000) == 0x0c400000) {
> ...
>
> This is always false, so code below is dead.
Good catch, thanks.
Nicolas
^ permalink raw reply
* [PATCH] i.MX23/28 framebuffer driver
From: Dave Airlie @ 2011-02-21 3:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1102171545470.21278@casper.infradead.org>
On Fri, Feb 18, 2011 at 2:14 AM, James Simmons <jsimmons@infradead.org> wrote:
>
>> I'm still in the learning-as-I-go phase here, so definitely not ready
>> to propose a solution, but it does seem to me like there is room for
>> some sort of kms-helper library here to handle more of the boilerplate
>> xf86-video-* stuff.. ?I guess I'll have a better picture once I have a
>> chance to add support for the various multi-monitor configurations.
>> But certainly would be interested if anyone already has some ideas.
>
> I have been thinking about this as well. One of the short coming for DRM
> on embedded platforms is the lack of a small well defined library that one
> could use. Right now libkms only handles buffer allocation. The mode
> setting is currently tied to the libdrm library. It would be nice to
> seperate the two out more. Move the mode setting code to libkms and then
> have libdrm be a wrapper around this. This way libdrm can both support
> the legacy drm drivers and the new kms drivers at the same time. It also
> makes a clear seperation. Jakob if you are willing to take this work I
> will gladly seen you patches.
the problem with that is libkms relies on libdrm, so you'd have a
circular dependency.
With the addition of the dumb ioctl to the kernel we can certainly
avoid some of that dependency,
but you guys are missing one important point about libkms, its meant
to be a fallback.
Why?
well because most GPUs in reality have non-generic memory layouts,
they prefer having tiled buffers for different things and different
types of tiling. There is simply no nice way to abstract that out,
since it depends on the userspace libraries that use this. Starting
off with an untiled allocation for the frontbuffer can pretty much
leave you messed up when you plug anything like acceleration in
afterwards.
I have been looking at this from the USB driver pov as well, and we
can probably resurrect the old xf86-video-modesetting driver along
with some code in the X server maybe, mesa also has a libkms fallback
X driver that works quite well if just a bit messy to build (since its
in mesa).
Dave.
^ permalink raw reply
* [PATCH 1/2] Add a common struct clk
From: Jeremy Kerr @ 2011-02-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298256658.861611.43913489619.0.gpush@pororo>
We currently have ~21 definitions of struct clk in the ARM architecture,
each defined on a per-platform basis. This makes it difficult to define
platform- (or architecture-) independent clock sources without making
assumptions about struct clk, and impossible to compile two
platforms with different struct clks into a single image.
This change is an effort to unify struct clk where possible, by defining
a common struct clk, containing a set of clock operations. Different
clock implementations can set their own operations, and have a standard
interface for generic code. The callback interface is exposed to the
kernel proper, while the clock implementations only need to be seen by
the platform internals.
This allows us to share clock code among platforms, and makes it
possible to dynamically create clock devices in platform-independent
code.
Platforms can enable the generic struct clock through
CONFIG_USE_COMMON_STRUCT_CLK. In this case, the clock infrastructure
consists of a common struct clk:
struct clk {
const struct clk_ops *ops;
unsigned int enable_count;
unsigned int prepare_count;
spinlock_t enable_lock;
struct mutex prepare_lock;
};
And a set of clock operations (defined per type of clock):
struct clk_ops {
int (*enable)(struct clk *);
void (*disable)(struct clk *);
unsigned long (*get_rate)(struct clk *);
[...]
};
To define a hardware-specific clock, machine code can "subclass" the
struct clock into a new struct (adding any device-specific data), and
provide a set of operations:
struct clk_foo {
struct clk clk;
void __iomem *some_register;
};
struct clk_ops clk_foo_ops = {
.get_rate = clk_foo_get_rate,
};
The common clock definitions are based on a development patch from Ben
Herrenschmidt <benh@kernel.crashing.org>.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
---
drivers/clk/Kconfig | 3
drivers/clk/Makefile | 1
drivers/clk/clk.c | 132 ++++++++++++++++++++++++++++++++++
drivers/clk/clkdev.c | 7 +
include/linux/clk.h | 164 ++++++++++++++++++++++++++++++++++++++++---
5 files changed, 298 insertions(+), 9 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4168c88..6e3ae54 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -2,3 +2,6 @@
config CLKDEV_LOOKUP
bool
select HAVE_CLK
+
+config USE_COMMON_STRUCT_CLK
+ bool
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 07613fa..a1a06d3 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
+obj-$(CONFIG_USE_COMMON_STRUCT_CLK) += clk.o
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 0000000..0bc9c6f
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ *
+ * This program 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.
+ *
+ * Standard functionality for the common clock API.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+
+int clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+
+ mutex_lock(&clk->prepare_lock);
+ if (clk->prepare_count == 0 && clk->ops->prepare)
+ ret = clk->ops->prepare(clk);
+
+ if (!ret)
+ clk->prepare_count++;
+ mutex_unlock(&clk->prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_prepare);
+
+void clk_unprepare(struct clk *clk)
+{
+ mutex_lock(&clk->prepare_lock);
+
+ WARN_ON(clk->prepare_count == 0);
+
+ if (--clk->prepare_count == 0 && clk->ops->unprepare) {
+ WARN_ON(clk->enable_count != 0);
+ clk->ops->unprepare(clk);
+ }
+
+ mutex_unlock(&clk->prepare_lock);
+}
+EXPORT_SYMBOL_GPL(clk_unprepare);
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ WARN_ON(clk->prepare_count == 0);
+
+ spin_lock_irqsave(&clk->enable_lock, flags);
+ if (clk->enable_count == 0 && clk->ops->enable)
+ ret = clk->ops->enable(clk);
+
+ if (!ret)
+ clk->enable_count++;
+ spin_unlock_irqrestore(&clk->enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk->enable_lock, flags);
+
+ WARN_ON(clk->enable_count == 0);
+
+ if (!--clk->enable_count == 0 && clk->ops->disable)
+ clk->ops->disable(clk);
+
+ spin_unlock_irqrestore(&clk->enable_lock, flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk->ops->get_rate)
+ return clk->ops->get_rate(clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk->ops->round_rate)
+ return clk->ops->round_rate(clk, rate);
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ might_sleep();
+
+ if (clk->ops->set_rate)
+ return clk->ops->set_rate(clk, rate);
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ if (clk->ops->set_parent)
+ return clk->ops->set_parent(clk, parent);
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (clk->ops->get_parent)
+ return clk->ops->get_parent(clk);
+ return ERR_PTR(-ENOSYS);
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+int __clk_get(struct clk *clk)
+{
+ if (clk->ops->get)
+ return clk->ops->get(clk);
+ return 1;
+}
+
+void __clk_put(struct clk *clk)
+{
+ if (clk->ops->put)
+ clk->ops->put(clk);
+}
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 0fc0a79..a7999d2 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -23,6 +23,13 @@
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
+/* For USE_COMMON_STRUCT_CLK, these are provided in clk.c, but not exported
+ * through other headers; we don't want them used anywhere but here. */
+#ifdef CONFIG_USE_COMMON_STRUCT_CLK
+extern int __clk_get(struct clk *clk);
+extern void __clk_put(struct clk *clk);
+#endif
+
/*
* Find the correct struct clk for the device and connection ID.
* We do slightly fuzzy matching here:
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1d37f42..604be74 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -3,6 +3,7 @@
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
+ * Copyright (c) 2010-2011 Jeremy Kerr <jeremy.kerr@canonical.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,18 +12,168 @@
#ifndef __LINUX_CLK_H
#define __LINUX_CLK_H
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
struct device;
-/*
- * The base API.
+#ifdef CONFIG_USE_COMMON_STRUCT_CLK
+
+/* If we're using the common struct clk, we define the base clk object here */
+
+/**
+ * struct clk - hardware independent clock structure
+ * @ops: implementation-specific ops for this clock
+ * @enable_count: count of clk_enable() calls active on this clock
+ * @enable_lock: lock for atomic enable
+ * @prepare_count: count of clk_prepare() calls active on this clock
+ * @prepare_lock: lock for sleepable prepare
+ *
+ * The base clock object, used by drivers for hardware-independent manipulation
+ * of clock lines. This will be 'subclassed' by device-specific implementations,
+ * which add device-specific data to struct clk. For example:
+ *
+ * struct clk_foo {
+ * struct clk;
+ * [device specific fields]
+ * };
+ *
+ * The clock driver code will manage the device-specific data, and pass
+ * clk_foo.clk to the common clock code. The clock driver will be called
+ * through the @ops callbacks.
+ *
+ * The @enable_lock and @prepare_lock members are used to serialise accesses
+ * to the ops->enable and ops->prepare functions (and the corresponding
+ * ops->disable and ops->unprepare functions).
*/
+struct clk {
+ const struct clk_ops *ops;
+ unsigned int enable_count;
+ unsigned int prepare_count;
+ spinlock_t enable_lock;
+ struct mutex prepare_lock;
+};
+/* static initialiser for clocks. */
+#define INIT_CLK(name, o) { \
+ .ops = &o, \
+ .enable_lock = __SPIN_LOCK_UNLOCKED(name.enable_lock), \
+ .prepare_lock = __MUTEX_INITIALIZER(name.prepare_lock), \
+}
+
+/**
+ * struct clk_ops - Callback operations for clocks; these are to be provided
+ * by the clock implementation, and will be called by drivers through the clk_*
+ * API.
+ *
+ * @prepare: Prepare the clock for enabling. This must not return until
+ * the clock is fully prepared, and it's safe to call clk_enable.
+ * This callback is intended to allow clock implementations to
+ * do any initialisation that may sleep. Called with
+ * clk->prepare_lock held.
+ *
+ * @unprepare: Release the clock from its prepared state. This will typically
+ * undo any work done in the @prepare callback. Called with
+ * clk->prepare_lock held.
+ *
+ * @enable: Enable the clock atomically. This must not return until the
+ * clock is generating a valid clock signal, usable by consumer
+ * devices. Called with clk->enable_lock held. This function
+ * must not sleep.
+ *
+ * @disable: Disable the clock atomically. Called with clk->enable_lock held.
+ * This function must not sleep.
+ *
+ * @get: Called by the core clock code when a device driver acquires a
+ * clock via clk_get(). Optional.
+ *
+ * @put: Called by the core clock code when a devices driver releases a
+ * clock via clk_put(). Optional.
+ *
+ * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
+ * implementations to split any work between atomic (enable) and sleepable
+ * (prepare) contexts. If a clock requires sleeping code to be turned on, this
+ * should be done in clk_prepare. Switching that will not sleep should be done
+ * in clk_enable.
+ *
+ * Typically, drivers will call clk_prepare when a clock may be needed later
+ * (eg. when a device is opened), and clk_enable when the clock is actually
+ * required (eg. from an interrupt). Note that clk_prepare *must* have been
+ * called before clk_enable.
+ *
+ * For other callbacks, see the corresponding clk_* functions. Parameters and
+ * return values are passed directly from/to these API functions, or
+ * -ENOSYS (or zero, in the case of clk_get_rate) is returned if the callback
+ * is NULL, see drivers/clk/clk.c for implementation details. All are optional.
+ */
+struct clk_ops {
+ int (*prepare)(struct clk *);
+ void (*unprepare)(struct clk *);
+ int (*enable)(struct clk *);
+ void (*disable)(struct clk *);
+ int (*get)(struct clk *);
+ void (*put)(struct clk *);
+ unsigned long (*get_rate)(struct clk *);
+ long (*round_rate)(struct clk *, unsigned long);
+ int (*set_rate)(struct clk *, unsigned long);
+ int (*set_parent)(struct clk *, struct clk *);
+ struct clk * (*get_parent)(struct clk *);
+};
+
+/**
+ * clk_prepare - prepare clock for atomic enabling.
+ *
+ * @clk: The clock to prepare
+ *
+ * Do any possibly sleeping initialisation on @clk, allowing the clock to be
+ * later enabled atomically (via clk_enable). This function may sleep.
+ */
+int clk_prepare(struct clk *clk);
+
+/**
+ * clk_unprepare - release clock from prepared state
+ *
+ * @clk: The clock to release
+ *
+ * Do any (possibly sleeping) cleanup on clk. This function may sleep.
+ */
+void clk_unprepare(struct clk *clk);
+
+/**
+ * clk_common_init - initialise a clock for driver usage
+ *
+ * @clk: The clock to initialise
+ *
+ * Used for runtime intialization of clocks; you don't need to call this
+ * if your clock has been (statically) initialized with INIT_CLK.
+ */
+static inline void clk_common_init(struct clk *clk)
+{
+ clk->enable_count = clk->prepare_count = 0;
+ spin_lock_init(&clk->enable_lock);
+ mutex_init(&clk->prepare_lock);
+}
+
+#else /* !CONFIG_USE_COMMON_STRUCT_CLK */
/*
- * struct clk - an machine class defined object / cookie.
+ * Global clock object, actual structure is declared per-machine
*/
struct clk;
+static inline void clk_common_init(struct clk *clk) { }
+
+/*
+ * For !CONFIG_USE_COMMON_STRUCT_CLK, we don't enforce any atomicity
+ * requirements for clk_enable/clk_disable, so the prepare and unprepare
+ * functions are no-ops
+ */
+int clk_prepare(struct clk *clk) { return 0; }
+void clk_unprepare(struct clk *clk) { }
+
+#endif /* !CONFIG_USE_COMMON_STRUCT_CLK */
+
/**
* clk_get - lookup and obtain a reference to a clock producer.
* @dev: device for clock "consumer"
@@ -67,6 +218,7 @@ void clk_disable(struct clk *clk);
/**
* clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
* This is only valid once the clock source has been enabled.
+ * Returns zero if the clock rate is unknown.
* @clk: clock source
*/
unsigned long clk_get_rate(struct clk *clk);
@@ -83,12 +235,6 @@ unsigned long clk_get_rate(struct clk *clk);
*/
void clk_put(struct clk *clk);
-
-/*
- * The remaining APIs are optional for machine class support.
- */
-
-
/**
* clk_round_rate - adjust a rate to the exact rate a clock can provide
* @clk: clock source
^ permalink raw reply related
* [PATCH 0/2] Common struct clk implementation, v13
From: Jeremy Kerr @ 2011-02-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102011711.31258.jeremy.kerr@canonical.com>
Hi all,
These patches are an attempt to allow platforms to share clock code. At
present, the definitions of 'struct clk' are local to platform code,
which makes allocating and initialising cross-platform clock sources
difficult, and makes it impossible to compile a single image containing
support for two ARM platforms with different struct clks.
The three patches are for the architecture-independent kernel code,
introducing the common clk infrastructure. The changelog for the first
patch includes details about the new clock definitions.
Many thanks to the following for their input:
* Benjamin Herrenschmidt <benh@kernel.crashing.org>
* Ben Dooks <ben-linux@fluff.org>
* Baruch Siach <baruch@tkos.co.il>
* Russell King <linux@arm.linux.org.uk>
* Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
* Lorenzo Pieralisi <Lorenzo.Pieralisi@arm.com>
* Vincent Guittot <vincent.guittot@linaro.org>
* Sascha Hauer <s.hauer@pengutronix.de>
* Ryan Mallon <ryan@bluewatersys.com>
* Colin Cross <ccross@google.com>
* Jassi Brar <jassisinghbrar@gmail.com>
* Saravana Kannan <skannan@codeaurora.org>
Cheers,
Jeremy
--
v13:
* Don't expose __clk_get and clk_put - prototypes in clkdev.c instead
* Add might_sleep to clk_set_rate
* Comment clarifications & fixups
* Remove zero initialisers
* Fold warnings into main clk.c change
v12:
* Always refcount, even when enable/prepare ops are NULL
* Unify prepare & enable count checking
* Update comments for prepare/unprepare
* Use spin_{lock,unlock}_irqsave
* Change clk_put to __clk_put, and use the shared clk_put
v11:
* add prepare/unprepare for non-atomic switching, document atomicity
* move to drivers/clk/
v10:
* comment fixups, from Uwe's review
* added DEFINE_CLK_FIXED
v9:
* comment improvements
* kerneldoc fixups
* add WARN_ON to clk_disable
v8:
* add atomic clocks, and locking wrappers
* expand comments on clk and clk_ops
v7:
* change CLK_INIT to initialise clk->mutex statically
v6:
* fixed up references to 'clk_operations' in the changelog
v5:
* uninline main API, and share definitions with !USE_COMMON_STRUCT_CLK
* add __clk_get
* delay mutex init
* kerneldoc for struct clk
v4:
* use mutex for enable/disable locking
* DEFINE_CLK -> INIT_CLK, and pass the clk name for mutex init
* struct clk_operations -> struct clk_ops
v3:
* do clock usage refcounting in common code
* provide sample port
v2:
* no longer ARM-specific
* use clk_operations
---
Jeremy Kerr (2):
Add a common struct clk
clk: Generic support for fixed-rate clocks
^ permalink raw reply
* [PATCH 2/2] clk: Generic support for fixed-rate clocks
From: Jeremy Kerr @ 2011-02-21 2:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298256658.861611.43913489619.0.gpush@pororo>
Since most platforms will need a fixed-rate clock, add one. This will
also serve as a basic example of an implementation of struct clk.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
---
drivers/clk/clk.c | 14 ++++++++++++++
include/linux/clk.h | 16 ++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0bc9c6f..0da0bb9 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -130,3 +130,17 @@ void __clk_put(struct clk *clk)
if (clk->ops->put)
clk->ops->put(clk);
}
+
+/* clk_fixed support */
+
+#define to_clk_fixed(clk) (container_of(clk, struct clk_fixed, clk))
+
+static unsigned long clk_fixed_get_rate(struct clk *clk)
+{
+ return to_clk_fixed(clk)->rate;
+}
+
+struct clk_ops clk_fixed_ops = {
+ .get_rate = clk_fixed_get_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_ops);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 604be74..7c0808c 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -155,6 +155,22 @@ static inline void clk_common_init(struct clk *clk)
mutex_init(&clk->prepare_lock);
}
+/* Simple fixed-rate clock */
+struct clk_fixed {
+ struct clk clk;
+ unsigned long rate;
+};
+
+extern struct clk_ops clk_fixed_ops;
+
+#define INIT_CLK_FIXED(name, r) { \
+ .clk = INIT_CLK(name.clk, clk_fixed_ops), \
+ .rate = (r) \
+}
+
+#define DEFINE_CLK_FIXED(name, r) \
+ struct clk_fixed name = INIT_CLK_FIXED(name, r)
+
#else /* !CONFIG_USE_COMMON_STRUCT_CLK */
/*
^ permalink raw reply related
* [PATCH 3/3] OMAP2+: voltage: reorganize, split code from data
From: Paul Walmsley @ 2011-02-21 2:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110221020231.7598.14024.stgit@twilight.localdomain>
This is a first pass at reorganizing mach-omap2/voltage.c:
- Separate almost all of the data from the code of mach-omap2/voltage.c.
The code remains in mach-omap2/voltage.c. The data goes into one
of several places, depending on what type of data it is:
- Silicon process/validation data: mach-omap2/opp*_data.c
- VC (Voltage Controller) data: mach-omap2/vc*_data.c
- VP (Voltage Processor) data: mach-omap2/vp*_data.c
- Voltage domain data: mach-omap2/voltagedomains*_data.c
The ultimate goal is for all this data to be autogenerated, the same
way we autogenerate the rest of our data.
- Separate VC and VP common data from VDD-specific VC and VP data.
- Separate common voltage.c code from SoC-specific code; reuse common code.
- Reorganize structures to avoid unnecessary memory loss due to unpacked
fields.
There is much left to be done. VC code and VP code should be separated out
into vc*.c and vp*.c files. Many fields in the existing structures are
superfluous, and should be removed. Some code in voltage.c seems to be
duplicated; that code should be moved into functions of its own. Proper
voltage domain code should be created, as was done with the powerdomain
and clockdomains, and powerdomains should reference voltagedomains.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
arch/arm/mach-omap2/Makefile | 20
arch/arm/mach-omap2/omap_opp_data.h | 12
arch/arm/mach-omap2/opp3xxx_data.c | 44 +
arch/arm/mach-omap2/opp4xxx_data.c | 30 +
arch/arm/mach-omap2/vc.h | 83 ++
arch/arm/mach-omap2/vc3xxx_data.c | 63 ++
arch/arm/mach-omap2/vc44xx_data.c | 72 ++
arch/arm/mach-omap2/voltage.c | 1016 +++++++------------------
arch/arm/mach-omap2/voltage.h | 66 ++
arch/arm/mach-omap2/voltagedomains3xxx_data.c | 104 +++
arch/arm/mach-omap2/voltagedomains44xx_data.c | 108 +++
arch/arm/mach-omap2/vp.h | 143 ++++
arch/arm/mach-omap2/vp3xxx_data.c | 82 ++
arch/arm/mach-omap2/vp44xx_data.c | 97 ++
14 files changed, 1192 insertions(+), 748 deletions(-)
create mode 100644 arch/arm/mach-omap2/vc.h
create mode 100644 arch/arm/mach-omap2/vc3xxx_data.c
create mode 100644 arch/arm/mach-omap2/vc44xx_data.c
create mode 100644 arch/arm/mach-omap2/voltagedomains3xxx_data.c
create mode 100644 arch/arm/mach-omap2/voltagedomains44xx_data.c
create mode 100644 arch/arm/mach-omap2/vp.h
create mode 100644 arch/arm/mach-omap2/vp3xxx_data.c
create mode 100644 arch/arm/mach-omap2/vp44xx_data.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 1c0c2b0..d5c1dba 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -59,10 +59,10 @@ endif
# Power Management
ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
-obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o voltage.o
-obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o voltage.o \
+obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o
+obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
cpuidle34xx.o pm_bus.o
-obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o voltage.o pm_bus.o
+obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
@@ -78,13 +78,23 @@ endif
# PRCM
obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o
-obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o
+obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
+ vc3xxx_data.o vp3xxx_data.o
# XXX The presence of cm2xxx_3xxx.o on the line below is temporary and
# will be removed once the OMAP4 part of the codebase is converted to
# use OMAP4-specific PRCM functions.
obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \
cm44xx.o prcm_mpu44xx.o \
- prminst44xx.o
+ prminst44xx.o vc44xx_data.o \
+ vp44xx_data.o
+
+# OMAP voltage domains
+voltagedomain-common := voltage.o
+obj-$(CONFIG_ARCH_OMAP2) += $(voltagedomain-common)
+obj-$(CONFIG_ARCH_OMAP3) += $(voltagedomain-common) \
+ voltagedomains3xxx_data.o
+obj-$(CONFIG_ARCH_OMAP4) += $(voltagedomain-common) \
+ voltagedomains44xx_data.o
# OMAP powerdomain framework
powerdomain-common += powerdomain.o powerdomain-common.o
diff --git a/arch/arm/mach-omap2/omap_opp_data.h b/arch/arm/mach-omap2/omap_opp_data.h
index 46ac27d..d8158bb 100644
--- a/arch/arm/mach-omap2/omap_opp_data.h
+++ b/arch/arm/mach-omap2/omap_opp_data.h
@@ -21,6 +21,8 @@
#include <plat/omap_hwmod.h>
+#include "voltage.h"
+
/*
* *BIG FAT WARNING*:
* USE the following ONLY in opp data initialization common to an SoC.
@@ -69,4 +71,14 @@ struct omap_opp_def {
extern int __init omap_init_opp_table(struct omap_opp_def *opp_def,
u32 opp_def_size);
+
+extern struct omap_volt_data omap34xx_vddmpu_volt_data[];
+extern struct omap_volt_data omap34xx_vddcore_volt_data[];
+extern struct omap_volt_data omap36xx_vddmpu_volt_data[];
+extern struct omap_volt_data omap36xx_vddcore_volt_data[];
+
+extern struct omap_volt_data omap44xx_vdd_mpu_volt_data[];
+extern struct omap_volt_data omap44xx_vdd_iva_volt_data[];
+extern struct omap_volt_data omap44xx_vdd_core_volt_data[];
+
#endif /* __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H */
diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c
index 0486fce..3c7f0c0 100644
--- a/arch/arm/mach-omap2/opp3xxx_data.c
+++ b/arch/arm/mach-omap2/opp3xxx_data.c
@@ -4,8 +4,9 @@
* Copyright (C) 2009-2010 Texas Instruments Incorporated - http://www.ti.com/
* Nishanth Menon
* Kevin Hilman
- * Copyright (C) 2010 Nokia Corporation.
+ * Copyright (C) 2010-2011 Nokia Corporation.
* Eduardo Valentin
+ * Paul Walmsley
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,8 +21,49 @@
#include <plat/cpu.h>
+#include "control.h"
#include "omap_opp_data.h"
+/* 34xx */
+
+/* VDD1 */
+struct omap_volt_data omap34xx_vddmpu_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD1, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD1, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD1, 0xf9, 0x18),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP4_UV, OMAP343X_CONTROL_FUSE_OPP4_VDD1, 0xf9, 0x18),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP5_UV, OMAP343X_CONTROL_FUSE_OPP5_VDD1, 0xf9, 0x18),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+/* VDD2 */
+struct omap_volt_data omap34xx_vddcore_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD2, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD2, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD2, 0xf9, 0x18),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+/* 36xx */
+
+/* VDD1 */
+struct omap_volt_data omap36xx_vddmpu_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD1, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD1, 0xf9, 0x16),
+ VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP120_UV, OMAP3630_CONTROL_FUSE_OPP120_VDD1, 0xfa, 0x23),
+ VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP1G_UV, OMAP3630_CONTROL_FUSE_OPP1G_VDD1, 0xfa, 0x27),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+/* VDD2 */
+struct omap_volt_data omap36xx_vddcore_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD2, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD2, 0xf9, 0x16),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+/* OPP data */
+
static struct omap_opp_def __initdata omap34xx_opp_def_list[] = {
/* MPU OPP1 */
OPP_INITIALIZER("mpu", true, 125000000, 975000),
diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c
index a11fa56..addaf70 100644
--- a/arch/arm/mach-omap2/opp4xxx_data.c
+++ b/arch/arm/mach-omap2/opp4xxx_data.c
@@ -5,8 +5,9 @@
* Nishanth Menon
* Kevin Hilman
* Thara Gopinath
- * Copyright (C) 2010 Nokia Corporation.
+ * Copyright (C) 2010-2011 Nokia Corporation.
* Eduardo Valentin
+ * Paul Walmsley
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -21,8 +22,35 @@
#include <plat/cpu.h>
+#include "control.h"
#include "omap_opp_data.h"
+/*
+ * Structures containing OMAP4430 voltage supported and various
+ * voltage dependent data for each VDD.
+ */
+struct omap_volt_data omap44xx_vdd_mpu_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP50_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP50, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP100_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP100, 0xf9, 0x16),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPTURBO, 0xfa, 0x23),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPNITRO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPNITRO, 0xfa, 0x27),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+struct omap_volt_data omap44xx_vdd_iva_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP50_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP50, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP100_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP100, 0xf9, 0x16),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_IVA_OPPTURBO, 0xfa, 0x23),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+struct omap_volt_data omap44xx_vdd_core_volt_data[] = {
+ VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP50_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP50, 0xf4, 0x0c),
+ VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP100_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP100, 0xf9, 0x16),
+ VOLT_DATA_DEFINE(0, 0, 0, 0),
+};
+
+
static struct omap_opp_def __initdata omap44xx_opp_def_list[] = {
/* MPU OPP1 - OPP50 */
OPP_INITIALIZER("mpu", true, 300000000, 1100000),
diff --git a/arch/arm/mach-omap2/vc.h b/arch/arm/mach-omap2/vc.h
new file mode 100644
index 0000000..e776777
--- /dev/null
+++ b/arch/arm/mach-omap2/vc.h
@@ -0,0 +1,83 @@
+/*
+ * OMAP3/4 Voltage Controller (VC) structure and macro definitions
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_VC_H
+#define __ARCH_ARM_MACH_OMAP2_VC_H
+
+#include <linux/kernel.h>
+
+/**
+ * struct omap_vc_common_data - per-VC register/bitfield data
+ * @cmd_on_mask: ON bitmask in PRM_VC_CMD_VAL* register
+ * @valid: VALID bitmask in PRM_VC_BYPASS_VAL register
+ * @smps_sa_reg: Offset of PRM_VC_SMPS_SA reg from PRM start
+ * @smps_volra_reg: Offset of PRM_VC_SMPS_VOL_RA reg from PRM start
+ * @bypass_val_reg: Offset of PRM_VC_BYPASS_VAL reg from PRM start
+ * @data_shift: DATA field shift in PRM_VC_BYPASS_VAL register
+ * @slaveaddr_shift: SLAVEADDR field shift in PRM_VC_BYPASS_VAL register
+ * @regaddr_shift: REGADDR field shift in PRM_VC_BYPASS_VAL register
+ * @cmd_on_shift: ON field shift in PRM_VC_CMD_VAL_* register
+ * @cmd_onlp_shift: ONLP field shift in PRM_VC_CMD_VAL_* register
+ * @cmd_ret_shift: RET field shift in PRM_VC_CMD_VAL_* register
+ * @cmd_off_shift: OFF field shift in PRM_VC_CMD_VAL_* register
+ *
+ * XXX One of cmd_on_mask and cmd_on_shift are not needed
+ * XXX VALID should probably be a shift, not a mask
+ */
+struct omap_vc_common_data {
+ u32 cmd_on_mask;
+ u32 valid;
+ u8 smps_sa_reg;
+ u8 smps_volra_reg;
+ u8 bypass_val_reg;
+ u8 data_shift;
+ u8 slaveaddr_shift;
+ u8 regaddr_shift;
+ u8 cmd_on_shift;
+ u8 cmd_onlp_shift;
+ u8 cmd_ret_shift;
+ u8 cmd_off_shift;
+};
+
+/**
+ * struct omap_vc_instance_data - VC per-instance data
+ * @vc_common: pointer to VC common data for this platform
+ * @smps_sa_mask: SA* bitmask in the PRM_VC_SMPS_SA register
+ * @smps_volra_mask: VOLRA* bitmask in the PRM_VC_VOL_RA register
+ * @smps_sa_shift: SA* field shift in the PRM_VC_SMPS_SA register
+ * @smps_volra_shift: VOLRA* field shift in the PRM_VC_VOL_RA register
+ *
+ * XXX It is not necessary to have both a *_mask and a *_shift -
+ * remove one
+ */
+struct omap_vc_instance_data {
+ const struct omap_vc_common_data *vc_common;
+ u32 smps_sa_mask;
+ u32 smps_volra_mask;
+ u8 cmdval_reg;
+ u8 smps_sa_shift;
+ u8 smps_volra_shift;
+};
+
+extern struct omap_vc_instance_data omap3_vc1_data;
+extern struct omap_vc_instance_data omap3_vc2_data;
+
+extern struct omap_vc_instance_data omap4_vc_mpu_data;
+extern struct omap_vc_instance_data omap4_vc_iva_data;
+extern struct omap_vc_instance_data omap4_vc_core_data;
+
+#endif
+
diff --git a/arch/arm/mach-omap2/vc3xxx_data.c b/arch/arm/mach-omap2/vc3xxx_data.c
new file mode 100644
index 0000000..f37dc4b
--- /dev/null
+++ b/arch/arm/mach-omap2/vc3xxx_data.c
@@ -0,0 +1,63 @@
+/*
+ * OMAP3 Voltage Controller (VC) data
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <plat/common.h>
+
+#include "prm-regbits-34xx.h"
+#include "voltage.h"
+
+#include "vc.h"
+
+/*
+ * VC data common to 34xx/36xx chips
+ * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
+ */
+static struct omap_vc_common_data omap3_vc_common = {
+ .smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET,
+ .smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
+ .bypass_val_reg = OMAP3_PRM_VC_BYPASS_VAL_OFFSET,
+ .data_shift = OMAP3430_DATA_SHIFT,
+ .slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT,
+ .regaddr_shift = OMAP3430_REGADDR_SHIFT,
+ .valid = OMAP3430_VALID_MASK,
+ .cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT,
+ .cmd_on_mask = OMAP3430_VC_CMD_ON_MASK,
+ .cmd_onlp_shift = OMAP3430_VC_CMD_ONLP_SHIFT,
+ .cmd_ret_shift = OMAP3430_VC_CMD_RET_SHIFT,
+ .cmd_off_shift = OMAP3430_VC_CMD_OFF_SHIFT,
+};
+
+struct omap_vc_instance_data omap3_vc1_data = {
+ .vc_common = &omap3_vc_common,
+ .cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET,
+ .smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT,
+ .smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA0_MASK,
+ .smps_volra_shift = OMAP3430_VOLRA0_SHIFT,
+ .smps_volra_mask = OMAP3430_VOLRA0_MASK,
+};
+
+struct omap_vc_instance_data omap3_vc2_data = {
+ .vc_common = &omap3_vc_common,
+ .cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET,
+ .smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT,
+ .smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA1_MASK,
+ .smps_volra_shift = OMAP3430_VOLRA1_SHIFT,
+ .smps_volra_mask = OMAP3430_VOLRA1_MASK,
+};
diff --git a/arch/arm/mach-omap2/vc44xx_data.c b/arch/arm/mach-omap2/vc44xx_data.c
new file mode 100644
index 0000000..548cb06
--- /dev/null
+++ b/arch/arm/mach-omap2/vc44xx_data.c
@@ -0,0 +1,72 @@
+/*
+ * OMAP4 Voltage Controller (VC) data
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <plat/common.h>
+
+#include "prm44xx.h"
+#include "prm-regbits-44xx.h"
+#include "voltage.h"
+
+#include "vc.h"
+
+/*
+ * VC data common to 44xx chips
+ * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
+ */
+static const struct omap_vc_common_data omap4_vc_data = {
+ .smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
+ .smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
+ .bypass_val_reg = OMAP4_PRM_VC_VAL_BYPASS_OFFSET,
+ .data_shift = OMAP4430_DATA_SHIFT,
+ .slaveaddr_shift = OMAP4430_SLAVEADDR_SHIFT,
+ .regaddr_shift = OMAP4430_REGADDR_SHIFT,
+ .valid = OMAP4430_VALID_MASK,
+ .cmd_on_shift = OMAP4430_ON_SHIFT,
+ .cmd_on_mask = OMAP4430_ON_MASK,
+ .cmd_onlp_shift = OMAP4430_ONLP_SHIFT,
+ .cmd_ret_shift = OMAP4430_RET_SHIFT,
+ .cmd_off_shift = OMAP4430_OFF_SHIFT,
+};
+
+/* VC instance data for each controllable voltage line */
+struct omap_vc_instance_data omap4_vc_mpu_data = {
+ .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET,
+ .smps_sa_shift = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_SHIFT,
+ .smps_sa_mask = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK,
+ .smps_volra_shift = OMAP4430_VOLRA_VDD_MPU_L_SHIFT,
+ .smps_volra_mask = OMAP4430_VOLRA_VDD_MPU_L_MASK,
+};
+
+struct omap_vc_instance_data omap4_vc_iva_data = {
+ .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET,
+ .smps_sa_shift = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_SHIFT,
+ .smps_sa_mask = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK,
+ .smps_volra_shift = OMAP4430_VOLRA_VDD_IVA_L_SHIFT,
+ .smps_volra_mask = OMAP4430_VOLRA_VDD_IVA_L_MASK,
+};
+
+struct omap_vc_instance_data omap4_vc_core_data = {
+ .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET,
+ .smps_sa_shift = OMAP4430_SA_VDD_CORE_L_0_6_SHIFT,
+ .smps_sa_mask = OMAP4430_SA_VDD_CORE_L_0_6_MASK,
+ .smps_volra_shift = OMAP4430_VOLRA_VDD_CORE_L_SHIFT,
+ .smps_volra_mask = OMAP4430_VOLRA_VDD_CORE_L_MASK,
+};
+
diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c
index 3c9bcdc..97f8e73 100644
--- a/arch/arm/mach-omap2/voltage.c
+++ b/arch/arm/mach-omap2/voltage.c
@@ -7,8 +7,9 @@
* Rajendra Nayak <rnayak@ti.com>
* Lesly A M <x0080970@ti.com>
*
- * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2008, 2011 Nokia Corporation
* Kalle Jokiniemi
+ * Paul Walmsley
*
* Copyright (C) 2010 Texas Instruments, Inc.
* Thara Gopinath <thara@ti.com>
@@ -36,284 +37,28 @@
#include "voltage.h"
-#define VP_IDLE_TIMEOUT 200
-#define VP_TRANXDONE_TIMEOUT 300
+#include "vc.h"
+#include "vp.h"
+
#define VOLTAGE_DIR_SIZE 16
-/* Voltage processor register offsets */
-struct vp_reg_offs {
- u8 vpconfig;
- u8 vstepmin;
- u8 vstepmax;
- u8 vlimitto;
- u8 vstatus;
- u8 voltage;
-};
-
-/* Voltage Processor bit field values, shifts and masks */
-struct vp_reg_val {
- /* PRM module */
- u16 prm_mod;
- /* VPx_VPCONFIG */
- u32 vpconfig_erroroffset;
- u16 vpconfig_errorgain;
- u32 vpconfig_errorgain_mask;
- u8 vpconfig_errorgain_shift;
- u32 vpconfig_initvoltage_mask;
- u8 vpconfig_initvoltage_shift;
- u32 vpconfig_timeouten;
- u32 vpconfig_initvdd;
- u32 vpconfig_forceupdate;
- u32 vpconfig_vpenable;
- /* VPx_VSTEPMIN */
- u8 vstepmin_stepmin;
- u16 vstepmin_smpswaittimemin;
- u8 vstepmin_stepmin_shift;
- u8 vstepmin_smpswaittimemin_shift;
- /* VPx_VSTEPMAX */
- u8 vstepmax_stepmax;
- u16 vstepmax_smpswaittimemax;
- u8 vstepmax_stepmax_shift;
- u8 vstepmax_smpswaittimemax_shift;
- /* VPx_VLIMITTO */
- u8 vlimitto_vddmin;
- u8 vlimitto_vddmax;
- u16 vlimitto_timeout;
- u8 vlimitto_vddmin_shift;
- u8 vlimitto_vddmax_shift;
- u8 vlimitto_timeout_shift;
- /* PRM_IRQSTATUS*/
- u32 tranxdone_status;
-};
-
-/* Voltage controller registers and offsets */
-struct vc_reg_info {
- /* PRM module */
- u16 prm_mod;
- /* VC register offsets */
- u8 smps_sa_reg;
- u8 smps_volra_reg;
- u8 bypass_val_reg;
- u8 cmdval_reg;
- u8 voltsetup_reg;
- /*VC_SMPS_SA*/
- u8 smps_sa_shift;
- u32 smps_sa_mask;
- /* VC_SMPS_VOL_RA */
- u8 smps_volra_shift;
- u32 smps_volra_mask;
- /* VC_BYPASS_VAL */
- u8 data_shift;
- u8 slaveaddr_shift;
- u8 regaddr_shift;
- u32 valid;
- /* VC_CMD_VAL */
- u8 cmd_on_shift;
- u8 cmd_onlp_shift;
- u8 cmd_ret_shift;
- u8 cmd_off_shift;
- u32 cmd_on_mask;
- /* PRM_VOLTSETUP */
- u8 voltsetup_shift;
- u32 voltsetup_mask;
-};
-/**
- * omap_vdd_info - Per Voltage Domain info
- *
- * @volt_data : voltage table having the distinct voltages supported
- * by the domain and other associated per voltage data.
- * @pmic_info : pmic specific parameters which should be populted by
- * the pmic drivers.
- * @vp_offs : structure containing the offsets for various
- * vp registers
- * @vp_reg : the register values, shifts, masks for various
- * vp registers
- * @vc_reg : structure containing various various vc registers,
- * shifts, masks etc.
- * @voltdm : pointer to the voltage domain structure
- * @debug_dir : debug directory for this voltage domain.
- * @curr_volt : current voltage for this vdd.
- * @ocp_mod : The prm module for accessing the prm irqstatus reg.
- * @prm_irqst_reg : prm irqstatus register.
- * @vp_enabled : flag to keep track of whether vp is enabled or not
- * @volt_scale : API to scale the voltage of the vdd.
- */
-struct omap_vdd_info {
- struct omap_volt_data *volt_data;
- struct omap_volt_pmic_info *pmic_info;
- struct vp_reg_offs vp_offs;
- struct vp_reg_val vp_reg;
- struct vc_reg_info vc_reg;
- struct voltagedomain voltdm;
- struct dentry *debug_dir;
- u32 curr_volt;
- u16 ocp_mod;
- u8 prm_irqst_reg;
- bool vp_enabled;
- u32 (*read_reg) (u16 mod, u8 offset);
- void (*write_reg) (u32 val, u16 mod, u8 offset);
- int (*volt_scale) (struct omap_vdd_info *vdd,
- unsigned long target_volt);
-};
-
-static struct omap_vdd_info *vdd_info;
+static struct omap_vdd_info **vdd_info;
+
/*
* Number of scalable voltage domains.
*/
static int nr_scalable_vdd;
-/* OMAP3 VDD sturctures */
-static struct omap_vdd_info omap3_vdd_info[] = {
- {
- .vp_offs = {
- .vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET,
- .vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET,
- .vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET,
- .vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET,
- .vstatus = OMAP3_PRM_VP1_STATUS_OFFSET,
- .voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET,
- },
- .voltdm = {
- .name = "mpu",
- },
- },
- {
- .vp_offs = {
- .vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET,
- .vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET,
- .vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET,
- .vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET,
- .vstatus = OMAP3_PRM_VP2_STATUS_OFFSET,
- .voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET,
- },
- .voltdm = {
- .name = "core",
- },
- },
-};
-
-#define OMAP3_NR_SCALABLE_VDD ARRAY_SIZE(omap3_vdd_info)
-
-/* OMAP4 VDD sturctures */
-static struct omap_vdd_info omap4_vdd_info[] = {
- {
- .vp_offs = {
- .vpconfig = OMAP4_PRM_VP_MPU_CONFIG_OFFSET,
- .vstepmin = OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET,
- .vstepmax = OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET,
- .vlimitto = OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET,
- .vstatus = OMAP4_PRM_VP_MPU_STATUS_OFFSET,
- .voltage = OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET,
- },
- .voltdm = {
- .name = "mpu",
- },
- },
- {
- .vp_offs = {
- .vpconfig = OMAP4_PRM_VP_IVA_CONFIG_OFFSET,
- .vstepmin = OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET,
- .vstepmax = OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET,
- .vlimitto = OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET,
- .vstatus = OMAP4_PRM_VP_IVA_STATUS_OFFSET,
- .voltage = OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET,
- },
- .voltdm = {
- .name = "iva",
- },
- },
- {
- .vp_offs = {
- .vpconfig = OMAP4_PRM_VP_CORE_CONFIG_OFFSET,
- .vstepmin = OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET,
- .vstepmax = OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET,
- .vlimitto = OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET,
- .vstatus = OMAP4_PRM_VP_CORE_STATUS_OFFSET,
- .voltage = OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET,
- },
- .voltdm = {
- .name = "core",
- },
- },
-};
-
-#define OMAP4_NR_SCALABLE_VDD ARRAY_SIZE(omap4_vdd_info)
-
-/*
- * Structures containing OMAP3430/OMAP3630 voltage supported and various
- * voltage dependent data for each VDD.
- */
-#define VOLT_DATA_DEFINE(_v_nom, _efuse_offs, _errminlimit, _errgain) \
-{ \
- .volt_nominal = _v_nom, \
- .sr_efuse_offs = _efuse_offs, \
- .sr_errminlimit = _errminlimit, \
- .vp_errgain = _errgain \
-}
-
-/* VDD1 */
-static struct omap_volt_data omap34xx_vddmpu_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD1, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD1, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD1, 0xf9, 0x18),
- VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP4_UV, OMAP343X_CONTROL_FUSE_OPP4_VDD1, 0xf9, 0x18),
- VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP5_UV, OMAP343X_CONTROL_FUSE_OPP5_VDD1, 0xf9, 0x18),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-static struct omap_volt_data omap36xx_vddmpu_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD1, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD1, 0xf9, 0x16),
- VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP120_UV, OMAP3630_CONTROL_FUSE_OPP120_VDD1, 0xfa, 0x23),
- VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP1G_UV, OMAP3630_CONTROL_FUSE_OPP1G_VDD1, 0xfa, 0x27),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-/* VDD2 */
-static struct omap_volt_data omap34xx_vddcore_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD2, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD2, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD2, 0xf9, 0x18),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-static struct omap_volt_data omap36xx_vddcore_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD2, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD2, 0xf9, 0x16),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-/*
- * Structures containing OMAP4430 voltage supported and various
- * voltage dependent data for each VDD.
- */
-static struct omap_volt_data omap44xx_vdd_mpu_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP50_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP50, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP100_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP100, 0xf9, 0x16),
- VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPTURBO, 0xfa, 0x23),
- VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPNITRO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPNITRO, 0xfa, 0x27),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-static struct omap_volt_data omap44xx_vdd_iva_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP50_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP50, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP100_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP100, 0xf9, 0x16),
- VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_IVA_OPPTURBO, 0xfa, 0x23),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
-
-static struct omap_volt_data omap44xx_vdd_core_volt_data[] = {
- VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP50_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP50, 0xf4, 0x0c),
- VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP100_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP100, 0xf9, 0x16),
- VOLT_DATA_DEFINE(0, 0, 0, 0),
-};
+/* XXX document */
+static s16 prm_mod_offs;
+static s16 prm_irqst_ocp_mod_offs;
static struct dentry *voltage_dir;
/* Init function pointers */
-static void (*vc_init) (struct omap_vdd_info *vdd);
-static int (*vdd_data_configure) (struct omap_vdd_info *vdd);
+static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
+ unsigned long target_volt);
static u32 omap3_voltage_read_reg(u16 mod, u8 offset)
{
@@ -336,6 +81,60 @@ static void omap4_voltage_write_reg(u32 val, u16 mod, u8 offset)
omap4_prminst_write_inst_reg(val, OMAP4430_PRM_PARTITION, mod, offset);
}
+static int __init _config_common_vdd_data(struct omap_vdd_info *vdd)
+{
+ char *sys_ck_name;
+ struct clk *sys_ck;
+ u32 sys_clk_speed, timeout_val, waittime;
+
+ /*
+ * XXX Clockfw should handle this, or this should be in a
+ * struct record
+ */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ sys_ck_name = "sys_ck";
+ else if (cpu_is_omap44xx())
+ sys_ck_name = "sys_clkin_ck";
+
+ /*
+ * Sys clk rate is require to calculate vp timeout value and
+ * smpswaittimemin and smpswaittimemax.
+ */
+ sys_ck = clk_get(NULL, sys_ck_name);
+ if (IS_ERR(sys_ck)) {
+ pr_warning("%s: Could not get the sys clk to calculate"
+ "various vdd_%s params\n", __func__, vdd->voltdm.name);
+ return -EINVAL;
+ }
+ sys_clk_speed = clk_get_rate(sys_ck);
+ clk_put(sys_ck);
+ /* Divide to avoid overflow */
+ sys_clk_speed /= 1000;
+
+ /* Generic voltage parameters */
+ vdd->curr_volt = 1200000;
+ vdd->volt_scale = vp_forceupdate_scale_voltage;
+ vdd->vp_enabled = false;
+
+ vdd->vp_rt_data.vpconfig_erroroffset =
+ (vdd->pmic_info->vp_erroroffset <<
+ vdd->vp_data->vp_common->vpconfig_erroroffset_shift);
+
+ timeout_val = (sys_clk_speed * vdd->pmic_info->vp_timeout_us) / 1000;
+ vdd->vp_rt_data.vlimitto_timeout = timeout_val;
+ vdd->vp_rt_data.vlimitto_vddmin = vdd->pmic_info->vp_vddmin;
+ vdd->vp_rt_data.vlimitto_vddmax = vdd->pmic_info->vp_vddmax;
+
+ waittime = ((vdd->pmic_info->step_size / vdd->pmic_info->slew_rate) *
+ sys_clk_speed) / 1000;
+ vdd->vp_rt_data.vstepmin_smpswaittimemin = waittime;
+ vdd->vp_rt_data.vstepmax_smpswaittimemax = waittime;
+ vdd->vp_rt_data.vstepmin_stepmin = vdd->pmic_info->vp_vstepmin;
+ vdd->vp_rt_data.vstepmax_stepmax = vdd->pmic_info->vp_vstepmax;
+
+ return 0;
+}
+
/* Voltage debugfs support */
static int vp_volt_debug_get(void *data, u64 *val)
{
@@ -347,7 +146,7 @@ static int vp_volt_debug_get(void *data, u64 *val)
return -EINVAL;
}
- vsel = vdd->read_reg(vdd->vp_reg.prm_mod, vdd->vp_offs.voltage);
+ vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
pr_notice("curr_vsel = %x\n", vsel);
if (!vdd->pmic_info->vsel_to_uv) {
@@ -380,7 +179,6 @@ DEFINE_SIMPLE_ATTRIBUTE(nom_volt_debug_fops, nom_volt_debug_get, NULL,
static void vp_latch_vsel(struct omap_vdd_info *vdd)
{
u32 vpconfig;
- u16 mod;
unsigned long uvdc;
char vsel;
@@ -397,30 +195,27 @@ static void vp_latch_vsel(struct omap_vdd_info *vdd)
return;
}
- mod = vdd->vp_reg.prm_mod;
-
vsel = vdd->pmic_info->uv_to_vsel(uvdc);
- vpconfig = vdd->read_reg(mod, vdd->vp_offs.vpconfig);
- vpconfig &= ~(vdd->vp_reg.vpconfig_initvoltage_mask |
- vdd->vp_reg.vpconfig_initvdd);
- vpconfig |= vsel << vdd->vp_reg.vpconfig_initvoltage_shift;
+ vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
+ vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvoltage_mask |
+ vdd->vp_data->vp_common->vpconfig_initvdd);
+ vpconfig |= vsel << vdd->vp_data->vp_common->vpconfig_initvoltage_shift;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Trigger initVDD value copy to voltage processor */
- vdd->write_reg((vpconfig | vdd->vp_reg.vpconfig_initvdd), mod,
- vdd->vp_offs.vpconfig);
+ vdd->write_reg((vpconfig | vdd->vp_data->vp_common->vpconfig_initvdd),
+ prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear initVDD copy trigger bit */
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
}
/* Generic voltage init functions */
static void __init vp_init(struct omap_vdd_info *vdd)
{
u32 vp_val;
- u16 mod;
if (!vdd->read_reg || !vdd->write_reg) {
pr_err("%s: No read/write API for accessing vdd_%s regs\n",
@@ -428,33 +223,31 @@ static void __init vp_init(struct omap_vdd_info *vdd)
return;
}
- mod = vdd->vp_reg.prm_mod;
-
- vp_val = vdd->vp_reg.vpconfig_erroroffset |
- (vdd->vp_reg.vpconfig_errorgain <<
- vdd->vp_reg.vpconfig_errorgain_shift) |
- vdd->vp_reg.vpconfig_timeouten;
- vdd->write_reg(vp_val, mod, vdd->vp_offs.vpconfig);
-
- vp_val = ((vdd->vp_reg.vstepmin_smpswaittimemin <<
- vdd->vp_reg.vstepmin_smpswaittimemin_shift) |
- (vdd->vp_reg.vstepmin_stepmin <<
- vdd->vp_reg.vstepmin_stepmin_shift));
- vdd->write_reg(vp_val, mod, vdd->vp_offs.vstepmin);
-
- vp_val = ((vdd->vp_reg.vstepmax_smpswaittimemax <<
- vdd->vp_reg.vstepmax_smpswaittimemax_shift) |
- (vdd->vp_reg.vstepmax_stepmax <<
- vdd->vp_reg.vstepmax_stepmax_shift));
- vdd->write_reg(vp_val, mod, vdd->vp_offs.vstepmax);
-
- vp_val = ((vdd->vp_reg.vlimitto_vddmax <<
- vdd->vp_reg.vlimitto_vddmax_shift) |
- (vdd->vp_reg.vlimitto_vddmin <<
- vdd->vp_reg.vlimitto_vddmin_shift) |
- (vdd->vp_reg.vlimitto_timeout <<
- vdd->vp_reg.vlimitto_timeout_shift));
- vdd->write_reg(vp_val, mod, vdd->vp_offs.vlimitto);
+ vp_val = vdd->vp_rt_data.vpconfig_erroroffset |
+ (vdd->vp_rt_data.vpconfig_errorgain <<
+ vdd->vp_data->vp_common->vpconfig_errorgain_shift) |
+ vdd->vp_data->vp_common->vpconfig_timeouten;
+ vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vpconfig);
+
+ vp_val = ((vdd->vp_rt_data.vstepmin_smpswaittimemin <<
+ vdd->vp_data->vp_common->vstepmin_smpswaittimemin_shift) |
+ (vdd->vp_rt_data.vstepmin_stepmin <<
+ vdd->vp_data->vp_common->vstepmin_stepmin_shift));
+ vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmin);
+
+ vp_val = ((vdd->vp_rt_data.vstepmax_smpswaittimemax <<
+ vdd->vp_data->vp_common->vstepmax_smpswaittimemax_shift) |
+ (vdd->vp_rt_data.vstepmax_stepmax <<
+ vdd->vp_data->vp_common->vstepmax_stepmax_shift));
+ vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmax);
+
+ vp_val = ((vdd->vp_rt_data.vlimitto_vddmax <<
+ vdd->vp_data->vp_common->vlimitto_vddmax_shift) |
+ (vdd->vp_rt_data.vlimitto_vddmin <<
+ vdd->vp_data->vp_common->vlimitto_vddmin_shift) |
+ (vdd->vp_rt_data.vlimitto_timeout <<
+ vdd->vp_data->vp_common->vlimitto_timeout_shift));
+ vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vlimitto);
}
static void __init vdd_debugfs_init(struct omap_vdd_info *vdd)
@@ -481,23 +274,23 @@ static void __init vdd_debugfs_init(struct omap_vdd_info *vdd)
}
(void) debugfs_create_x16("vp_errorgain", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vpconfig_errorgain));
+ &(vdd->vp_rt_data.vpconfig_errorgain));
(void) debugfs_create_x16("vp_smpswaittimemin", S_IRUGO,
vdd->debug_dir,
- &(vdd->vp_reg.vstepmin_smpswaittimemin));
+ &(vdd->vp_rt_data.vstepmin_smpswaittimemin));
(void) debugfs_create_x8("vp_stepmin", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vstepmin_stepmin));
+ &(vdd->vp_rt_data.vstepmin_stepmin));
(void) debugfs_create_x16("vp_smpswaittimemax", S_IRUGO,
vdd->debug_dir,
- &(vdd->vp_reg.vstepmax_smpswaittimemax));
+ &(vdd->vp_rt_data.vstepmax_smpswaittimemax));
(void) debugfs_create_x8("vp_stepmax", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vstepmax_stepmax));
+ &(vdd->vp_rt_data.vstepmax_stepmax));
(void) debugfs_create_x8("vp_vddmax", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vlimitto_vddmax));
+ &(vdd->vp_rt_data.vlimitto_vddmax));
(void) debugfs_create_x8("vp_vddmin", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vlimitto_vddmin));
+ &(vdd->vp_rt_data.vlimitto_vddmin));
(void) debugfs_create_x16("vp_timeout", S_IRUGO, vdd->debug_dir,
- &(vdd->vp_reg.vlimitto_timeout));
+ &(vdd->vp_rt_data.vlimitto_timeout));
(void) debugfs_create_file("curr_vp_volt", S_IRUGO, vdd->debug_dir,
(void *) vdd, &vp_volt_debug_fops);
(void) debugfs_create_file("curr_nominal_volt", S_IRUGO,
@@ -510,8 +303,12 @@ static int _pre_volt_scale(struct omap_vdd_info *vdd,
unsigned long target_volt, u8 *target_vsel, u8 *current_vsel)
{
struct omap_volt_data *volt_data;
+ const struct omap_vc_common_data *vc_common;
+ const struct omap_vp_common_data *vp_common;
u32 vc_cmdval, vp_errgain_val;
- u16 vp_mod, vc_mod;
+
+ vc_common = vdd->vc_data->vc_common;
+ vp_common = vdd->vp_data->vp_common;
/* Check if suffiecient pmic info is available for this vdd */
if (!vdd->pmic_info) {
@@ -533,33 +330,30 @@ static int _pre_volt_scale(struct omap_vdd_info *vdd,
return -EINVAL;
}
- vp_mod = vdd->vp_reg.prm_mod;
- vc_mod = vdd->vc_reg.prm_mod;
-
/* Get volt_data corresponding to target_volt */
volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt);
if (IS_ERR(volt_data))
volt_data = NULL;
*target_vsel = vdd->pmic_info->uv_to_vsel(target_volt);
- *current_vsel = vdd->read_reg(vp_mod, vdd->vp_offs.voltage);
+ *current_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
/* Setting the ON voltage to the new target voltage */
- vc_cmdval = vdd->read_reg(vc_mod, vdd->vc_reg.cmdval_reg);
- vc_cmdval &= ~vdd->vc_reg.cmd_on_mask;
- vc_cmdval |= (*target_vsel << vdd->vc_reg.cmd_on_shift);
- vdd->write_reg(vc_cmdval, vc_mod, vdd->vc_reg.cmdval_reg);
+ vc_cmdval = vdd->read_reg(prm_mod_offs, vdd->vc_data->cmdval_reg);
+ vc_cmdval &= ~vc_common->cmd_on_mask;
+ vc_cmdval |= (*target_vsel << vc_common->cmd_on_shift);
+ vdd->write_reg(vc_cmdval, prm_mod_offs, vdd->vc_data->cmdval_reg);
/* Setting vp errorgain based on the voltage */
if (volt_data) {
- vp_errgain_val = vdd->read_reg(vp_mod,
- vdd->vp_offs.vpconfig);
- vdd->vp_reg.vpconfig_errorgain = volt_data->vp_errgain;
- vp_errgain_val &= ~vdd->vp_reg.vpconfig_errorgain_mask;
- vp_errgain_val |= vdd->vp_reg.vpconfig_errorgain <<
- vdd->vp_reg.vpconfig_errorgain_shift;
- vdd->write_reg(vp_errgain_val, vp_mod,
- vdd->vp_offs.vpconfig);
+ vp_errgain_val = vdd->read_reg(prm_mod_offs,
+ vdd->vp_data->vpconfig);
+ vdd->vp_rt_data.vpconfig_errorgain = volt_data->vp_errgain;
+ vp_errgain_val &= ~vp_common->vpconfig_errorgain_mask;
+ vp_errgain_val |= vdd->vp_rt_data.vpconfig_errorgain <<
+ vp_common->vpconfig_errorgain_shift;
+ vdd->write_reg(vp_errgain_val, prm_mod_offs,
+ vdd->vp_data->vpconfig);
}
return 0;
@@ -585,7 +379,6 @@ static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd,
{
u32 loop_cnt = 0, retries_cnt = 0;
u32 vc_valid, vc_bypass_val_reg, vc_bypass_value;
- u16 mod;
u8 target_vsel, current_vsel;
int ret;
@@ -593,20 +386,19 @@ static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd,
if (ret)
return ret;
- mod = vdd->vc_reg.prm_mod;
-
- vc_valid = vdd->vc_reg.valid;
- vc_bypass_val_reg = vdd->vc_reg.bypass_val_reg;
- vc_bypass_value = (target_vsel << vdd->vc_reg.data_shift) |
+ vc_valid = vdd->vc_data->vc_common->valid;
+ vc_bypass_val_reg = vdd->vc_data->vc_common->bypass_val_reg;
+ vc_bypass_value = (target_vsel << vdd->vc_data->vc_common->data_shift) |
(vdd->pmic_info->pmic_reg <<
- vdd->vc_reg.regaddr_shift) |
+ vdd->vc_data->vc_common->regaddr_shift) |
(vdd->pmic_info->i2c_slave_addr <<
- vdd->vc_reg.slaveaddr_shift);
+ vdd->vc_data->vc_common->slaveaddr_shift);
- vdd->write_reg(vc_bypass_value, mod, vc_bypass_val_reg);
- vdd->write_reg(vc_bypass_value | vc_valid, mod, vc_bypass_val_reg);
+ vdd->write_reg(vc_bypass_value, prm_mod_offs, vc_bypass_val_reg);
+ vdd->write_reg(vc_bypass_value | vc_valid, prm_mod_offs,
+ vc_bypass_val_reg);
- vc_bypass_value = vdd->read_reg(mod, vc_bypass_val_reg);
+ vc_bypass_value = vdd->read_reg(prm_mod_offs, vc_bypass_val_reg);
/*
* Loop till the bypass command is acknowledged from the SMPS.
* NOTE: This is legacy code. The loop count and retry count needs
@@ -625,7 +417,8 @@ static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd,
loop_cnt = 0;
udelay(10);
}
- vc_bypass_value = vdd->read_reg(mod, vc_bypass_val_reg);
+ vc_bypass_value = vdd->read_reg(prm_mod_offs,
+ vc_bypass_val_reg);
}
_post_volt_scale(vdd, target_volt, target_vsel, current_vsel);
@@ -637,7 +430,6 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
unsigned long target_volt)
{
u32 vpconfig;
- u16 mod, ocp_mod;
u8 target_vsel, current_vsel, prm_irqst_reg;
int ret, timeout = 0;
@@ -645,20 +437,18 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
if (ret)
return ret;
- mod = vdd->vp_reg.prm_mod;
- ocp_mod = vdd->ocp_mod;
- prm_irqst_reg = vdd->prm_irqst_reg;
+ prm_irqst_reg = vdd->vp_data->prm_irqst_data->prm_irqst_reg;
/*
* Clear all pending TransactionDone interrupt/status. Typical latency
* is <3us
*/
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
- vdd->write_reg(vdd->vp_reg.tranxdone_status,
- ocp_mod, prm_irqst_reg);
- if (!(vdd->read_reg(ocp_mod, prm_irqst_reg) &
- vdd->vp_reg.tranxdone_status))
- break;
+ vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status,
+ prm_irqst_ocp_mod_offs, prm_irqst_reg);
+ if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
+ vdd->vp_data->prm_irqst_data->tranxdone_status))
+ break;
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT) {
@@ -668,30 +458,31 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
}
/* Configure for VP-Force Update */
- vpconfig = vdd->read_reg(mod, vdd->vp_offs.vpconfig);
- vpconfig &= ~(vdd->vp_reg.vpconfig_initvdd |
- vdd->vp_reg.vpconfig_forceupdate |
- vdd->vp_reg.vpconfig_initvoltage_mask);
+ vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
+ vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvdd |
+ vdd->vp_data->vp_common->vpconfig_forceupdate |
+ vdd->vp_data->vp_common->vpconfig_initvoltage_mask);
vpconfig |= ((target_vsel <<
- vdd->vp_reg.vpconfig_initvoltage_shift));
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vdd->vp_data->vp_common->vpconfig_initvoltage_shift));
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Trigger initVDD value copy to voltage processor */
- vpconfig |= vdd->vp_reg.vpconfig_initvdd;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig |= vdd->vp_data->vp_common->vpconfig_initvdd;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Force update of voltage */
- vpconfig |= vdd->vp_reg.vpconfig_forceupdate;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig |= vdd->vp_data->vp_common->vpconfig_forceupdate;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/*
* Wait for TransactionDone. Typical latency is <200us.
* Depends on SMPSWAITTIMEMIN/MAX and voltage change
*/
timeout = 0;
- omap_test_timeout((vdd->read_reg(ocp_mod, prm_irqst_reg) &
- vdd->vp_reg.tranxdone_status),
- VP_TRANXDONE_TIMEOUT, timeout);
+ pr_err("prm_irqst_ocp_mod_offs = %08x\n", prm_irqst_ocp_mod_offs);
+ omap_test_timeout((vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
+ vdd->vp_data->prm_irqst_data->tranxdone_status),
+ VP_TRANXDONE_TIMEOUT, timeout);
if (timeout >= VP_TRANXDONE_TIMEOUT)
pr_err("%s: vdd_%s TRANXDONE timeout exceeded."
"TRANXDONE never got set after the voltage update\n",
@@ -705,11 +496,11 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
*/
timeout = 0;
while (timeout++ < VP_TRANXDONE_TIMEOUT) {
- vdd->write_reg(vdd->vp_reg.tranxdone_status,
- ocp_mod, prm_irqst_reg);
- if (!(vdd->read_reg(ocp_mod, prm_irqst_reg) &
- vdd->vp_reg.tranxdone_status))
- break;
+ vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status,
+ prm_irqst_ocp_mod_offs, prm_irqst_reg);
+ if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) &
+ vdd->vp_data->prm_irqst_data->tranxdone_status))
+ break;
udelay(1);
}
@@ -718,222 +509,95 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd,
"to clear the TRANXDONE status\n",
__func__, vdd->voltdm.name);
- vpconfig = vdd->read_reg(mod, vdd->vp_offs.vpconfig);
+ vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear initVDD copy trigger bit */
- vpconfig &= ~vdd->vp_reg.vpconfig_initvdd;;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig &= ~vdd->vp_data->vp_common->vpconfig_initvdd;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/* Clear force bit */
- vpconfig &= ~vdd->vp_reg.vpconfig_forceupdate;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig &= ~vdd->vp_data->vp_common->vpconfig_forceupdate;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
return 0;
}
-/* OMAP3 specific voltage init functions */
+static void __init omap3_vfsm_init(struct omap_vdd_info *vdd)
+{
+ /*
+ * Voltage Manager FSM parameters init
+ * XXX This data should be passed in from the board file
+ */
+ vdd->write_reg(OMAP3_CLKSETUP, prm_mod_offs, OMAP3_PRM_CLKSETUP_OFFSET);
+ vdd->write_reg(OMAP3_VOLTOFFSET, prm_mod_offs,
+ OMAP3_PRM_VOLTOFFSET_OFFSET);
+ vdd->write_reg(OMAP3_VOLTSETUP2, prm_mod_offs,
+ OMAP3_PRM_VOLTSETUP2_OFFSET);
+}
-/*
- * Intializes the voltage controller registers with the PMIC and board
- * specific parameters and voltage setup times for OMAP3.
- */
static void __init omap3_vc_init(struct omap_vdd_info *vdd)
{
- u32 vc_val;
- u16 mod;
- u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
static bool is_initialized;
+ u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
+ u32 vc_val;
- if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) {
- pr_err("%s: PMIC info requried to configure vc for"
- "vdd_%s not populated.Hence cannot initialize vc\n",
- __func__, vdd->voltdm.name);
- return;
- }
-
- if (!vdd->read_reg || !vdd->write_reg) {
- pr_err("%s: No read/write API for accessing vdd_%s regs\n",
- __func__, vdd->voltdm.name);
+ if (is_initialized)
return;
- }
-
- mod = vdd->vc_reg.prm_mod;
-
- /* Set up the SMPS_SA(i2c slave address in VC */
- vc_val = vdd->read_reg(mod, vdd->vc_reg.smps_sa_reg);
- vc_val &= ~vdd->vc_reg.smps_sa_mask;
- vc_val |= vdd->pmic_info->i2c_slave_addr << vdd->vc_reg.smps_sa_shift;
- vdd->write_reg(vc_val, mod, vdd->vc_reg.smps_sa_reg);
-
- /* Setup the VOLRA(pmic reg addr) in VC */
- vc_val = vdd->read_reg(mod, vdd->vc_reg.smps_volra_reg);
- vc_val &= ~vdd->vc_reg.smps_volra_mask;
- vc_val |= vdd->pmic_info->pmic_reg << vdd->vc_reg.smps_volra_shift;
- vdd->write_reg(vc_val, mod, vdd->vc_reg.smps_volra_reg);
-
- /*Configure the setup times */
- vc_val = vdd->read_reg(mod, vdd->vc_reg.voltsetup_reg);
- vc_val &= ~vdd->vc_reg.voltsetup_mask;
- vc_val |= vdd->pmic_info->volt_setup_time <<
- vdd->vc_reg.voltsetup_shift;
- vdd->write_reg(vc_val, mod, vdd->vc_reg.voltsetup_reg);
/* Set up the on, inactive, retention and off voltage */
on_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->on_volt);
onlp_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->onlp_volt);
ret_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->ret_volt);
off_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->off_volt);
- vc_val = ((on_vsel << vdd->vc_reg.cmd_on_shift) |
- (onlp_vsel << vdd->vc_reg.cmd_onlp_shift) |
- (ret_vsel << vdd->vc_reg.cmd_ret_shift) |
- (off_vsel << vdd->vc_reg.cmd_off_shift));
- vdd->write_reg(vc_val, mod, vdd->vc_reg.cmdval_reg);
-
- if (is_initialized)
- return;
+ vc_val = ((on_vsel << vdd->vc_data->vc_common->cmd_on_shift) |
+ (onlp_vsel << vdd->vc_data->vc_common->cmd_onlp_shift) |
+ (ret_vsel << vdd->vc_data->vc_common->cmd_ret_shift) |
+ (off_vsel << vdd->vc_data->vc_common->cmd_off_shift));
+ vdd->write_reg(vc_val, prm_mod_offs, vdd->vc_data->cmdval_reg);
- /* Generic VC parameters init */
- vdd->write_reg(OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK, mod,
+ /*
+ * Generic VC parameters init
+ * XXX This data should be abstracted out
+ */
+ vdd->write_reg(OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK, prm_mod_offs,
OMAP3_PRM_VC_CH_CONF_OFFSET);
- vdd->write_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK, mod,
+ vdd->write_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK, prm_mod_offs,
OMAP3_PRM_VC_I2C_CFG_OFFSET);
- vdd->write_reg(OMAP3_CLKSETUP, mod, OMAP3_PRM_CLKSETUP_OFFSET);
- vdd->write_reg(OMAP3_VOLTOFFSET, mod, OMAP3_PRM_VOLTOFFSET_OFFSET);
- vdd->write_reg(OMAP3_VOLTSETUP2, mod, OMAP3_PRM_VOLTSETUP2_OFFSET);
+
+ omap3_vfsm_init(vdd);
+
is_initialized = true;
}
-/* Sets up all the VDD related info for OMAP3 */
-static int __init omap3_vdd_data_configure(struct omap_vdd_info *vdd)
+
+/* OMAP4 specific voltage init functions */
+static void __init omap4_vc_init(struct omap_vdd_info *vdd)
{
- struct clk *sys_ck;
- u32 sys_clk_speed, timeout_val, waittime;
+ static bool is_initialized;
+ u32 vc_val;
- if (!vdd->pmic_info) {
- pr_err("%s: PMIC info requried to configure vdd_%s not"
- "populated.Hence cannot initialize vdd_%s\n",
- __func__, vdd->voltdm.name, vdd->voltdm.name);
- return -EINVAL;
- }
+ if (is_initialized)
+ return;
- if (!strcmp(vdd->voltdm.name, "mpu")) {
- if (cpu_is_omap3630())
- vdd->volt_data = omap36xx_vddmpu_volt_data;
- else
- vdd->volt_data = omap34xx_vddmpu_volt_data;
-
- vdd->vp_reg.tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK;
- vdd->vc_reg.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET;
- vdd->vc_reg.smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT;
- vdd->vc_reg.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA0_MASK;
- vdd->vc_reg.smps_volra_shift = OMAP3430_VOLRA0_SHIFT;
- vdd->vc_reg.smps_volra_mask = OMAP3430_VOLRA0_MASK;
- vdd->vc_reg.voltsetup_shift = OMAP3430_SETUP_TIME1_SHIFT;
- vdd->vc_reg.voltsetup_mask = OMAP3430_SETUP_TIME1_MASK;
- } else if (!strcmp(vdd->voltdm.name, "core")) {
- if (cpu_is_omap3630())
- vdd->volt_data = omap36xx_vddcore_volt_data;
- else
- vdd->volt_data = omap34xx_vddcore_volt_data;
-
- vdd->vp_reg.tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK;
- vdd->vc_reg.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET;
- vdd->vc_reg.smps_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT;
- vdd->vc_reg.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA1_MASK;
- vdd->vc_reg.smps_volra_shift = OMAP3430_VOLRA1_SHIFT;
- vdd->vc_reg.smps_volra_mask = OMAP3430_VOLRA1_MASK;
- vdd->vc_reg.voltsetup_shift = OMAP3430_SETUP_TIME2_SHIFT;
- vdd->vc_reg.voltsetup_mask = OMAP3430_SETUP_TIME2_MASK;
- } else {
- pr_warning("%s: vdd_%s does not exisit in OMAP3\n",
- __func__, vdd->voltdm.name);
- return -EINVAL;
- }
+ /* TODO: Configure setup times and CMD_VAL values*/
/*
- * Sys clk rate is require to calculate vp timeout value and
- * smpswaittimemin and smpswaittimemax.
+ * Generic VC parameters init
+ * XXX This data should be abstracted out
*/
- sys_ck = clk_get(NULL, "sys_ck");
- if (IS_ERR(sys_ck)) {
- pr_warning("%s: Could not get the sys clk to calculate"
- "various vdd_%s params\n", __func__, vdd->voltdm.name);
- return -EINVAL;
- }
- sys_clk_speed = clk_get_rate(sys_ck);
- clk_put(sys_ck);
- /* Divide to avoid overflow */
- sys_clk_speed /= 1000;
-
- /* Generic voltage parameters */
- vdd->curr_volt = 1200000;
- vdd->ocp_mod = OCP_MOD;
- vdd->prm_irqst_reg = OMAP3_PRM_IRQSTATUS_MPU_OFFSET;
- vdd->read_reg = omap3_voltage_read_reg;
- vdd->write_reg = omap3_voltage_write_reg;
- vdd->volt_scale = vp_forceupdate_scale_voltage;
- vdd->vp_enabled = false;
+ vc_val = (OMAP4430_RAV_VDD_MPU_L_MASK | OMAP4430_CMD_VDD_MPU_L_MASK |
+ OMAP4430_RAV_VDD_IVA_L_MASK | OMAP4430_CMD_VDD_IVA_L_MASK |
+ OMAP4430_RAV_VDD_CORE_L_MASK | OMAP4430_CMD_VDD_CORE_L_MASK);
+ vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_CHANNEL_OFFSET);
- /* VC parameters */
- vdd->vc_reg.prm_mod = OMAP3430_GR_MOD;
- vdd->vc_reg.smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET;
- vdd->vc_reg.smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET;
- vdd->vc_reg.bypass_val_reg = OMAP3_PRM_VC_BYPASS_VAL_OFFSET;
- vdd->vc_reg.voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET;
- vdd->vc_reg.data_shift = OMAP3430_DATA_SHIFT;
- vdd->vc_reg.slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT;
- vdd->vc_reg.regaddr_shift = OMAP3430_REGADDR_SHIFT;
- vdd->vc_reg.valid = OMAP3430_VALID_MASK;
- vdd->vc_reg.cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT;
- vdd->vc_reg.cmd_on_mask = OMAP3430_VC_CMD_ON_MASK;
- vdd->vc_reg.cmd_onlp_shift = OMAP3430_VC_CMD_ONLP_SHIFT;
- vdd->vc_reg.cmd_ret_shift = OMAP3430_VC_CMD_RET_SHIFT;
- vdd->vc_reg.cmd_off_shift = OMAP3430_VC_CMD_OFF_SHIFT;
-
- vdd->vp_reg.prm_mod = OMAP3430_GR_MOD;
-
- /* VPCONFIG bit fields */
- vdd->vp_reg.vpconfig_erroroffset = (vdd->pmic_info->vp_erroroffset <<
- OMAP3430_ERROROFFSET_SHIFT);
- vdd->vp_reg.vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK;
- vdd->vp_reg.vpconfig_errorgain_shift = OMAP3430_ERRORGAIN_SHIFT;
- vdd->vp_reg.vpconfig_initvoltage_shift = OMAP3430_INITVOLTAGE_SHIFT;
- vdd->vp_reg.vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK;
- vdd->vp_reg.vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK;
- vdd->vp_reg.vpconfig_initvdd = OMAP3430_INITVDD_MASK;
- vdd->vp_reg.vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK;
- vdd->vp_reg.vpconfig_vpenable = OMAP3430_VPENABLE_MASK;
-
- /* VSTEPMIN VSTEPMAX bit fields */
- waittime = ((vdd->pmic_info->step_size / vdd->pmic_info->slew_rate) *
- sys_clk_speed) / 1000;
- vdd->vp_reg.vstepmin_smpswaittimemin = waittime;
- vdd->vp_reg.vstepmax_smpswaittimemax = waittime;
- vdd->vp_reg.vstepmin_stepmin = vdd->pmic_info->vp_vstepmin;
- vdd->vp_reg.vstepmax_stepmax = vdd->pmic_info->vp_vstepmax;
- vdd->vp_reg.vstepmin_smpswaittimemin_shift =
- OMAP3430_SMPSWAITTIMEMIN_SHIFT;
- vdd->vp_reg.vstepmax_smpswaittimemax_shift =
- OMAP3430_SMPSWAITTIMEMAX_SHIFT;
- vdd->vp_reg.vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT;
- vdd->vp_reg.vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT;
-
- /* VLIMITTO bit fields */
- timeout_val = (sys_clk_speed * vdd->pmic_info->vp_timeout_us) / 1000;
- vdd->vp_reg.vlimitto_timeout = timeout_val;
- vdd->vp_reg.vlimitto_vddmin = vdd->pmic_info->vp_vddmin;
- vdd->vp_reg.vlimitto_vddmax = vdd->pmic_info->vp_vddmax;
- vdd->vp_reg.vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT;
- vdd->vp_reg.vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT;
- vdd->vp_reg.vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT;
+ /* XXX These are magic numbers and do not belong! */
+ vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT);
+ vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET);
- return 0;
+ is_initialized = true;
}
-/* OMAP4 specific voltage init functions */
-static void __init omap4_vc_init(struct omap_vdd_info *vdd)
+static void __init omap_vc_init(struct omap_vdd_info *vdd)
{
u32 vc_val;
- u16 mod;
- static bool is_initialized;
if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) {
pr_err("%s: PMIC info requried to configure vc for"
@@ -948,173 +612,61 @@ static void __init omap4_vc_init(struct omap_vdd_info *vdd)
return;
}
- mod = vdd->vc_reg.prm_mod;
-
/* Set up the SMPS_SA(i2c slave address in VC */
- vc_val = vdd->read_reg(mod, vdd->vc_reg.smps_sa_reg);
- vc_val &= ~vdd->vc_reg.smps_sa_mask;
- vc_val |= vdd->pmic_info->i2c_slave_addr << vdd->vc_reg.smps_sa_shift;
- vdd->write_reg(vc_val, mod, vdd->vc_reg.smps_sa_reg);
+ vc_val = vdd->read_reg(prm_mod_offs,
+ vdd->vc_data->vc_common->smps_sa_reg);
+ vc_val &= ~vdd->vc_data->smps_sa_mask;
+ vc_val |= vdd->pmic_info->i2c_slave_addr << vdd->vc_data->smps_sa_shift;
+ vdd->write_reg(vc_val, prm_mod_offs,
+ vdd->vc_data->vc_common->smps_sa_reg);
/* Setup the VOLRA(pmic reg addr) in VC */
- vc_val = vdd->read_reg(mod, vdd->vc_reg.smps_volra_reg);
- vc_val &= ~vdd->vc_reg.smps_volra_mask;
- vc_val |= vdd->pmic_info->pmic_reg << vdd->vc_reg.smps_volra_shift;
- vdd->write_reg(vc_val, mod, vdd->vc_reg.smps_volra_reg);
-
- /* TODO: Configure setup times and CMD_VAL values*/
-
- if (is_initialized)
- return;
-
- /* Generic VC parameters init */
- vc_val = (OMAP4430_RAV_VDD_MPU_L_MASK | OMAP4430_CMD_VDD_MPU_L_MASK |
- OMAP4430_RAV_VDD_IVA_L_MASK | OMAP4430_CMD_VDD_IVA_L_MASK |
- OMAP4430_RAV_VDD_CORE_L_MASK | OMAP4430_CMD_VDD_CORE_L_MASK);
- vdd->write_reg(vc_val, mod, OMAP4_PRM_VC_CFG_CHANNEL_OFFSET);
-
- vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT);
- vdd->write_reg(vc_val, mod, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET);
+ vc_val = vdd->read_reg(prm_mod_offs,
+ vdd->vc_data->vc_common->smps_volra_reg);
+ vc_val &= ~vdd->vc_data->smps_volra_mask;
+ vc_val |= vdd->pmic_info->pmic_reg << vdd->vc_data->smps_volra_shift;
+ vdd->write_reg(vc_val, prm_mod_offs,
+ vdd->vc_data->vc_common->smps_volra_reg);
+
+ /* Configure the setup times */
+ vc_val = vdd->read_reg(prm_mod_offs, vdd->vfsm->voltsetup_reg);
+ vc_val &= ~vdd->vfsm->voltsetup_mask;
+ vc_val |= vdd->pmic_info->volt_setup_time <<
+ vdd->vfsm->voltsetup_shift;
+ vdd->write_reg(vc_val, prm_mod_offs, vdd->vfsm->voltsetup_reg);
- is_initialized = true;
+ if (cpu_is_omap34xx())
+ omap3_vc_init(vdd);
+ else if (cpu_is_omap44xx())
+ omap4_vc_init(vdd);
}
-/* Sets up all the VDD related info for OMAP4 */
-static int __init omap4_vdd_data_configure(struct omap_vdd_info *vdd)
+static int __init omap_vdd_data_configure(struct omap_vdd_info *vdd)
{
- struct clk *sys_ck;
- u32 sys_clk_speed, timeout_val, waittime;
+ int ret = -EINVAL;
if (!vdd->pmic_info) {
pr_err("%s: PMIC info requried to configure vdd_%s not"
"populated.Hence cannot initialize vdd_%s\n",
__func__, vdd->voltdm.name, vdd->voltdm.name);
- return -EINVAL;
+ goto ovdc_out;
}
- if (!strcmp(vdd->voltdm.name, "mpu")) {
- vdd->volt_data = omap44xx_vdd_mpu_volt_data;
- vdd->vp_reg.tranxdone_status =
- OMAP4430_VP_MPU_TRANXDONE_ST_MASK;
- vdd->vc_reg.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET;
- vdd->vc_reg.smps_sa_shift =
- OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_SHIFT;
- vdd->vc_reg.smps_sa_mask =
- OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK;
- vdd->vc_reg.smps_volra_shift = OMAP4430_VOLRA_VDD_MPU_L_SHIFT;
- vdd->vc_reg.smps_volra_mask = OMAP4430_VOLRA_VDD_MPU_L_MASK;
- vdd->vc_reg.voltsetup_reg =
- OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET;
- vdd->prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET;
- } else if (!strcmp(vdd->voltdm.name, "core")) {
- vdd->volt_data = omap44xx_vdd_core_volt_data;
- vdd->vp_reg.tranxdone_status =
- OMAP4430_VP_CORE_TRANXDONE_ST_MASK;
- vdd->vc_reg.cmdval_reg =
- OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET;
- vdd->vc_reg.smps_sa_shift = OMAP4430_SA_VDD_CORE_L_0_6_SHIFT;
- vdd->vc_reg.smps_sa_mask = OMAP4430_SA_VDD_CORE_L_0_6_MASK;
- vdd->vc_reg.smps_volra_shift = OMAP4430_VOLRA_VDD_CORE_L_SHIFT;
- vdd->vc_reg.smps_volra_mask = OMAP4430_VOLRA_VDD_CORE_L_MASK;
- vdd->vc_reg.voltsetup_reg =
- OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET;
- vdd->prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET;
- } else if (!strcmp(vdd->voltdm.name, "iva")) {
- vdd->volt_data = omap44xx_vdd_iva_volt_data;
- vdd->vp_reg.tranxdone_status =
- OMAP4430_VP_IVA_TRANXDONE_ST_MASK;
- vdd->vc_reg.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET;
- vdd->vc_reg.smps_sa_shift =
- OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_SHIFT;
- vdd->vc_reg.smps_sa_mask =
- OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK;
- vdd->vc_reg.smps_volra_shift = OMAP4430_VOLRA_VDD_IVA_L_SHIFT;
- vdd->vc_reg.smps_volra_mask = OMAP4430_VOLRA_VDD_IVA_L_MASK;
- vdd->vc_reg.voltsetup_reg =
- OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET;
- vdd->prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET;
- } else {
- pr_warning("%s: vdd_%s does not exisit in OMAP4\n",
- __func__, vdd->voltdm.name);
- return -EINVAL;
- }
+ if (IS_ERR_VALUE(_config_common_vdd_data(vdd)))
+ goto ovdc_out;
- /*
- * Sys clk rate is require to calculate vp timeout value and
- * smpswaittimemin and smpswaittimemax.
- */
- sys_ck = clk_get(NULL, "sys_clkin_ck");
- if (IS_ERR(sys_ck)) {
- pr_warning("%s: Could not get the sys clk to calculate"
- "various vdd_%s params\n", __func__, vdd->voltdm.name);
- return -EINVAL;
+ if (cpu_is_omap34xx()) {
+ vdd->read_reg = omap3_voltage_read_reg;
+ vdd->write_reg = omap3_voltage_write_reg;
+ ret = 0;
+ } else if (cpu_is_omap44xx()) {
+ vdd->read_reg = omap4_voltage_read_reg;
+ vdd->write_reg = omap4_voltage_write_reg;
+ ret = 0;
}
- sys_clk_speed = clk_get_rate(sys_ck);
- clk_put(sys_ck);
- /* Divide to avoid overflow */
- sys_clk_speed /= 1000;
-
- /* Generic voltage parameters */
- vdd->curr_volt = 1200000;
- vdd->ocp_mod = OMAP4430_PRM_OCP_SOCKET_INST;
- vdd->read_reg = omap4_voltage_read_reg;
- vdd->write_reg = omap4_voltage_write_reg;
- vdd->volt_scale = vp_forceupdate_scale_voltage;
- vdd->vp_enabled = false;
- /* VC parameters */
- vdd->vc_reg.prm_mod = OMAP4430_PRM_DEVICE_INST;
- vdd->vc_reg.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET;
- vdd->vc_reg.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET;
- vdd->vc_reg.bypass_val_reg = OMAP4_PRM_VC_VAL_BYPASS_OFFSET;
- vdd->vc_reg.data_shift = OMAP4430_DATA_SHIFT;
- vdd->vc_reg.slaveaddr_shift = OMAP4430_SLAVEADDR_SHIFT;
- vdd->vc_reg.regaddr_shift = OMAP4430_REGADDR_SHIFT;
- vdd->vc_reg.valid = OMAP4430_VALID_MASK;
- vdd->vc_reg.cmd_on_shift = OMAP4430_ON_SHIFT;
- vdd->vc_reg.cmd_on_mask = OMAP4430_ON_MASK;
- vdd->vc_reg.cmd_onlp_shift = OMAP4430_ONLP_SHIFT;
- vdd->vc_reg.cmd_ret_shift = OMAP4430_RET_SHIFT;
- vdd->vc_reg.cmd_off_shift = OMAP4430_OFF_SHIFT;
-
- vdd->vp_reg.prm_mod = OMAP4430_PRM_DEVICE_INST;
-
- /* VPCONFIG bit fields */
- vdd->vp_reg.vpconfig_erroroffset = (vdd->pmic_info->vp_erroroffset <<
- OMAP4430_ERROROFFSET_SHIFT);
- vdd->vp_reg.vpconfig_errorgain_mask = OMAP4430_ERRORGAIN_MASK;
- vdd->vp_reg.vpconfig_errorgain_shift = OMAP4430_ERRORGAIN_SHIFT;
- vdd->vp_reg.vpconfig_initvoltage_shift = OMAP4430_INITVOLTAGE_SHIFT;
- vdd->vp_reg.vpconfig_initvoltage_mask = OMAP4430_INITVOLTAGE_MASK;
- vdd->vp_reg.vpconfig_timeouten = OMAP4430_TIMEOUTEN_MASK;
- vdd->vp_reg.vpconfig_initvdd = OMAP4430_INITVDD_MASK;
- vdd->vp_reg.vpconfig_forceupdate = OMAP4430_FORCEUPDATE_MASK;
- vdd->vp_reg.vpconfig_vpenable = OMAP4430_VPENABLE_MASK;
-
- /* VSTEPMIN VSTEPMAX bit fields */
- waittime = ((vdd->pmic_info->step_size / vdd->pmic_info->slew_rate) *
- sys_clk_speed) / 1000;
- vdd->vp_reg.vstepmin_smpswaittimemin = waittime;
- vdd->vp_reg.vstepmax_smpswaittimemax = waittime;
- vdd->vp_reg.vstepmin_stepmin = vdd->pmic_info->vp_vstepmin;
- vdd->vp_reg.vstepmax_stepmax = vdd->pmic_info->vp_vstepmax;
- vdd->vp_reg.vstepmin_smpswaittimemin_shift =
- OMAP4430_SMPSWAITTIMEMIN_SHIFT;
- vdd->vp_reg.vstepmax_smpswaittimemax_shift =
- OMAP4430_SMPSWAITTIMEMAX_SHIFT;
- vdd->vp_reg.vstepmin_stepmin_shift = OMAP4430_VSTEPMIN_SHIFT;
- vdd->vp_reg.vstepmax_stepmax_shift = OMAP4430_VSTEPMAX_SHIFT;
-
- /* VLIMITTO bit fields */
- timeout_val = (sys_clk_speed * vdd->pmic_info->vp_timeout_us) / 1000;
- vdd->vp_reg.vlimitto_timeout = timeout_val;
- vdd->vp_reg.vlimitto_vddmin = vdd->pmic_info->vp_vddmin;
- vdd->vp_reg.vlimitto_vddmax = vdd->pmic_info->vp_vddmax;
- vdd->vp_reg.vlimitto_vddmin_shift = OMAP4430_VDDMIN_SHIFT;
- vdd->vp_reg.vlimitto_vddmax_shift = OMAP4430_VDDMAX_SHIFT;
- vdd->vp_reg.vlimitto_timeout_shift = OMAP4430_TIMEOUT_SHIFT;
-
- return 0;
+ovdc_out:
+ return ret;
}
/* Public functions */
@@ -1162,8 +714,7 @@ unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm)
return 0;
}
- curr_vsel = vdd->read_reg(vdd->vp_reg.prm_mod,
- vdd->vp_offs.voltage);
+ curr_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
if (!vdd->pmic_info || !vdd->pmic_info->vsel_to_uv) {
pr_warning("%s: PMIC function to convert vsel to voltage"
@@ -1185,7 +736,6 @@ void omap_vp_enable(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
u32 vpconfig;
- u16 mod;
if (!voltdm || IS_ERR(voltdm)) {
pr_warning("%s: VDD specified does not exist!\n", __func__);
@@ -1199,8 +749,6 @@ void omap_vp_enable(struct voltagedomain *voltdm)
return;
}
- mod = vdd->vp_reg.prm_mod;
-
/* If VP is already enabled, do nothing. Return */
if (vdd->vp_enabled)
return;
@@ -1208,9 +756,9 @@ void omap_vp_enable(struct voltagedomain *voltdm)
vp_latch_vsel(vdd);
/* Enable VP */
- vpconfig = vdd->read_reg(mod, vdd->vp_offs.vpconfig);
- vpconfig |= vdd->vp_reg.vpconfig_vpenable;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
+ vpconfig |= vdd->vp_data->vp_common->vpconfig_vpenable;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
vdd->vp_enabled = true;
}
@@ -1225,7 +773,6 @@ void omap_vp_disable(struct voltagedomain *voltdm)
{
struct omap_vdd_info *vdd;
u32 vpconfig;
- u16 mod;
int timeout;
if (!voltdm || IS_ERR(voltdm)) {
@@ -1240,8 +787,6 @@ void omap_vp_disable(struct voltagedomain *voltdm)
return;
}
- mod = vdd->vp_reg.prm_mod;
-
/* If VP is already disabled, do nothing. Return */
if (!vdd->vp_enabled) {
pr_warning("%s: Trying to disable VP for vdd_%s when"
@@ -1250,14 +795,14 @@ void omap_vp_disable(struct voltagedomain *voltdm)
}
/* Disable VP */
- vpconfig = vdd->read_reg(mod, vdd->vp_offs.vpconfig);
- vpconfig &= ~vdd->vp_reg.vpconfig_vpenable;
- vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig);
+ vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig);
+ vpconfig &= ~vdd->vp_data->vp_common->vpconfig_vpenable;
+ vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig);
/*
* Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
*/
- omap_test_timeout((vdd->read_reg(mod, vdd->vp_offs.vstatus)),
+ omap_test_timeout((vdd->read_reg(prm_mod_offs, vdd->vp_data->vstatus)),
VP_IDLE_TIMEOUT, timeout);
if (timeout >= VP_IDLE_TIMEOUT)
@@ -1510,8 +1055,8 @@ struct voltagedomain *omap_voltage_domain_lookup(char *name)
}
for (i = 0; i < nr_scalable_vdd; i++) {
- if (!(strcmp(name, vdd_info[i].voltdm.name)))
- return &vdd_info[i].voltdm;
+ if (!(strcmp(name, vdd_info[i]->voltdm.name)))
+ return &vdd_info[i]->voltdm;
}
return ERR_PTR(-EINVAL);
@@ -1539,35 +1084,24 @@ int __init omap_voltage_late_init(void)
pr_err("%s: Unable to create voltage debugfs main dir\n",
__func__);
for (i = 0; i < nr_scalable_vdd; i++) {
- if (vdd_data_configure(&vdd_info[i]))
+ if (omap_vdd_data_configure(vdd_info[i]))
continue;
- vc_init(&vdd_info[i]);
- vp_init(&vdd_info[i]);
- vdd_debugfs_init(&vdd_info[i]);
+ omap_vc_init(vdd_info[i]);
+ vp_init(vdd_info[i]);
+ vdd_debugfs_init(vdd_info[i]);
}
return 0;
}
-/**
- * omap_voltage_early_init()- Volatage driver early init
- */
-static int __init omap_voltage_early_init(void)
+/* XXX document */
+int __init omap_voltage_early_init(s16 prm_mod, s16 prm_irqst_ocp_mod,
+ struct omap_vdd_info *omap_vdd_array[],
+ u8 omap_vdd_count)
{
- if (cpu_is_omap34xx()) {
- vdd_info = omap3_vdd_info;
- nr_scalable_vdd = OMAP3_NR_SCALABLE_VDD;
- vc_init = omap3_vc_init;
- vdd_data_configure = omap3_vdd_data_configure;
- } else if (cpu_is_omap44xx()) {
- vdd_info = omap4_vdd_info;
- nr_scalable_vdd = OMAP4_NR_SCALABLE_VDD;
- vc_init = omap4_vc_init;
- vdd_data_configure = omap4_vdd_data_configure;
- } else {
- pr_warning("%s: voltage driver support not added\n", __func__);
- }
-
+ prm_mod_offs = prm_mod;
+ prm_irqst_ocp_mod_offs = prm_irqst_ocp_mod;
+ vdd_info = omap_vdd_array;
+ nr_scalable_vdd = omap_vdd_count;
return 0;
}
-core_initcall(omap_voltage_early_init);
diff --git a/arch/arm/mach-omap2/voltage.h b/arch/arm/mach-omap2/voltage.h
index 5bd204e..af0cf65 100644
--- a/arch/arm/mach-omap2/voltage.h
+++ b/arch/arm/mach-omap2/voltage.h
@@ -16,9 +16,21 @@
#include <linux/err.h>
+#include "vc.h"
+#include "vp.h"
+
+/* XXX document */
#define VOLTSCALE_VPFORCEUPDATE 1
#define VOLTSCALE_VCBYPASS 2
+#define VOLT_DATA_DEFINE(_v_nom, _efuse_offs, _errminlimit, _errgain) \
+{ \
+ .volt_nominal = _v_nom, \
+ .sr_efuse_offs = _efuse_offs, \
+ .sr_errminlimit = _errminlimit, \
+ .vp_errgain = _errgain \
+}
+
/*
* OMAP3 GENERIC setup times. Revisit to see if these needs to be
* passed from board or PMIC file
@@ -59,6 +71,21 @@
#define OMAP4430_VDD_CORE_OPP100_UV 1100000
/**
+ * struct omap_vfsm_instance_data - per-voltage manager FSM register/bitfield
+ * data
+ * @voltsetup_mask:
+ * @voltsetup_reg:
+ * @voltsetup_shift:
+ *
+ * XXX What about VOLTOFFSET/VOLTCTRL?
+ */
+struct omap_vfsm_instance_data {
+ u32 voltsetup_mask;
+ u8 voltsetup_reg;
+ u8 voltsetup_shift;
+};
+
+/**
* struct voltagedomain - omap voltage domain global structure.
* @name: Name of the voltage domain which can be used as a unique
* identifier.
@@ -113,6 +140,42 @@ struct omap_volt_pmic_info {
u8 (*uv_to_vsel) (unsigned long uV);
};
+/**
+ * omap_vdd_info - Per Voltage Domain info
+ *
+ * @volt_data : voltage table having the distinct voltages supported
+ * by the domain and other associated per voltage data.
+ * @pmic_info : pmic specific parameters which should be populted by
+ * the pmic drivers.
+ * @vp_data : the register values, shifts, masks for various
+ * vp registers
+ * @vp_rt_data : VP data derived at runtime, not predefined
+ * @vc_data : structure containing various various vc registers,
+ * shifts, masks etc.
+ * @vfsm : voltage manager FSM data
+ * @voltdm : pointer to the voltage domain structure
+ * @debug_dir : debug directory for this voltage domain.
+ * @curr_volt : current voltage for this vdd.
+ * @vp_enabled : flag to keep track of whether vp is enabled or not
+ * @volt_scale : API to scale the voltage of the vdd.
+ */
+struct omap_vdd_info {
+ struct omap_volt_data *volt_data;
+ struct omap_volt_pmic_info *pmic_info;
+ struct omap_vp_instance_data *vp_data;
+ struct omap_vp_runtime_data vp_rt_data;
+ struct omap_vc_instance_data *vc_data;
+ const struct omap_vfsm_instance_data *vfsm;
+ struct voltagedomain voltdm;
+ struct dentry *debug_dir;
+ u32 curr_volt;
+ bool vp_enabled;
+ u32 (*read_reg) (u16 mod, u8 offset);
+ void (*write_reg) (u32 val, u16 mod, u8 offset);
+ int (*volt_scale) (struct omap_vdd_info *vdd,
+ unsigned long target_volt);
+};
+
unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm);
void omap_vp_enable(struct voltagedomain *voltdm);
void omap_vp_disable(struct voltagedomain *voltdm);
@@ -125,6 +188,9 @@ struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
unsigned long volt);
unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm);
struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm);
+int __init omap_voltage_early_init(s16 prm_mod, s16 prm_irqst_mod,
+ struct omap_vdd_info *omap_vdd_array[],
+ u8 omap_vdd_count);
#ifdef CONFIG_PM
int omap_voltage_register_pmic(struct voltagedomain *voltdm,
struct omap_volt_pmic_info *pmic_info);
diff --git a/arch/arm/mach-omap2/voltagedomains3xxx_data.c b/arch/arm/mach-omap2/voltagedomains3xxx_data.c
new file mode 100644
index 0000000..cb92209
--- /dev/null
+++ b/arch/arm/mach-omap2/voltagedomains3xxx_data.c
@@ -0,0 +1,104 @@
+/*
+ * OMAP3 voltage domain data
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <plat/common.h>
+
+#include "prm-regbits-34xx.h"
+#include "prm-regbits-44xx.h"
+#include "prm44xx.h"
+#include "prcm44xx.h"
+#include "prminst44xx.h"
+#include "control.h"
+#include "voltage.h"
+#include "omap_opp_data.h"
+#include "vc.h"
+#include "vp.h"
+
+/*
+ * VDD data
+ */
+
+static const struct omap_vfsm_instance_data omap3_vdd1_vfsm_data = {
+ .voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
+ .voltsetup_shift = OMAP3430_SETUP_TIME1_SHIFT,
+ .voltsetup_mask = OMAP3430_SETUP_TIME1_MASK,
+};
+
+static struct omap_vdd_info omap3_vdd1_info = {
+ .vp_data = &omap3_vp1_data,
+ .vc_data = &omap3_vc1_data,
+ .vfsm = &omap3_vdd1_vfsm_data,
+ .voltdm = {
+ .name = "mpu",
+ },
+};
+
+static const struct omap_vfsm_instance_data omap3_vdd2_vfsm_data = {
+ .voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
+ .voltsetup_shift = OMAP3430_SETUP_TIME2_SHIFT,
+ .voltsetup_mask = OMAP3430_SETUP_TIME2_MASK,
+};
+
+static struct omap_vdd_info omap3_vdd2_info = {
+ .vp_data = &omap3_vp2_data,
+ .vc_data = &omap3_vc2_data,
+ .vfsm = &omap3_vdd2_vfsm_data,
+ .voltdm = {
+ .name = "core",
+ },
+};
+
+/* OMAP3 VDD sturctures */
+static struct omap_vdd_info *omap3_vdd_info[] = {
+ &omap3_vdd1_info,
+ &omap3_vdd2_info,
+};
+
+/* OMAP3 specific voltage init functions */
+static int __init omap3xxx_voltage_early_init(void)
+{
+ s16 prm_mod = OMAP3430_GR_MOD;
+ s16 prm_irqst_ocp_mod = OCP_MOD;
+
+ if (!cpu_is_omap34xx())
+ return 0;
+
+ /*
+ * XXX Will depend on the process, validation, and binning
+ * for the currently-running IC
+ */
+ if (cpu_is_omap3630()) {
+ omap3_vdd1_info.volt_data = omap36xx_vddmpu_volt_data;
+ omap3_vdd2_info.volt_data = omap36xx_vddcore_volt_data;
+ } else {
+ omap3_vdd1_info.volt_data = omap34xx_vddmpu_volt_data;
+ omap3_vdd2_info.volt_data = omap34xx_vddcore_volt_data;
+ }
+
+ return omap_voltage_early_init(prm_mod, prm_irqst_ocp_mod,
+ omap3_vdd_info,
+ ARRAY_SIZE(omap3_vdd_info));
+};
+core_initcall(omap3xxx_voltage_early_init);
diff --git a/arch/arm/mach-omap2/voltagedomains44xx_data.c b/arch/arm/mach-omap2/voltagedomains44xx_data.c
new file mode 100644
index 0000000..52bfd8a
--- /dev/null
+++ b/arch/arm/mach-omap2/voltagedomains44xx_data.c
@@ -0,0 +1,108 @@
+/*
+ * OMAP3/OMAP4 Voltage Management Routines
+ *
+ * Author: Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Kalle Jokiniemi
+ *
+ * Copyright (C) 2010 Texas Instruments, Inc.
+ * Thara Gopinath <thara@ti.com>
+ *
+ * This program 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <plat/common.h>
+
+#include "prm-regbits-34xx.h"
+#include "prm-regbits-44xx.h"
+#include "prm44xx.h"
+#include "prcm44xx.h"
+#include "prminst44xx.h"
+#include "control.h"
+#include "voltage.h"
+#include "omap_opp_data.h"
+#include "vc.h"
+#include "vp.h"
+
+/* OMAP4 VDD sturctures */
+static const struct omap_vfsm_instance_data omap4_vdd_mpu_vfsm_data = {
+ .voltsetup_reg = OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET,
+};
+
+static struct omap_vdd_info omap4_vdd_mpu_info = {
+ .vp_data = &omap4_vp_mpu_data,
+ .vc_data = &omap4_vc_mpu_data,
+ .vfsm = &omap4_vdd_mpu_vfsm_data,
+ .voltdm = {
+ .name = "mpu",
+ },
+};
+
+static const struct omap_vfsm_instance_data omap4_vdd_iva_vfsm_data = {
+ .voltsetup_reg = OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET,
+};
+
+static struct omap_vdd_info omap4_vdd_iva_info = {
+ .vp_data = &omap4_vp_iva_data,
+ .vc_data = &omap4_vc_iva_data,
+ .vfsm = &omap4_vdd_iva_vfsm_data,
+ .voltdm = {
+ .name = "iva",
+ },
+};
+
+static const struct omap_vfsm_instance_data omap4_vdd_core_vfsm_data = {
+ .voltsetup_reg = OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
+};
+
+static struct omap_vdd_info omap4_vdd_core_info = {
+ .vp_data = &omap4_vp_core_data,
+ .vc_data = &omap4_vc_core_data,
+ .vfsm = &omap4_vdd_core_vfsm_data,
+ .voltdm = {
+ .name = "core",
+ },
+};
+
+static struct omap_vdd_info *omap4_vdd_info[] = {
+ &omap4_vdd_mpu_info,
+ &omap4_vdd_iva_info,
+ &omap4_vdd_core_info,
+};
+
+/* OMAP4 specific voltage init functions */
+static int __init omap44xx_voltage_early_init(void)
+{
+ s16 prm_mod = OMAP4430_PRM_DEVICE_INST;
+ s16 prm_irqst_ocp_mod = OMAP4430_PRM_OCP_SOCKET_INST;
+
+ if (!cpu_is_omap44xx())
+ return 0;
+
+ /*
+ * XXX Will depend on the process, validation, and binning
+ * for the currently-running IC
+ */
+ omap4_vdd_mpu_info.volt_data = omap44xx_vdd_mpu_volt_data;
+ omap4_vdd_iva_info.volt_data = omap44xx_vdd_iva_volt_data;
+ omap4_vdd_core_info.volt_data = omap44xx_vdd_core_volt_data;
+
+ return omap_voltage_early_init(prm_mod, prm_irqst_ocp_mod,
+ omap4_vdd_info,
+ ARRAY_SIZE(omap4_vdd_info));
+};
+core_initcall(omap44xx_voltage_early_init);
diff --git a/arch/arm/mach-omap2/vp.h b/arch/arm/mach-omap2/vp.h
new file mode 100644
index 0000000..7ce134f
--- /dev/null
+++ b/arch/arm/mach-omap2/vp.h
@@ -0,0 +1,143 @@
+/*
+ * OMAP3/4 Voltage Processor (VP) structure and macro definitions
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_VP_H
+#define __ARCH_ARM_MACH_OMAP2_VP_H
+
+#include <linux/kernel.h>
+
+/* XXX document */
+#define VP_IDLE_TIMEOUT 200
+#define VP_TRANXDONE_TIMEOUT 300
+
+
+/**
+ * struct omap_vp_common_data - register data common to all VDDs
+ * @vpconfig_errorgain_mask: ERRORGAIN bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_initvoltage_mask: INITVOLTAGE bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_timeouten_mask: TIMEOUT bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_initvdd: INITVDD bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_forceupdate: FORCEUPDATE bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_vpenable: VPENABLE bitmask in the PRM_VP*_CONFIG reg
+ * @vpconfig_erroroffset_shift: ERROROFFSET field shift in PRM_VP*_CONFIG reg
+ * @vpconfig_errorgain_shift: ERRORGAIN field shift in PRM_VP*_CONFIG reg
+ * @vpconfig_initvoltage_shift: INITVOLTAGE field shift in PRM_VP*_CONFIG reg
+ * @vpconfig_stepmin_shift: VSTEPMIN field shift in the PRM_VP*_VSTEPMIN reg
+ * @vpconfig_smpswaittimemin_shift: SMPSWAITTIMEMIN field shift in PRM_VP*_VSTEPMIN reg
+ * @vpconfig_stepmax_shift: VSTEPMAX field shift in the PRM_VP*_VSTEPMAX reg
+ * @vpconfig_smpswaittimemax_shift: SMPSWAITTIMEMAX field shift in PRM_VP*_VSTEPMAX reg
+ * @vpconfig_vlimitto_vddmin_shift: VDDMIN field shift in PRM_VP*_VLIMITTO reg
+ * @vpconfig_vlimitto_vddmax_shift: VDDMAX field shift in PRM_VP*_VLIMITTO reg
+ * @vpconfig_vlimitto_timeout_shift: TIMEOUT field shift in PRM_VP*_VLIMITTO reg
+ *
+ * XXX It it not necessary to have both a mask and a shift for the same
+ * bitfield - remove one
+ * XXX Many of these fields are wrongly named -- e.g., vpconfig_smps* -- fix!
+ */
+struct omap_vp_common_data {
+ u32 vpconfig_errorgain_mask;
+ u32 vpconfig_initvoltage_mask;
+ u32 vpconfig_timeouten;
+ u32 vpconfig_initvdd;
+ u32 vpconfig_forceupdate;
+ u32 vpconfig_vpenable;
+ u8 vpconfig_erroroffset_shift;
+ u8 vpconfig_errorgain_shift;
+ u8 vpconfig_initvoltage_shift;
+ u8 vstepmin_stepmin_shift;
+ u8 vstepmin_smpswaittimemin_shift;
+ u8 vstepmax_stepmax_shift;
+ u8 vstepmax_smpswaittimemax_shift;
+ u8 vlimitto_vddmin_shift;
+ u8 vlimitto_vddmax_shift;
+ u8 vlimitto_timeout_shift;
+};
+
+/**
+ * struct omap_vp_prm_irqst_data - PRM_IRQSTATUS_MPU.VP_TRANXDONE_ST data
+ * @prm_irqst_reg: reg offset for PRM_IRQSTATUS_MPU from top of PRM
+ * @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg
+ *
+ * XXX prm_irqst_reg does not belong here
+ * XXX Note that on OMAP3, VP_TRANXDONE interrupt may not work due to a
+ * hardware bug
+ * XXX This structure is probably not needed
+ */
+struct omap_vp_prm_irqst_data {
+ u8 prm_irqst_reg;
+ u32 tranxdone_status;
+};
+
+/**
+ * struct omap_vp_instance_data - VP register offsets (per-VDD)
+ * @vp_common: pointer to struct omap_vp_common_data * for this SoC
+ * @prm_irqst_data: pointer to struct omap_vp_prm_irqst_data for this VDD
+ * @vpconfig: PRM_VP*_CONFIG reg offset from PRM start
+ * @vstepmin: PRM_VP*_VSTEPMIN reg offset from PRM start
+ * @vlimitto: PRM_VP*_VLIMITTO reg offset from PRM start
+ * @vstatus: PRM_VP*_VSTATUS reg offset from PRM start
+ * @voltage: PRM_VP*_VOLTAGE reg offset from PRM start
+ *
+ * XXX vp_common is probably not needed since it is per-SoC
+ */
+struct omap_vp_instance_data {
+ const struct omap_vp_common_data *vp_common;
+ const struct omap_vp_prm_irqst_data *prm_irqst_data;
+ u8 vpconfig;
+ u8 vstepmin;
+ u8 vstepmax;
+ u8 vlimitto;
+ u8 vstatus;
+ u8 voltage;
+};
+
+/**
+ * struct omap_vp_runtime_data - VP data populated at runtime by code
+ * @vpconfig_erroroffset: value of ERROROFFSET bitfield in PRM_VP*_CONFIG
+ * @vpconfig_errorgain: value of ERRORGAIN bitfield in PRM_VP*_CONFIG
+ * @vstepmin_smpswaittimemin: value of SMPSWAITTIMEMIN bitfield in PRM_VP*_VSTEPMIN
+ * @vstepmax_smpswaittimemax: value of SMPSWAITTIMEMAX bitfield in PRM_VP*_VSTEPMAX
+ * @vlimitto_timeout: value of TIMEOUT bitfield in PRM_VP*_VLIMITTO
+ * @vstepmin_stepmin: value of VSTEPMIN bitfield in PRM_VP*_VSTEPMIN
+ * @vstepmax_stepmax: value of VSTEPMAX bitfield in PRM_VP*_VSTEPMAX
+ * @vlimitto_vddmin: value of VDDMIN bitfield in PRM_VP*_VLIMITTO
+ * @vlimitto_vddmax: value of VDDMAX bitfield in PRM_VP*_VLIMITTO
+ *
+ * XXX Is this structure really needed? Why not just program the
+ * device directly? They are in PRM space, therefore in the WKUP
+ * powerdomain, so register contents should not be lost in off-mode.
+ * XXX Some of these fields are incorrectly named, e.g., vstep*
+ */
+struct omap_vp_runtime_data {
+ u32 vpconfig_erroroffset;
+ u16 vpconfig_errorgain;
+ u16 vstepmin_smpswaittimemin;
+ u16 vstepmax_smpswaittimemax;
+ u16 vlimitto_timeout;
+ u8 vstepmin_stepmin;
+ u8 vstepmax_stepmax;
+ u8 vlimitto_vddmin;
+ u8 vlimitto_vddmax;
+};
+
+extern struct omap_vp_instance_data omap3_vp1_data;
+extern struct omap_vp_instance_data omap3_vp2_data;
+
+extern struct omap_vp_instance_data omap4_vp_mpu_data;
+extern struct omap_vp_instance_data omap4_vp_iva_data;
+extern struct omap_vp_instance_data omap4_vp_core_data;
+
+#endif
diff --git a/arch/arm/mach-omap2/vp3xxx_data.c b/arch/arm/mach-omap2/vp3xxx_data.c
new file mode 100644
index 0000000..6452170
--- /dev/null
+++ b/arch/arm/mach-omap2/vp3xxx_data.c
@@ -0,0 +1,82 @@
+/*
+ * OMAP3 Voltage Processor (VP) data
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <plat/common.h>
+
+#include "prm-regbits-34xx.h"
+#include "voltage.h"
+
+#include "vp.h"
+
+/*
+ * VP data common to 34xx/36xx chips
+ * XXX This stuff presumably belongs in the vp3xxx.c or vp.c file.
+ */
+static const struct omap_vp_common_data omap3_vp_common = {
+ .vpconfig_erroroffset_shift = OMAP3430_ERROROFFSET_SHIFT,
+ .vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK,
+ .vpconfig_errorgain_shift = OMAP3430_ERRORGAIN_SHIFT,
+ .vpconfig_initvoltage_shift = OMAP3430_INITVOLTAGE_SHIFT,
+ .vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK,
+ .vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK,
+ .vpconfig_initvdd = OMAP3430_INITVDD_MASK,
+ .vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK,
+ .vpconfig_vpenable = OMAP3430_VPENABLE_MASK,
+ .vstepmin_smpswaittimemin_shift = OMAP3430_SMPSWAITTIMEMIN_SHIFT,
+ .vstepmax_smpswaittimemax_shift = OMAP3430_SMPSWAITTIMEMAX_SHIFT,
+ .vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT,
+ .vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT,
+ .vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT,
+ .vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT,
+ .vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT,
+};
+
+static const struct omap_vp_prm_irqst_data omap3_vp1_prm_irqst_data = {
+ .prm_irqst_reg = OMAP3_PRM_IRQSTATUS_MPU_OFFSET,
+ .tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK,
+};
+
+struct omap_vp_instance_data omap3_vp1_data = {
+ .vp_common = &omap3_vp_common,
+ .vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET,
+ .vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET,
+ .vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET,
+ .vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET,
+ .vstatus = OMAP3_PRM_VP1_STATUS_OFFSET,
+ .voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET,
+ .prm_irqst_data = &omap3_vp1_prm_irqst_data,
+};
+
+static const struct omap_vp_prm_irqst_data omap3_vp2_prm_irqst_data = {
+ .prm_irqst_reg = OMAP3_PRM_IRQSTATUS_MPU_OFFSET,
+ .tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK,
+};
+
+struct omap_vp_instance_data omap3_vp2_data = {
+ .vp_common = &omap3_vp_common,
+ .vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET,
+ .vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET,
+ .vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET,
+ .vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET,
+ .vstatus = OMAP3_PRM_VP2_STATUS_OFFSET,
+ .voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET,
+ .prm_irqst_data = &omap3_vp2_prm_irqst_data,
+};
diff --git a/arch/arm/mach-omap2/vp44xx_data.c b/arch/arm/mach-omap2/vp44xx_data.c
new file mode 100644
index 0000000..7b26f75
--- /dev/null
+++ b/arch/arm/mach-omap2/vp44xx_data.c
@@ -0,0 +1,97 @@
+/*
+ * OMAP3 Voltage Processor (VP) data
+ *
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * This program 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.
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/init.h>
+
+#include <plat/common.h>
+
+#include "prm44xx.h"
+#include "prm-regbits-44xx.h"
+#include "voltage.h"
+
+#include "vp.h"
+
+/*
+ * VP data common to 44xx chips
+ * XXX This stuff presumably belongs in the vp44xx.c or vp.c file.
+ */
+static const struct omap_vp_common_data omap4_vp_data = {
+ .vpconfig_erroroffset_shift = OMAP4430_ERROROFFSET_SHIFT,
+ .vpconfig_errorgain_mask = OMAP4430_ERRORGAIN_MASK,
+ .vpconfig_errorgain_shift = OMAP4430_ERRORGAIN_SHIFT,
+ .vpconfig_initvoltage_shift = OMAP4430_INITVOLTAGE_SHIFT,
+ .vpconfig_initvoltage_mask = OMAP4430_INITVOLTAGE_MASK,
+ .vpconfig_timeouten = OMAP4430_TIMEOUTEN_MASK,
+ .vpconfig_initvdd = OMAP4430_INITVDD_MASK,
+ .vpconfig_forceupdate = OMAP4430_FORCEUPDATE_MASK,
+ .vpconfig_vpenable = OMAP4430_VPENABLE_MASK,
+ .vstepmin_smpswaittimemin_shift = OMAP4430_SMPSWAITTIMEMIN_SHIFT,
+ .vstepmax_smpswaittimemax_shift = OMAP4430_SMPSWAITTIMEMAX_SHIFT,
+ .vstepmin_stepmin_shift = OMAP4430_VSTEPMIN_SHIFT,
+ .vstepmax_stepmax_shift = OMAP4430_VSTEPMAX_SHIFT,
+ .vlimitto_vddmin_shift = OMAP4430_VDDMIN_SHIFT,
+ .vlimitto_vddmax_shift = OMAP4430_VDDMAX_SHIFT,
+ .vlimitto_timeout_shift = OMAP4430_TIMEOUT_SHIFT,
+};
+
+static const struct omap_vp_prm_irqst_data omap4_vp_mpu_prm_irqst_data = {
+ .prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET,
+ .tranxdone_status = OMAP4430_VP_MPU_TRANXDONE_ST_MASK,
+};
+
+struct omap_vp_instance_data omap4_vp_mpu_data = {
+ .vpconfig = OMAP4_PRM_VP_MPU_CONFIG_OFFSET,
+ .vstepmin = OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET,
+ .vstepmax = OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET,
+ .vlimitto = OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET,
+ .vstatus = OMAP4_PRM_VP_MPU_STATUS_OFFSET,
+ .voltage = OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET,
+ .prm_irqst_data = &omap4_vp_mpu_prm_irqst_data,
+};
+
+static const struct omap_vp_prm_irqst_data omap4_vp_iva_prm_irqst_data = {
+ .prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
+ .tranxdone_status = OMAP4430_VP_IVA_TRANXDONE_ST_MASK,
+};
+
+struct omap_vp_instance_data omap4_vp_iva_data = {
+ .vpconfig = OMAP4_PRM_VP_IVA_CONFIG_OFFSET,
+ .vstepmin = OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET,
+ .vstepmax = OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET,
+ .vlimitto = OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET,
+ .vstatus = OMAP4_PRM_VP_IVA_STATUS_OFFSET,
+ .voltage = OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET,
+ .prm_irqst_data = &omap4_vp_iva_prm_irqst_data,
+};
+
+static const struct omap_vp_prm_irqst_data omap4_vp_core_prm_irqst_data = {
+ .prm_irqst_reg = OMAP4_PRM_IRQSTATUS_MPU_OFFSET,
+ .tranxdone_status = OMAP4430_VP_CORE_TRANXDONE_ST_MASK,
+};
+
+struct omap_vp_instance_data omap4_vp_core_data = {
+ .vpconfig = OMAP4_PRM_VP_CORE_CONFIG_OFFSET,
+ .vstepmin = OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET,
+ .vstepmax = OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET,
+ .vlimitto = OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET,
+ .vstatus = OMAP4_PRM_VP_CORE_STATUS_OFFSET,
+ .voltage = OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET,
+ .prm_irqst_data = &omap4_vp_core_prm_irqst_data,
+};
+
^ permalink raw reply related
* [PATCH 2/3] OMAP: voltage: move plat/voltage.h to mach-omap2/voltage.h
From: Paul Walmsley @ 2011-02-21 2:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110221020231.7598.14024.stgit@twilight.localdomain>
At this point in time, there's no reason for this header file to be in
plat-omap/include/plat/voltage.h. It should not be included by device
drivers, and the code that uses it is currently all under mach-omap2/.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
arch/arm/mach-omap2/omap_twl.c | 2 +-
arch/arm/mach-omap2/pm.c | 2 +-
arch/arm/mach-omap2/smartreflex.h | 3 ++-
arch/arm/mach-omap2/sr_device.c | 2 +-
arch/arm/mach-omap2/voltage.c | 3 ++-
arch/arm/mach-omap2/voltage.h | 0
arch/arm/plat-omap/include/plat/omap_hwmod.h | 1 -
7 files changed, 7 insertions(+), 6 deletions(-)
rename arch/arm/{plat-omap/include/plat/voltage.h => mach-omap2/voltage.h} (100%)
diff --git a/arch/arm/mach-omap2/omap_twl.c b/arch/arm/mach-omap2/omap_twl.c
index 00e1d2b..ad8c18a 100644
--- a/arch/arm/mach-omap2/omap_twl.c
+++ b/arch/arm/mach-omap2/omap_twl.c
@@ -18,7 +18,7 @@
#include <linux/kernel.h>
#include <linux/i2c/twl.h>
-#include <plat/voltage.h>
+#include "voltage.h"
#include "pm.h"
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index d5a102c..4baa674 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -18,8 +18,8 @@
#include <plat/omap-pm.h>
#include <plat/omap_device.h>
#include <plat/common.h>
-#include <plat/voltage.h>
+#include "voltage.h"
#include "powerdomain.h"
#include "clockdomain.h"
#include "pm.h"
diff --git a/arch/arm/mach-omap2/smartreflex.h b/arch/arm/mach-omap2/smartreflex.h
index 6568c88..5f35b9e 100644
--- a/arch/arm/mach-omap2/smartreflex.h
+++ b/arch/arm/mach-omap2/smartreflex.h
@@ -21,7 +21,8 @@
#define __ASM_ARM_OMAP_SMARTREFLEX_H
#include <linux/platform_device.h>
-#include <plat/voltage.h>
+
+#include "voltage.h"
/*
* Different Smartreflex IPs version. The v1 is the 65nm version used in
diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c
index a636604..10d3c5e 100644
--- a/arch/arm/mach-omap2/sr_device.c
+++ b/arch/arm/mach-omap2/sr_device.c
@@ -23,9 +23,9 @@
#include <linux/io.h>
#include <plat/omap_device.h>
-#include <plat/voltage.h>
#include "smartreflex.h"
+#include "voltage.h"
#include "control.h"
#include "pm.h"
diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c
index 12be525..3c9bcdc 100644
--- a/arch/arm/mach-omap2/voltage.c
+++ b/arch/arm/mach-omap2/voltage.c
@@ -26,7 +26,6 @@
#include <linux/slab.h>
#include <plat/common.h>
-#include <plat/voltage.h>
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
@@ -35,6 +34,8 @@
#include "prminst44xx.h"
#include "control.h"
+#include "voltage.h"
+
#define VP_IDLE_TIMEOUT 200
#define VP_TRANXDONE_TIMEOUT 300
#define VOLTAGE_DIR_SIZE 16
diff --git a/arch/arm/plat-omap/include/plat/voltage.h b/arch/arm/mach-omap2/voltage.h
similarity index 100%
rename from arch/arm/plat-omap/include/plat/voltage.h
rename to arch/arm/mach-omap2/voltage.h
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 1eee85a..93beae2 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -34,7 +34,6 @@
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <plat/cpu.h>
-#include <plat/voltage.h>
struct omap_device;
^ permalink raw reply related
* [PATCH 1/3] OMAP: smartreflex: move plat/smartreflex.h to mach-omap2/smartreflex.h
From: Paul Walmsley @ 2011-02-21 2:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110221020231.7598.14024.stgit@twilight.localdomain>
There's no reason for this header file to be in
plat-omap/include/plat/smartreflex.h. The hardware devices are in
OMAP2+ SoCs only. Leaving this header file in plat-omap causes
problems due to cross-dependencies with other header files that should
live in mach-omap2/.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 2 +-
arch/arm/mach-omap2/smartreflex-class3.c | 2 +-
arch/arm/mach-omap2/smartreflex.c | 2 +-
arch/arm/mach-omap2/smartreflex.h | 0
arch/arm/mach-omap2/sr_device.c | 2 +-
5 files changed, 4 insertions(+), 4 deletions(-)
rename arch/arm/{plat-omap/include/plat/smartreflex.h => mach-omap2/smartreflex.h} (100%)
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 8d81813..7f0b5e7 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -21,13 +21,13 @@
#include <plat/l4_3xxx.h>
#include <plat/i2c.h>
#include <plat/gpio.h>
-#include <plat/smartreflex.h>
#include "omap_hwmod_common_data.h"
#include "prm-regbits-34xx.h"
#include "cm-regbits-34xx.h"
#include "wd_timer.h"
+#include "smartreflex.h"
/*
* OMAP3xxx hardware module integration data
diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c
index 60e7055..f438cf4 100644
--- a/arch/arm/mach-omap2/smartreflex-class3.c
+++ b/arch/arm/mach-omap2/smartreflex-class3.c
@@ -11,7 +11,7 @@
* published by the Free Software Foundation.
*/
-#include <plat/smartreflex.h>
+#include "smartreflex.h"
static int sr_class3_enable(struct voltagedomain *voltdm)
{
diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
index c37e823..077dfb6 100644
--- a/arch/arm/mach-omap2/smartreflex.c
+++ b/arch/arm/mach-omap2/smartreflex.c
@@ -26,9 +26,9 @@
#include <linux/pm_runtime.h>
#include <plat/common.h>
-#include <plat/smartreflex.h>
#include "pm.h"
+#include "smartreflex.h"
#define SMARTREFLEX_NAME_LEN 16
#define NVALUE_NAME_LEN 40
diff --git a/arch/arm/plat-omap/include/plat/smartreflex.h b/arch/arm/mach-omap2/smartreflex.h
similarity index 100%
rename from arch/arm/plat-omap/include/plat/smartreflex.h
rename to arch/arm/mach-omap2/smartreflex.h
diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c
index b1e0af1..a636604 100644
--- a/arch/arm/mach-omap2/sr_device.c
+++ b/arch/arm/mach-omap2/sr_device.c
@@ -23,9 +23,9 @@
#include <linux/io.h>
#include <plat/omap_device.h>
-#include <plat/smartreflex.h>
#include <plat/voltage.h>
+#include "smartreflex.h"
#include "control.h"
#include "pm.h"
^ permalink raw reply related
* [PATCH 0/3] OMAP2+: voltage: first pass at cleanup/reorganization
From: Paul Walmsley @ 2011-02-21 2:08 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
this patch set does an initial round of cleanup on the OMAP "voltage" code
in arch/arm/mach-omap2/voltage.c. As part of this process, the header files
for the voltage code and SmartReflex are moved into arch/arm/mach-omap2, since
they are currently OMAP2+-specific.
There is still quite a bit of cleanup left to do; hopefully someone else will
get to it before I do. More details are in the last patch.
This series was built-tested for an OSK5912-specific config, omap1_defconfig,
a N800-specific config, omap2plus_defconfig, an OMAP3-specific config, and
an OMAP4-specific config. It was boot-tested on an OMAP35xx Beagleboard.
Further testing assistance is, of course, appreciated.
This series is available from git://git.pwsan.com/linux-2.6 in the
'voltage_split_2.6.39' branch.
- Paul
---
voltage_split_2.6.39
text data bss dec hex filename
5766071 488104 5596920 11851095 b4d557 vmlinux.omap2plus_defconfig.orig
5765631 487848 5596920 11850399 b4d29f vmlinux.omap2plus_defconfig
Paul Walmsley (3):
OMAP: smartreflex: move plat/smartreflex.h to mach-omap2/smartreflex.h
OMAP: voltage: move plat/voltage.h to mach-omap2/voltage.h
OMAP2+: voltage: reorganize, split code from data
arch/arm/mach-omap2/Makefile | 20
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 2
arch/arm/mach-omap2/omap_opp_data.h | 12
arch/arm/mach-omap2/omap_twl.c | 2
arch/arm/mach-omap2/opp3xxx_data.c | 44 +
arch/arm/mach-omap2/opp4xxx_data.c | 30 +
arch/arm/mach-omap2/pm.c | 2
arch/arm/mach-omap2/smartreflex-class3.c | 2
arch/arm/mach-omap2/smartreflex.c | 2
arch/arm/mach-omap2/smartreflex.h | 3
arch/arm/mach-omap2/sr_device.c | 4
arch/arm/mach-omap2/vc.h | 83 ++
arch/arm/mach-omap2/vc3xxx_data.c | 63 ++
arch/arm/mach-omap2/vc44xx_data.c | 72 ++
arch/arm/mach-omap2/voltage.c | 1019 +++++++------------------
arch/arm/mach-omap2/voltage.h | 66 ++
arch/arm/mach-omap2/voltagedomains3xxx_data.c | 104 +++
arch/arm/mach-omap2/voltagedomains44xx_data.c | 108 +++
arch/arm/mach-omap2/vp.h | 143 ++++
arch/arm/mach-omap2/vp3xxx_data.c | 82 ++
arch/arm/mach-omap2/vp44xx_data.c | 97 ++
arch/arm/plat-omap/include/plat/omap_hwmod.h | 1
22 files changed, 1203 insertions(+), 758 deletions(-)
rename arch/arm/{plat-omap/include/plat/smartreflex.h => mach-omap2/smartreflex.h} (99%)
create mode 100644 arch/arm/mach-omap2/vc.h
create mode 100644 arch/arm/mach-omap2/vc3xxx_data.c
create mode 100644 arch/arm/mach-omap2/vc44xx_data.c
rename arch/arm/{plat-omap/include/plat/voltage.h => mach-omap2/voltage.h} (68%)
create mode 100644 arch/arm/mach-omap2/voltagedomains3xxx_data.c
create mode 100644 arch/arm/mach-omap2/voltagedomains44xx_data.c
create mode 100644 arch/arm/mach-omap2/vp.h
create mode 100644 arch/arm/mach-omap2/vp3xxx_data.c
create mode 100644 arch/arm/mach-omap2/vp44xx_data.c
^ permalink raw reply
* [PATCH v2] i2c: tegra: Add i2c support
From: Colin Cross @ 2011-02-21 1:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTim0X=gUwYJmR+EAYjGamiJb7tgiVEjwqCEF0-L0@mail.gmail.com>
Adds I2C bus driver for nVidia Tegra SoCs. Tegra includes 4 I2C
controllers, one of which is inside the Dynamic Voltage Controller
and has a slightly different register map.
Signed-off-by: Colin Cross <ccross@android.com>
---
v2: fixes comments from Ben Dooks and Stephen Warren
drivers/i2c/busses/Kconfig | 7 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-tegra.c | 700 ++++++++++++++++++++++++++++++++++++++++
include/linux/i2c-tegra.h | 25 ++
4 files changed, 733 insertions(+), 0 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-tegra.c
create mode 100644 include/linux/i2c-tegra.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 113505a..a5f48dc 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -607,6 +607,13 @@ config I2C_STU300
This driver can also be built as a module. If so, the module
will be called i2c-stu300.
+config I2C_TEGRA
+ tristate "NVIDIA Tegra internal I2C controller"
+ depends on ARCH_TEGRA
+ help
+ If you say yes to this option, support will be included for the
+ I2C controller embedded in NVIDIA Tegra SOCs
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 9d2d0ec..a0d4afe 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
+obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
new file mode 100644
index 0000000..3921f66
--- /dev/null
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -0,0 +1,700 @@
+/*
+ * drivers/i2c/busses/i2c-tegra.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c-tegra.h>
+
+#include <asm/unaligned.h>
+
+#include <mach/clk.h>
+
+#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define BYTES_PER_FIFO_WORD 4
+
+#define I2C_CNFG 0x000
+#define I2C_CNFG_PACKET_MODE_EN (1<<10)
+#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
+#define I2C_SL_CNFG 0x020
+#define I2C_SL_CNFG_NEWSL (1<<2)
+#define I2C_SL_ADDR1 0x02c
+#define I2C_TX_FIFO 0x050
+#define I2C_RX_FIFO 0x054
+#define I2C_PACKET_TRANSFER_STATUS 0x058
+#define I2C_FIFO_CONTROL 0x05c
+#define I2C_FIFO_CONTROL_TX_FLUSH (1<<1)
+#define I2C_FIFO_CONTROL_RX_FLUSH (1<<0)
+#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5
+#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2
+#define I2C_FIFO_STATUS 0x060
+#define I2C_FIFO_STATUS_TX_MASK 0xF0
+#define I2C_FIFO_STATUS_TX_SHIFT 4
+#define I2C_FIFO_STATUS_RX_MASK 0x0F
+#define I2C_FIFO_STATUS_RX_SHIFT 0
+#define I2C_INT_MASK 0x064
+#define I2C_INT_STATUS 0x068
+#define I2C_INT_PACKET_XFER_COMPLETE (1<<7)
+#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1<<6)
+#define I2C_INT_TX_FIFO_OVERFLOW (1<<5)
+#define I2C_INT_RX_FIFO_UNDERFLOW (1<<4)
+#define I2C_INT_NO_ACK (1<<3)
+#define I2C_INT_ARBITRATION_LOST (1<<2)
+#define I2C_INT_TX_FIFO_DATA_REQ (1<<1)
+#define I2C_INT_RX_FIFO_DATA_REQ (1<<0)
+#define I2C_CLK_DIVISOR 0x06c
+
+#define DVC_CTRL_REG1 0x000
+#define DVC_CTRL_REG1_INTR_EN (1<<10)
+#define DVC_CTRL_REG2 0x004
+#define DVC_CTRL_REG3 0x008
+#define DVC_CTRL_REG3_SW_PROG (1<<26)
+#define DVC_CTRL_REG3_I2C_DONE_INTR_EN (1<<30)
+#define DVC_STATUS 0x00c
+#define DVC_STATUS_I2C_DONE_INTR (1<<30)
+
+#define I2C_ERR_NONE 0x00
+#define I2C_ERR_NO_ACK 0x01
+#define I2C_ERR_ARBITRATION_LOST 0x02
+
+#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
+#define PACKET_HEADER0_PACKET_ID_SHIFT 16
+#define PACKET_HEADER0_CONT_ID_SHIFT 12
+#define PACKET_HEADER0_PROTOCOL_I2C (1<<4)
+
+#define I2C_HEADER_HIGHSPEED_MODE (1<<22)
+#define I2C_HEADER_CONT_ON_NAK (1<<21)
+#define I2C_HEADER_SEND_START_BYTE (1<<20)
+#define I2C_HEADER_READ (1<<19)
+#define I2C_HEADER_10BIT_ADDR (1<<18)
+#define I2C_HEADER_IE_ENABLE (1<<17)
+#define I2C_HEADER_REPEAT_START (1<<16)
+#define I2C_HEADER_MASTER_ADDR_SHIFT 12
+#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
+
+/**
+ * struct tegra_i2c_dev - per device i2c context
+ * @dev: device reference for power management
+ * @adapter: core i2c layer adapter information
+ * @clk: clock reference for i2c controller
+ * @i2c_clk: clock reference for i2c bus
+ * @iomem: memory resource for registers
+ * @base: ioremapped registers cookie
+ * @cont_id: i2c controller id, used for for packet header
+ * @irq: irq number of transfer complete interrupt
+ * @is_dvc: identifies the DVC i2c controller, has a different register layout
+ * @msg_complete: transfer completion notifier
+ * @msg_err: error code for completed message
+ * @msg_buf: pointer to current message data
+ * @msg_buf_remaining: size of unsent data in the message buffer
+ * @msg_read: identifies read transfers
+ * @bus_clk_rate: current i2c bus clock rate
+ * @is_suspended: prevents i2c controller accesses after suspend is called
+ */
+struct tegra_i2c_dev {
+ struct device *dev;
+ struct i2c_adapter adapter;
+ struct clk *clk;
+ struct clk *i2c_clk;
+ struct resource *iomem;
+ void __iomem *base;
+ int cont_id;
+ int irq;
+ int is_dvc;
+ struct completion msg_complete;
+ int msg_err;
+ u8 *msg_buf;
+ size_t msg_buf_remaining;
+ int msg_read;
+ unsigned long bus_clk_rate;
+ bool is_suspended;
+};
+
+static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
+{
+ writel(val, i2c_dev->base + reg);
+}
+
+static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
+{
+ return readl(i2c_dev->base + reg);
+}
+
+/*
+ * i2c_writel and i2c_readl will offset the register if necessary to talk
+ * to the I2C block inside the DVC block
+ */
+static unsigned long tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev,
+ unsigned long reg)
+{
+ if (i2c_dev->is_dvc)
+ reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40;
+ return reg;
+}
+
+static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
+ unsigned long reg)
+{
+ writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+}
+
+static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
+{
+ return readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+}
+
+static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data,
+ unsigned long reg, int len)
+{
+ writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
+}
+
+static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
+ unsigned long reg, int len)
+{
+ readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
+}
+
+static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
+ int_mask &= ~mask;
+ i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
+}
+
+static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK);
+ int_mask |= mask;
+ i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
+}
+
+static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
+{
+ unsigned long timeout = jiffies + HZ;
+ u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL);
+ val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH;
+ i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+
+ while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) &
+ (I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+ return 0;
+}
+
+static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val;
+ int rx_fifo_avail;
+ u8 *buf = i2c_dev->msg_buf;
+ size_t buf_remaining = i2c_dev->msg_buf_remaining;
+ int words_to_transfer;
+
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
+ I2C_FIFO_STATUS_RX_SHIFT;
+
+ /* Rounds down to not include partial word at the end of buf */
+ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
+ if (words_to_transfer > rx_fifo_avail)
+ words_to_transfer = rx_fifo_avail;
+
+ i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer);
+
+ buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
+ rx_fifo_avail -= words_to_transfer;
+
+ /*
+ * If there is a partial word at the end of buf, handle it manually to
+ * prevent overwriting past the end of buf
+ */
+ if (rx_fifo_avail > 0 && buf_remaining > 0) {
+ BUG_ON(buf_remaining > 3);
+ val = i2c_readl(i2c_dev, I2C_RX_FIFO);
+ memcpy(buf, &val, buf_remaining);
+ buf_remaining = 0;
+ rx_fifo_avail--;
+ }
+
+ BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf;
+ return 0;
+}
+
+static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val;
+ int tx_fifo_avail;
+ u8 *buf = i2c_dev->msg_buf;
+ size_t buf_remaining = i2c_dev->msg_buf_remaining;
+ int words_to_transfer;
+
+ val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
+ tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
+ I2C_FIFO_STATUS_TX_SHIFT;
+
+ /* Rounds down to not include partial word at the end of buf */
+ words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
+ if (words_to_transfer > tx_fifo_avail)
+ words_to_transfer = tx_fifo_avail;
+
+ i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+
+ buf += words_to_transfer * BYTES_PER_FIFO_WORD;
+ buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
+ tx_fifo_avail -= words_to_transfer;
+
+ /*
+ * If there is a partial word at the end of buf, handle it manually to
+ * prevent reading past the end of buf, which could cross a page
+ * boundary and fault.
+ */
+ if (tx_fifo_avail > 0 && buf_remaining > 0) {
+ BUG_ON(buf_remaining > 3);
+ memcpy(&val, buf, buf_remaining);
+ i2c_writel(i2c_dev, val, I2C_TX_FIFO);
+ buf_remaining = 0;
+ tx_fifo_avail--;
+ }
+
+ BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0);
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf;
+ return 0;
+}
+
+/*
+ * One of the Tegra I2C blocks is inside the DVC (Digital Voltage Controller)
+ * block. This block is identical to the rest of the I2C blocks, except that
+ * it only supports master mode, it has registers moved around, and it needs
+ * some extra init to get it into I2C mode. The register moves are handled
+ * by i2c_readl and i2c_writel
+ */
+static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val = 0;
+ val = dvc_readl(i2c_dev, DVC_CTRL_REG3);
+ val |= DVC_CTRL_REG3_SW_PROG;
+ val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN;
+ dvc_writel(i2c_dev, val, DVC_CTRL_REG3);
+
+ val = dvc_readl(i2c_dev, DVC_CTRL_REG1);
+ val |= DVC_CTRL_REG1_INTR_EN;
+ dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
+}
+
+static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
+{
+ u32 val;
+ int err = 0;
+
+ clk_enable(i2c_dev->clk);
+
+ tegra_periph_reset_assert(i2c_dev->clk);
+ udelay(2);
+ tegra_periph_reset_deassert(i2c_dev->clk);
+
+ if (i2c_dev->is_dvc)
+ tegra_dvc_init(i2c_dev);
+
+ val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN;
+ i2c_writel(i2c_dev, val, I2C_CNFG);
+ i2c_writel(i2c_dev, 0, I2C_INT_MASK);
+ clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
+
+ val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
+ 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
+ i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
+
+ if (tegra_i2c_flush_fifos(i2c_dev))
+ err = -ETIMEDOUT;
+
+ clk_disable(i2c_dev->clk);
+ return err;
+}
+
+static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
+{
+ u32 status;
+ const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ struct tegra_i2c_dev *i2c_dev = dev_id;
+
+ status = i2c_readl(i2c_dev, I2C_INT_STATUS);
+
+ if (status == 0) {
+ dev_warn(i2c_dev->dev, "interrupt with no status\n");
+ return IRQ_NONE;
+ }
+
+ if (unlikely(status & status_err)) {
+ if (status & I2C_INT_NO_ACK)
+ i2c_dev->msg_err |= I2C_ERR_NO_ACK;
+ if (status & I2C_INT_ARBITRATION_LOST)
+ i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST;
+ complete(&i2c_dev->msg_complete);
+ goto err;
+ }
+
+ if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) {
+ if (i2c_dev->msg_buf_remaining)
+ tegra_i2c_empty_rx_fifo(i2c_dev);
+ else
+ BUG();
+ }
+
+ if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) {
+ if (i2c_dev->msg_buf_remaining)
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+ else
+ tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
+ }
+
+ if ((status & I2C_INT_PACKET_XFER_COMPLETE) &&
+ !i2c_dev->msg_buf_remaining)
+ complete(&i2c_dev->msg_complete);
+
+ i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+ if (i2c_dev->is_dvc)
+ dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ return IRQ_HANDLED;
+err:
+ /* An error occured, mask all interrupts */
+ tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
+ I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
+ I2C_INT_RX_FIFO_DATA_REQ);
+ i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+ return IRQ_HANDLED;
+}
+
+static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
+ struct i2c_msg *msg, int stop)
+{
+ u32 packet_header;
+ u32 int_mask;
+ int ret;
+
+ tegra_i2c_flush_fifos(i2c_dev);
+ i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS);
+
+ if (msg->len == 0)
+ return -EINVAL;
+
+ i2c_dev->msg_buf = msg->buf;
+ i2c_dev->msg_buf_remaining = msg->len;
+ i2c_dev->msg_err = I2C_ERR_NONE;
+ i2c_dev->msg_read = (msg->flags & I2C_M_RD);
+ INIT_COMPLETION(i2c_dev->msg_complete);
+
+ packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
+ PACKET_HEADER0_PROTOCOL_I2C |
+ (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) |
+ (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+ packet_header = msg->len - 1;
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+ packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
+ packet_header |= I2C_HEADER_IE_ENABLE;
+ if (msg->flags & I2C_M_TEN)
+ packet_header |= I2C_HEADER_10BIT_ADDR;
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ packet_header |= I2C_HEADER_CONT_ON_NAK;
+ if (msg->flags & I2C_M_NOSTART)
+ packet_header |= I2C_HEADER_REPEAT_START;
+ if (msg->flags & I2C_M_RD)
+ packet_header |= I2C_HEADER_READ;
+ i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
+
+ if (!(msg->flags & I2C_M_RD))
+ tegra_i2c_fill_tx_fifo(i2c_dev);
+
+ int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ if (msg->flags & I2C_M_RD)
+ int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
+ else if (i2c_dev->msg_buf_remaining)
+ int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
+ tegra_i2c_unmask_irq(i2c_dev, int_mask);
+ dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
+ i2c_readl(i2c_dev, I2C_INT_MASK));
+
+ ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT);
+ tegra_i2c_mask_irq(i2c_dev, int_mask);
+
+ if (WARN_ON(ret == 0)) {
+ dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+
+ tegra_i2c_init(i2c_dev);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(i2c_dev->dev, "transfer complete: %d %d %d\n",
+ ret, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err);
+
+ if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
+ return 0;
+
+ tegra_i2c_init(i2c_dev);
+ if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ return 0;
+ return -EREMOTEIO;
+ }
+
+ return -EIO;
+}
+
+static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int i;
+ int ret = 0;
+
+ if (i2c_dev->is_suspended)
+ return -EBUSY;
+
+ clk_enable(i2c_dev->clk);
+ for (i = 0; i < num; i++) {
+ int stop = (i == (num - 1)) ? 1 : 0;
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop);
+ if (ret)
+ break;
+ }
+ clk_disable(i2c_dev->clk);
+ return ret ?: i;
+}
+
+static u32 tegra_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm tegra_i2c_algo = {
+ .master_xfer = tegra_i2c_xfer,
+ .functionality = tegra_i2c_func,
+};
+
+static int tegra_i2c_probe(struct platform_device *pdev)
+{
+ struct tegra_i2c_dev *i2c_dev;
+ struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct resource *iomem;
+ struct clk *clk;
+ struct clk *i2c_clk;
+ void *base;
+ int irq;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no mem resource\n");
+ return -EINVAL;
+ }
+ iomem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!iomem) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ base = ioremap(iomem->start, resource_size(iomem));
+ if (!base) {
+ dev_err(&pdev->dev, "Cannot ioremap I2C region\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no irq resource\n");
+ ret = -EINVAL;
+ goto err_iounmap;
+ }
+ irq = res->start;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "missing controller clock");
+ ret = PTR_ERR(clk);
+ goto err_release_region;
+ }
+
+ i2c_clk = clk_get(&pdev->dev, "i2c");
+ if (IS_ERR(i2c_clk)) {
+ dev_err(&pdev->dev, "missing bus clock");
+ ret = PTR_ERR(i2c_clk);
+ goto err_clk_put;
+ }
+
+ i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
+ if (!i2c_dev) {
+ ret = -ENOMEM;
+ goto err_i2c_clk_put;
+ }
+
+ i2c_dev->base = base;
+ i2c_dev->clk = clk;
+ i2c_dev->i2c_clk = i2c_clk;
+ i2c_dev->iomem = iomem;
+ i2c_dev->adapter.algo = &tegra_i2c_algo;
+ i2c_dev->irq = irq;
+ i2c_dev->cont_id = pdev->id;
+ i2c_dev->dev = &pdev->dev;
+ i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000;
+
+ if (pdev->id == 3)
+ i2c_dev->is_dvc = 1;
+ init_completion(&i2c_dev->msg_complete);
+
+ platform_set_drvdata(pdev, i2c_dev);
+
+ ret = tegra_i2c_init(i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize i2c controller");
+ goto err_free;
+ }
+
+ ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
+ goto err_free;
+ }
+
+ clk_enable(i2c_dev->i2c_clk);
+
+ i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
+ i2c_dev->adapter.owner = THIS_MODULE;
+ i2c_dev->adapter.class = I2C_CLASS_HWMON;
+ strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
+ sizeof(i2c_dev->adapter.name));
+ i2c_dev->adapter.algo = &tegra_i2c_algo;
+ i2c_dev->adapter.dev.parent = &pdev->dev;
+ i2c_dev->adapter.nr = pdev->id;
+
+ ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add I2C adapter\n");
+ goto err_free_irq;
+ }
+
+ return 0;
+err_free_irq:
+ free_irq(i2c_dev->irq, i2c_dev);
+err_free:
+ kfree(i2c_dev);
+err_i2c_clk_put:
+ clk_put(i2c_clk);
+err_clk_put:
+ clk_put(clk);
+err_release_region:
+ release_mem_region(iomem->start, resource_size(iomem));
+err_iounmap:
+ iounmap(base);
+ return ret;
+}
+
+static int tegra_i2c_remove(struct platform_device *pdev)
+{
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ i2c_del_adapter(&i2c_dev->adapter);
+ free_irq(i2c_dev->irq, i2c_dev);
+ clk_put(i2c_dev->i2c_clk);
+ clk_put(i2c_dev->clk);
+ release_mem_region(i2c_dev->iomem->start,
+ resource_size(i2c_dev->iomem));
+ iounmap(i2c_dev->base);
+ kfree(i2c_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_lock_adapter(&i2c_dev->adapter);
+ i2c_dev->is_suspended = true;
+ i2c_unlock_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+
+static int tegra_i2c_resume(struct platform_device *pdev)
+{
+ struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ i2c_lock_adapter(&i2c_dev->adapter);
+
+ ret = tegra_i2c_init(i2c_dev);
+
+ if (ret) {
+ i2c_unlock_adapter(&i2c_dev->adapter);
+ return ret;
+ }
+
+ i2c_dev->is_suspended = false;
+
+ i2c_unlock_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver tegra_i2c_driver = {
+ .probe = tegra_i2c_probe,
+ .remove = tegra_i2c_remove,
+#ifdef CONFIG_PM
+ .suspend = tegra_i2c_suspend,
+ .resume = tegra_i2c_resume,
+#endif
+ .driver = {
+ .name = "tegra-i2c",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init tegra_i2c_init_driver(void)
+{
+ return platform_driver_register(&tegra_i2c_driver);
+}
+
+static void __exit tegra_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&tegra_i2c_driver);
+}
+
+subsys_initcall(tegra_i2c_init_driver);
+module_exit(tegra_i2c_exit_driver);
+
+MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver");
+MODULE_AUTHOR("Colin Cross");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/i2c-tegra.h b/include/linux/i2c-tegra.h
new file mode 100644
index 0000000..9c85da4
--- /dev/null
+++ b/include/linux/i2c-tegra.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/i2c/busses/i2c-tegra.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_I2C_TEGRA_H
+#define _LINUX_I2C_TEGRA_H
+
+struct tegra_i2c_platform_data {
+ unsigned long bus_clk_rate;
+};
+
+#endif /* _LINUX_I2C_TEGRA_H */
--
1.7.3.1
^ permalink raw reply related
* [PATCH v2 05/21] ARM: tegra: clock: Disable clocks left on by bootloader
From: Olof Johansson @ 2011-02-21 0:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298154371-5641-7-git-send-email-ccross@android.com>
Hi,
On Sat, Feb 19, 2011 at 2:25 PM, Colin Cross <ccross@android.com> wrote:
> Adds CONFIG_TEGRA_DISABLE_BOOTLOADER_CLOCKS that iterates
> through all clocks, disabling any for which the refcount
> is 0 but the clock init detected the bootloader left the
> clock on.
I would argue that any kernel functionality that relies on clocks
being left on my bootloader is buggy, and/or should at least have
those clocks enabled in the static clock tables for the boards in
question, since it creates an undocumented dependency on firmware.
I would prefer if it disabled the clocks by default (after warning),
but that there was a runtime way to override while debugging (i.e. add
a bootarg 'keep_fwclocks' or similar to keep the firmware-enabled
clocks running and just warn about them).
-Olof
^ permalink raw reply
* [PATCH V4 3/4] ARM: Xilinx: base header files and assembly macros
From: John Linn @ 2011-02-21 0:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102202237.47327.arnd@arndb.de>
> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd at arndb.de]
> Sent: Sunday, February 20, 2011 2:38 PM
> To: John Linn
> Cc: linux-arm-kernel at lists.infradead.org;
linux-kernel at vger.kernel.org;
> linux at arm.linux.org.uk; catalin.marinas at arm.com; glikely at secretlab.ca;
> jamie at jamieiles.com
> Subject: Re: [PATCH V4 3/4] ARM: Xilinx: base header files and
assembly
> macros
>
> On Friday 18 February 2011, John Linn wrote:
> > +
> > +/* IO address mapping macros, nothing special at this time but
> required */
> > +
> > +#ifdef __ASSEMBLER__
> > +#define IOMEM(x) (x)
> > +#else
> > +#define IOMEM(x) ((void __force __iomem *)(x))
> > +#endif
> > +
> > +#define __io(a) __typesafe_io(a)
> > +#define __mem_pci(a) (a)
>
> Are you planning to support PCI or PCMCIA? If so, the __io definition
> will have to
> change so it points to the PIO register window.
No immediate plans for PCI or PCMCIA. Makes sense.
>
> > diff --git a/arch/arm/mach-xilinx/include/mach/timex.h
> b/arch/arm/mach-xilinx/include/mach/timex.h
> > new file mode 100644
> > index 0000000..4ebc0a6
> > --- /dev/null
> > +++ b/arch/arm/mach-xilinx/include/mach/timex.h
> > +#ifndef __MACH_TIMEX_H__
> > +#define __MACH_TIMEX_H__
> > +
> > +#define PERIPHERAL_CLOCK_RATE 2500000
> > +
> > +#define CLOCK_TICK_RATE (PERIPHERAL_CLOCK_RATE / 32)
> > +
> > +#endif
>
> I thought we were at the point where CLOCK_TICK_RATE is no longer
used.
The timer code in these patches is using it, no other comments on it so
far.
> Did the patches not make it in yet?
>
I haven't heard they made them in yet, just waiting and hoping for an
ack.
> The rest looks fine to me.
Great, appreciate the review and your time.
-- John
>
> Arnd
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
^ permalink raw reply
* [PATCH] arm: imx: Zero entire imxdma structure
From: Russell King - ARM Linux @ 2011-02-20 23:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTin4gJpzV6-bo15rn_AHVGvTD8+9vq3Q+ciLF8vB@mail.gmail.com>
On Sun, Feb 20, 2011 at 10:59:24PM +0000, Ilia Mirkin wrote:
> On Sun, Feb 20, 2011 at 10:10 PM, Ilia Mirkin <imirkin@alum.mit.edu> wrote:
> > The semantic match that finds the problem:
> > // <smpl>
> > @@
> > type T;
> > identifier x;
> > @@
> >
> > T *x;
> > ...
> > * memset(x, ..., ... * sizeof(x) * ...);
> > // </smpl>
> >
> > Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
> >
> > ---
> > ?arch/arm/mach-imx/dma-v1.c | ? ?2 +-
> > ?1 files changed, 1 insertions(+), 1 deletions(-)
> >
> > Untested, lack of tool chain and hardware. But there's definitely something
> > odd going on here, and this seemed like the most logical fix.
>
> Doing some quick digging (as I've been discovering that a few of the
> other patches I was planning on sending have already been sent out in
> the past), I noticed that this same patch was already sent on Dec 9,
> 2009 by Julia Lawall[1] (back when it was plat-mxc/dma-mx1-mx2.c), and
> then again by Vasiliy Kulikov[2] on Oct 10, 2010.
>
> On each occasion, the response was positive, but patch has yet to make
> it upstream. Weird.
Sascha, are you going to take this patch?
^ permalink raw reply
* [PATCH] arm: imx: Zero entire imxdma structure
From: Ilia Mirkin @ 2011-02-20 22:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298239851-958-1-git-send-email-imirkin@alum.mit.edu>
On Sun, Feb 20, 2011 at 10:10 PM, Ilia Mirkin <imirkin@alum.mit.edu> wrote:
> The semantic match that finds the problem:
> // <smpl>
> @@
> type T;
> identifier x;
> @@
>
> T *x;
> ...
> * memset(x, ..., ... * sizeof(x) * ...);
> // </smpl>
>
> Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
>
> ---
> ?arch/arm/mach-imx/dma-v1.c | ? ?2 +-
> ?1 files changed, 1 insertions(+), 1 deletions(-)
>
> Untested, lack of tool chain and hardware. But there's definitely something
> odd going on here, and this seemed like the most logical fix.
Doing some quick digging (as I've been discovering that a few of the
other patches I was planning on sending have already been sent out in
the past), I noticed that this same patch was already sent on Dec 9,
2009 by Julia Lawall[1] (back when it was plat-mxc/dma-mx1-mx2.c), and
then again by Vasiliy Kulikov[2] on Oct 10, 2010.
On each occasion, the response was positive, but patch has yet to make
it upstream. Weird.
--
Ilia Mirkin
imirkin at alum.mit.edu
[1] Message-ID: <Pine.LNX.4.64.0912092022040.1870@ask.diku.dk>
[2] Message-ID: <1286731703-17775-1-git-send-email-segooon@gmail.com>
^ permalink raw reply
* [PATCH] arm: imx: Zero entire imxdma structure
From: Ilia Mirkin @ 2011-02-20 22:10 UTC (permalink / raw)
To: linux-arm-kernel
The semantic match that finds the problem:
// <smpl>
@@
type T;
identifier x;
@@
T *x;
...
* memset(x, ..., ... * sizeof(x) * ...);
// </smpl>
Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
---
arch/arm/mach-imx/dma-v1.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
Untested, lack of tool chain and hardware. But there's definitely something
odd going on here, and this seemed like the most logical fix.
diff --git a/arch/arm/mach-imx/dma-v1.c b/arch/arm/mach-imx/dma-v1.c
index e9f1769..236f149 100644
--- a/arch/arm/mach-imx/dma-v1.c
+++ b/arch/arm/mach-imx/dma-v1.c
@@ -699,7 +699,7 @@ int imx_dma_request(int channel, const char *name)
local_irq_restore(flags);
return -EBUSY;
}
- memset(imxdma, 0, sizeof(imxdma));
+ memset(imxdma, 0, sizeof(*imxdma));
imxdma->name = name;
local_irq_restore(flags); /* request_irq() can block */
--
1.7.3.4
^ permalink raw reply related
* [PATCH 2/2] DM9000B: Fix PHY power for network down/up
From: Henry Nestler @ 2011-02-20 21:45 UTC (permalink / raw)
To: linux-arm-kernel
DM9000 revision B needs 1 ms delay after PHY power on (see spec), and PHY
power must on in register DM9000_GPR before all other settings will change.
Remember, that register DM9000_GPR was not changed by reset sequence.
Without these fix the FIFO goes out of sync and sends wrong data after
sequence of "ifconfig ethX down ; sleep 3 ; ifconfig ethX up".
---
Kernel version 2.6.38-rc5
drivers/net/dm9000.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 2d4c4fc..5925569 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -802,10 +802,7 @@ dm9000_init_dm9000(struct net_device *dev)
/* Checksum mode */
dm9000_set_rx_csum_unlocked(dev, db->rx_csum);
- /* GPIO0 on pre-activate PHY */
- iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
- iow(db, DM9000_GPR, 0); /* Enable PHY */
ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
@@ -1194,6 +1191,10 @@ dm9000_open(struct net_device *dev)
if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
return -EAGAIN;
+ /* GPIO0 on pre-activate PHY, Reg 1F is not set by reset */
+ iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
+ udelay(1000); /* delay needs by DM9000B */
+
/* Initialize DM9000 board */
dm9000_reset(db);
dm9000_init_dm9000(dev);
^ permalink raw reply related
* [RFC] orion5x: TS-78XX support for FPGA generated doorbell IRQ's
From: Nicolas Pitre @ 2011-02-20 21:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110216224522.GK20988@chipmunk>
On Wed, 16 Feb 2011, Alexander Clouter wrote:
> Hi,
>
> The TS-7800 board[1] FPGA can generate interrupts for devices connected
> to the PC/104 header. This works by triggering the doorbell IRQ, which
> seems typically to not be used on other orion5x boards seen in the wild.
>
> The following patch bumps NR_IRQS from 64 to 96 for the TS-7800 and
> calls upon the rather nifty set_irq_chained_handler() framework (the
> vendor of the board in their tree do hairy things in assembler).
The set_irq_chained_handler() is certainly the way to go.
As to NR_IRQS, it is best to leave it unchanged and provide the number
of IRQs you require in the machine description record (see commit
354e6f72d6 for some details).
> The code has been tested with an Ethernet card driven off ax88796.c, but
> I am looking for feedback if this is the Right Way(tm) to do this, and
> if there is a better way.
>
> Bear in mind that fpga_addr could vary if people wish to put their own
> bitstream on the FPGA and generate IRQ's in a similar fashion; which is
> why there is a switch statement present[2] as the VHDL coder might want
> to use different addresses.
Looks sane to me.
> Cheers
>
> [1] http://www.embeddedarm.com/products/board-detail.php?product=TS-7800
> [2] I am planning at some stage to rework all the FPGA related bits to
> use either MFD or even the new ARM Device Tree framework
>
> ---
> arch/arm/mach-orion5x/addr-map.c | 2 -
> arch/arm/mach-orion5x/include/mach/irqs.h | 10 ++-
> arch/arm/mach-orion5x/include/mach/orion5x.h | 10 ++
> arch/arm/mach-orion5x/irq.c | 3 +-
> arch/arm/mach-orion5x/ts78xx-fpga.h | 2 +
> arch/arm/mach-orion5x/ts78xx-setup.c | 121 ++++++++++++++++++++++++++
> 6 files changed, 143 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/mach-orion5x/addr-map.c b/arch/arm/mach-orion5x/addr-map.c
> index 1a5d6a0..1fa730c 100644
> --- a/arch/arm/mach-orion5x/addr-map.c
> +++ b/arch/arm/mach-orion5x/addr-map.c
> @@ -60,14 +60,12 @@
> /*
> * Helpers to get DDR bank info
> */
> -#define ORION5X_DDR_REG(x) (ORION5X_DDR_VIRT_BASE | (x))
> #define DDR_BASE_CS(n) ORION5X_DDR_REG(0x1500 + ((n) << 3))
> #define DDR_SIZE_CS(n) ORION5X_DDR_REG(0x1504 + ((n) << 3))
>
> /*
> * CPU Address Decode Windows registers
> */
> -#define ORION5X_BRIDGE_REG(x) (ORION5X_BRIDGE_VIRT_BASE | (x))
> #define CPU_WIN_CTRL(n) ORION5X_BRIDGE_REG(0x000 | ((n) << 4))
> #define CPU_WIN_BASE(n) ORION5X_BRIDGE_REG(0x004 | ((n) << 4))
> #define CPU_WIN_REMAP_LO(n) ORION5X_BRIDGE_REG(0x008 | ((n) << 4))
> diff --git a/arch/arm/mach-orion5x/include/mach/irqs.h b/arch/arm/mach-orion5x/include/mach/irqs.h
> index a6fa9d8..677b594 100644
> --- a/arch/arm/mach-orion5x/include/mach/irqs.h
> +++ b/arch/arm/mach-orion5x/include/mach/irqs.h
> @@ -54,7 +54,13 @@
> #define IRQ_ORION5X_GPIO_START 32
> #define NR_GPIO_IRQS 32
>
> -#define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS)
> -
> +#ifdef CONFIG_MACH_TS78XX
> +# define NR_TS78XX_FPGA_IRQS 32
> +# define FPGA_IRQ(irq) (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS + irq)
> +#
> +# define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS + NR_TS78XX_FPGA_IRQS)
> +#else
> +# define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS)
> +#endif
>
> #endif
> diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h
> index 2d87665..5a1711b 100644
> --- a/arch/arm/mach-orion5x/include/mach/orion5x.h
> +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h
> @@ -69,6 +69,7 @@
> ******************************************************************************/
>
> #define ORION5X_DDR_VIRT_BASE (ORION5X_REGS_VIRT_BASE | 0x00000)
> +#define ORION5X_DDR_REG(x) (ORION5X_DDR_VIRT_BASE | (x))
>
> #define ORION5X_DEV_BUS_PHYS_BASE (ORION5X_REGS_PHYS_BASE | 0x10000)
> #define ORION5X_DEV_BUS_VIRT_BASE (ORION5X_REGS_VIRT_BASE | 0x10000)
> @@ -81,6 +82,7 @@
> #define UART1_VIRT_BASE (ORION5X_DEV_BUS_VIRT_BASE | 0x2100)
>
> #define ORION5X_BRIDGE_VIRT_BASE (ORION5X_REGS_VIRT_BASE | 0x20000)
> +#define ORION5X_BRIDGE_REG(x) (ORION5X_BRIDGE_VIRT_BASE | (x))
>
> #define ORION5X_PCI_VIRT_BASE (ORION5X_REGS_VIRT_BASE | 0x30000)
>
> @@ -120,6 +122,14 @@
> #define DEV_BUS_INT_MASK ORION5X_DEV_BUS_REG(0x4d4)
>
> /*******************************************************************************
> + * CPU Doorbell Registers
> + ******************************************************************************/
> +#define H2C_DOORBELL_REG ORION5X_BRIDGE_REG(0x400)
> +#define H2C_DOORBELL_MASK_REG ORION5X_BRIDGE_REG(0x404)
> +#define C2H_DOORBELL_REG ORION5X_BRIDGE_REG(0x408)
> +#define C2H_DOORBELL_MASK_REG ORION5X_BRIDGE_REG(0x40c)
> +
> +/*******************************************************************************
> * Supported Devices & Revisions
> ******************************************************************************/
> /* Orion-1 (88F5181) and Orion-VoIP (88F5181L) */
> diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c
> index d7512b9..d5ba5b4 100644
> --- a/arch/arm/mach-orion5x/irq.c
> +++ b/arch/arm/mach-orion5x/irq.c
> @@ -43,7 +43,8 @@ void __init orion5x_init_irq(void)
> * Register chained level handlers for GPIO IRQs by default.
> * User can use set_type() if he wants to use edge types handlers.
> */
> - for (i = IRQ_ORION5X_GPIO_START; i < NR_IRQS; i++) {
> + for (i = IRQ_ORION5X_GPIO_START;
> + i < IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS; i++) {
> set_irq_chip(i, &orion_gpio_irq_chip);
> set_irq_handler(i, handle_level_irq);
> irq_desc[i].status |= IRQ_LEVEL;
> diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-orion5x/ts78xx-fpga.h
> index 791f754..8bb0331 100644
> --- a/arch/arm/mach-orion5x/ts78xx-fpga.h
> +++ b/arch/arm/mach-orion5x/ts78xx-fpga.h
> @@ -26,6 +26,8 @@ struct fpga_device {
> };
>
> struct fpga_devices {
> + struct fpga_device doorbell_irq;
> +
> /* Technologic Systems */
> struct fpga_device ts_rtc;
> struct fpga_device ts_nand;
> diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c
> index d3e29f8..592773a 100644
> --- a/arch/arm/mach-orion5x/ts78xx-setup.c
> +++ b/arch/arm/mach-orion5x/ts78xx-setup.c
> @@ -22,6 +22,8 @@
> #include <asm/mach/arch.h>
> #include <asm/mach/map.h>
> #include <mach/orion5x.h>
> +#include <linux/irq.h>
> +#include <mach/bridge-regs.h>
> #include "common.h"
> #include "mpp.h"
> #include "ts78xx-fpga.h"
> @@ -62,6 +64,112 @@ void __init ts78xx_map_io(void)
> }
>
> /*****************************************************************************
> + * IRQ
> + ****************************************************************************/
> +static void ts78xx_irq_ack(u32 irq)
> +{
> + u32 addr;
> +
> + addr = readl(H2C_DOORBELL_REG);
> + addr &= ~(1 << (irq - FPGA_IRQ(0)));
> + writel(addr, H2C_DOORBELL_REG);
> +}
> +
> +static void ts78xx_irq_mask(u32 irq)
> +{
> + void __iomem *fpgaaddr = get_irq_chip_data(irq);
> + unsigned long reg;
> +
> + reg = readl(H2C_DOORBELL_MASK_REG);
> + reg &= ~(1 << (irq - FPGA_IRQ(0)));
> + writel(reg, H2C_DOORBELL_MASK_REG);
> +
> + reg = readl(fpgaaddr);
> + reg &= ~(1 << (irq - FPGA_IRQ(0)));
> + writel(reg, fpgaaddr);
> +}
> +
> +static void ts78xx_irq_unmask(u32 irq)
> +{
> + void __iomem *fpgaaddr = get_irq_chip_data(irq);
> + unsigned long reg;
> +
> + reg = readl(H2C_DOORBELL_MASK_REG);
> + reg |= 1 << (irq - FPGA_IRQ(0));
> + writel(reg, H2C_DOORBELL_MASK_REG);
> +
> + reg = readl(H2C_DOORBELL_REG);
> + reg &= ~(1 << (irq - FPGA_IRQ(0)));
> + writel(reg, H2C_DOORBELL_REG);
> +
> + reg = readl(fpgaaddr);
> + reg |= 1 << (irq - FPGA_IRQ(0));
> + writel(reg, fpgaaddr);
> +}
> +
> +static struct irq_chip ts78xx_irq_chip = {
> + .name = "ts78xx_irq",
> + .ack = ts78xx_irq_ack,
> + .mask = ts78xx_irq_mask,
> + .unmask = ts78xx_irq_unmask,
> +};
> +
> +static void ts78xx_irq(unsigned int irq, struct irq_desc *desc)
> +{
> + unsigned long reg = readl(H2C_DOORBELL_REG);
> +
> + for_each_set_bit(irq, ®, 32)
> + generic_handle_irq(FPGA_IRQ(irq));
> +}
> +
> +static int ts78xx_doorbell_irq_load(void)
> +{
> + void __iomem *fpgaaddr;
> + unsigned int irq;
> +
> + switch (ts78xx_fpga.id) {
> + case TS7800_REV_1:
> + case TS7800_REV_2:
> + case TS7800_REV_3:
> + case TS7800_REV_4:
> + case TS7800_REV_5:
> + case TS7800_REV_6:
> + case TS7800_REV_7:
> + case TS7800_REV_8:
> + case TS7800_REV_9:
> + fpgaaddr = (void __iomem *)(TS78XX_FPGA_REGS_VIRT_BASE | 0x204);
> + break;
> + default:
> + return -ENODEV;
> + }
> +
> + for (irq = FPGA_IRQ(0); irq < FPGA_IRQ(NR_TS78XX_FPGA_IRQS); irq++) {
> + set_irq_chip(irq, &ts78xx_irq_chip);
> + set_irq_chip_data(irq, fpgaaddr);
> + set_irq_handler(irq, handle_level_irq);
> + irq_desc[irq].status |= IRQ_LEVEL;
> + set_irq_flags(irq, IRQF_VALID);
> + }
> +
> + set_irq_chained_handler(IRQ_ORION5X_DOORBELL_H2C, ts78xx_irq);
> +
> + return 0;
> +}
> +
> +static void ts78xx_doorbell_irq_unload(void)
> +{
> + unsigned int irq;
> +
> + set_irq_chained_handler(IRQ_ORION5X_DOORBELL_H2C, NULL);
> +
> + for (irq = FPGA_IRQ(0); irq < FPGA_IRQ(NR_TS78XX_FPGA_IRQS); irq++) {
> + set_irq_flags(irq, 0);
> + set_irq_chip(irq, NULL);
> + set_irq_chip_data(irq, NULL);
> + }
> +}
> +
> +/*****************************************************************************
> * Ethernet
> ****************************************************************************/
> static struct mv643xx_eth_platform_data ts78xx_eth_data = {
> @@ -376,6 +484,7 @@ static void ts78xx_ts_rng_unload(void)
> ****************************************************************************/
> static void ts78xx_fpga_devices_zero_init(void)
> {
> + ts78xx_fpga.supports.doorbell_irq.init = 0;
> ts78xx_fpga.supports.ts_rtc.init = 0;
> ts78xx_fpga.supports.ts_nand.init = 0;
> ts78xx_fpga.supports.ts_rng.init = 0;
> @@ -394,11 +503,13 @@ static void ts78xx_fpga_supports(void)
> case TS7800_REV_7:
> case TS7800_REV_8:
> case TS7800_REV_9:
> + ts78xx_fpga.supports.doorbell_irq.present = 1;
> ts78xx_fpga.supports.ts_rtc.present = 1;
> ts78xx_fpga.supports.ts_nand.present = 1;
> ts78xx_fpga.supports.ts_rng.present = 1;
> break;
> default:
> + ts78xx_fpga.supports.doorbell_irq.present = 0;
> ts78xx_fpga.supports.ts_rtc.present = 0;
> ts78xx_fpga.supports.ts_nand.present = 0;
> ts78xx_fpga.supports.ts_rng.present = 0;
> @@ -409,6 +520,14 @@ static int ts78xx_fpga_load_devices(void)
> {
> int tmp, ret = 0;
>
> + if (ts78xx_fpga.supports.doorbell_irq.present == 1) {
> + tmp = ts78xx_doorbell_irq_load();
> + if (tmp) {
> + printk(KERN_INFO "TS-78xx: Doorbell IRQs not registered\n");
> + ts78xx_fpga.supports.doorbell_irq.present = 0;
> + }
> + ret |= tmp;
> + }
> if (ts78xx_fpga.supports.ts_rtc.present == 1) {
> tmp = ts78xx_ts_rtc_load();
> if (tmp) {
> @@ -441,6 +560,8 @@ static int ts78xx_fpga_unload_devices(void)
> {
> int ret = 0;
>
> + if (ts78xx_fpga.supports.doorbell_irq.present == 1)
> + ts78xx_doorbell_irq_unload();
> if (ts78xx_fpga.supports.ts_rtc.present == 1)
> ts78xx_ts_rtc_unload();
> if (ts78xx_fpga.supports.ts_nand.present == 1)
> --
> 1.7.2.3
>
^ permalink raw reply
* [PATCH V4 3/4] ARM: Xilinx: base header files and assembly macros
From: Arnd Bergmann @ 2011-02-20 21:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <42efee69-aca1-446b-a997-f0063e113877@VA3EHSMHS009.ehs.local>
On Friday 18 February 2011, John Linn wrote:
> +
> +/* IO address mapping macros, nothing special at this time but required */
> +
> +#ifdef __ASSEMBLER__
> +#define IOMEM(x) (x)
> +#else
> +#define IOMEM(x) ((void __force __iomem *)(x))
> +#endif
> +
> +#define __io(a) __typesafe_io(a)
> +#define __mem_pci(a) (a)
Are you planning to support PCI or PCMCIA? If so, the __io definition will have to
change so it points to the PIO register window.
> diff --git a/arch/arm/mach-xilinx/include/mach/timex.h b/arch/arm/mach-xilinx/include/mach/timex.h
> new file mode 100644
> index 0000000..4ebc0a6
> --- /dev/null
> +++ b/arch/arm/mach-xilinx/include/mach/timex.h
> +#ifndef __MACH_TIMEX_H__
> +#define __MACH_TIMEX_H__
> +
> +#define PERIPHERAL_CLOCK_RATE 2500000
> +
> +#define CLOCK_TICK_RATE (PERIPHERAL_CLOCK_RATE / 32)
> +
> +#endif
I thought we were at the point where CLOCK_TICK_RATE is no longer used.
Did the patches not make it in yet?
The rest looks fine to me.
Arnd
^ permalink raw reply
* [PATCH resend] omap: Fix linker error in drivers/video/omap/lcd_2430sdp.c
From: Jarkko Nikula @ 2011-02-20 20:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298193466-29534-1-git-send-email-jhnikula@gmail.com>
There is a linker error from lcd_2430sdp.c if CONFIG_TWL4030_CORE is not
set. This can be triggered on OMAP2 builds where OMAP3 or OMAP4 are not set.
drivers/built-in.o: In function `sdp2430_panel_disable':
drivers/video/omap/lcd_2430sdp.c:123: undefined reference to `twl_i2c_write_u8'
drivers/video/omap/lcd_2430sdp.c:124: undefined reference to `twl_i2c_write_u8'
drivers/built-in.o: In function `sdp2430_panel_enable':
drivers/video/omap/lcd_2430sdp.c:110: undefined reference to `twl_i2c_write_u8'
drivers/video/omap/lcd_2430sdp.c:112: undefined reference to `twl_i2c_write_u8'
Fix this by adding TWL4030_CORE dependency to CONFIG_MACH_OMAP_2430SDP as
there is no own entry in drivers/video/omap/Kconfig.
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
Resend = forgot to cc LAKML.
Quite old issue most probably. Can be triggered in mainline anyway.
---
arch/arm/mach-omap2/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index b9d8a7b..bfdf240 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -132,6 +132,7 @@ config MACH_OMAP_2430SDP
depends on SOC_OMAP2430
default y
select OMAP_PACKAGE_ZAC
+ select TWL4030_CORE
config MACH_OMAP3_BEAGLE
bool "OMAP3 BEAGLE board"
--
1.7.0.4
^ permalink raw reply related
* [PATCH v2] mxcmmc: use dmaengine API
From: Chris Ball @ 2011-02-20 19:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102181437.02943.arnd@arndb.de>
Hi Sascha,
On Fri, Feb 18, 2011 at 02:37:02PM +0100, Arnd Bergmann wrote:
> On Friday 18 February 2011, Sascha Hauer wrote:
> > This switches the mxcmmc driver to use the dmaengine API. Unlike
> > the old one this one is always present in the tree, even if no DMA
> > is implemented, hence we can remove all the #ifdefs in from the driver.
> > The driver automatically switches to PIO mode if no DMA support or no
> > suitable channel is available.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>
> The patch looks good to me, but shouldn't you also add a Kconfig
> dependency or select on CONFIG_DMA_ENGINE? I don't see where that
> gets set.
>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
I've pushed this to mmc-next now, but please do reply to Arnd's Kconfig
question.
Thanks,
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply
* [PATCH v2 3/3] ARM: PXA: PXAFB: Fix typo in ypos assignment
From: Marek Vasut @ 2011-02-20 18:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298214147-11578-3-git-send-email-anarsoul@gmail.com>
On Sunday 20 February 2011 16:02:27 Vasily Khoruzhick wrote:
> Sascha Hauer <s.hauer@pengutronix.de> pointed that
> ypos takes value of xpos due to typo.
Looks good,
Acked-by: Marek Vasut <marek.vasut@gmail.com>
>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
> drivers/video/pxafb.c | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
> index 54a9ab8..80ea7e6 100644
> --- a/drivers/video/pxafb.c
> +++ b/drivers/video/pxafb.c
> @@ -766,7 +766,7 @@ static int overlayfb_check_var(struct fb_var_screeninfo
> *var, int xpos, ypos, pfor, bpp;
>
> xpos = NONSTD_TO_XPOS(var->nonstd);
> - ypos = NONSTD_TO_XPOS(var->nonstd);
> + ypos = NONSTD_TO_YPOS(var->nonstd);
> pfor = NONSTD_TO_PFOR(var->nonstd);
>
> bpp = pxafb_var_to_bpp(var);
> @@ -864,7 +864,7 @@ static int overlayfb_set_par(struct fb_info *info)
>
> bpp = pxafb_var_to_bpp(var);
> xpos = NONSTD_TO_XPOS(var->nonstd);
> - ypos = NONSTD_TO_XPOS(var->nonstd);
> + ypos = NONSTD_TO_YPOS(var->nonstd);
> pfor = NONSTD_TO_PFOR(var->nonstd);
>
> ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) |
^ permalink raw reply
* [PATCH v2 1/3] ARM: PXA: PXAFB: Fix double-free issue
From: Marek Vasut @ 2011-02-20 18:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298214147-11578-1-git-send-email-anarsoul@gmail.com>
On Sunday 20 February 2011 16:02:25 Vasily Khoruzhick wrote:
> From: Russell King - ARM Linux <linux@arm.linux.org.uk>
>
> Release callback tries to free memory even if it was not allocated in
> map_video_memory, fix it.
>
> Added by Vasily Khoruzhick:
>
> - Clear x_res/y_res fields of fb.var on release, to make sure
> our callback will be called on next FBIOPUT_VSCREENINFO ioctl.
> - Disable overlay only if it was enabled.
> - Don't touch FDADR1 if it's not necessary
>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
> v2: remove unnecessary newlines, add comment about FDADR1
>
> drivers/video/pxafb.c | 49
> ++++++++++++++++++++++++++++++++++--------------- drivers/video/pxafb.h |
> 2 +-
> 2 files changed, 35 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
> index 825b665..41a499f 100644
> --- a/drivers/video/pxafb.c
> +++ b/drivers/video/pxafb.c
> @@ -629,6 +629,9 @@ static void overlay1fb_disable(struct pxafb_layer *ofb)
> {
> uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
>
> + if (!(lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN))
> + return;
> +
Maybe you can even avoid reading LCCR5 above ;-)
> lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN);
>
> lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1));
> @@ -687,6 +690,9 @@ static void overlay2fb_disable(struct pxafb_layer *ofb)
> {
> uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
>
> + if (!(lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN))
> + return;
> +
Hm?
> lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN);
>
> lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2));
> @@ -720,12 +726,10 @@ static int overlayfb_open(struct fb_info *info, int
> user) if (user == 0)
> return -ENODEV;
>
> - /* allow only one user at a time */
> - if (atomic_inc_and_test(&ofb->usage))
> - return -EBUSY;
> + if (ofb->usage++ == 0)
> + /* unblank the base framebuffer */
> + fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
>
> - /* unblank the base framebuffer */
> - fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
> return 0;
> }
>
> @@ -733,12 +737,24 @@ static int overlayfb_release(struct fb_info *info,
> int user) {
> struct pxafb_layer *ofb = (struct pxafb_layer*) info;
>
> - atomic_dec(&ofb->usage);
> - ofb->ops->disable(ofb);
> -
> - free_pages_exact(ofb->video_mem, ofb->video_mem_size);
> - ofb->video_mem = NULL;
> - ofb->video_mem_size = 0;
> + if (--ofb->usage == 0) {
> + ofb->ops->disable(ofb);
> + ofb->fb.var.height = -1;
> + ofb->fb.var.width = -1;
> + ofb->fb.var.xres = ofb->fb.var.xres_virtual = 0;
> + ofb->fb.var.yres = ofb->fb.var.yres_virtual = 0;
> +
> + mutex_lock(&ofb->fb.mm_lock);
> + ofb->fb.fix.smem_start = 0;
> + ofb->fb.fix.smem_len = 0;
> + mutex_unlock(&ofb->fb.mm_lock);
> +
> + if (ofb->video_mem) {
> + free_pages_exact(ofb->video_mem, ofb->video_mem_size);
> + ofb->video_mem = NULL;
> + ofb->video_mem_size = 0;
> + }
> + }
> return 0;
> }
>
> @@ -817,7 +833,8 @@ static int overlayfb_map_video_memory(struct
> pxafb_layer *ofb) if (ofb->video_mem_size >= size)
> return 0;
>
> - free_pages_exact(ofb->video_mem, ofb->video_mem_size);
> + /* don't re-allocate: userspace may have the buffer mapped */
> + return -EINVAL;
> }
>
> ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
> @@ -891,7 +908,7 @@ static void __devinit init_pxafb_overlay(struct
> pxafb_info *fbi,
>
> ofb->id = id;
> ofb->ops = &ofb_ops[id];
> - atomic_set(&ofb->usage, 0);
> + ofb->usage = 0;
> ofb->fbi = fbi;
> init_completion(&ofb->branch_done);
> }
> @@ -1368,7 +1385,8 @@ static int pxafb_activate_var(struct
> fb_var_screeninfo *var, (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||
> (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||
> (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||
> - (lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))
> + ((fbi->lccr0 & LCCR0_SDS) &&
> + (lcd_readl(fbi, FDADR1) != fbi->fdadr[1])))
> pxafb_schedule_work(fbi, C_REENABLE);
>
> return 0;
> @@ -1420,7 +1438,8 @@ static void pxafb_enable_controller(struct pxafb_info
> *fbi) lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);
>
> lcd_writel(fbi, FDADR0, fbi->fdadr[0]);
> - lcd_writel(fbi, FDADR1, fbi->fdadr[1]);
> + if (fbi->lccr0 & LCCR0_SDS)
> + lcd_writel(fbi, FDADR1, fbi->fdadr[1]);
> lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);
> }
>
> diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
> index 2353521..84e3ae1 100644
> --- a/drivers/video/pxafb.h
> +++ b/drivers/video/pxafb.h
> @@ -92,7 +92,7 @@ struct pxafb_layer_ops {
> struct pxafb_layer {
> struct fb_info fb;
> int id;
> - atomic_t usage;
> + uint32_t usage;
> uint32_t control[2];
>
> struct pxafb_layer_ops *ops;
^ permalink raw reply
* [PATCH v2] ARM: vfp: Always save VFP state in vfp_pm_suspend
From: Colin Cross @ 2011-02-20 18:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110220125745.GC14495@n2100.arm.linux.org.uk>
On Sun, Feb 20, 2011 at 4:57 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Feb 16, 2011 at 11:36:45AM -0800, Colin Cross wrote:
>> On Tue, Feb 15, 2011 at 9:03 AM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > On Mon, Feb 14, 2011 at 02:55:47PM -0800, Colin Cross wrote:
>> >> diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
>> >> index 66bf8d1..7231d18 100644
>> >> --- a/arch/arm/vfp/vfpmodule.c
>> >> +++ b/arch/arm/vfp/vfpmodule.c
>> >> @@ -415,13 +415,13 @@ static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state)
>> >> ? ? ? struct thread_info *ti = current_thread_info();
>> >> ? ? ? u32 fpexc = fmrx(FPEXC);
>> >>
>> >> - ? ? /* if vfp is on, then save state for resumption */
>> >> - ? ? if (fpexc & FPEXC_EN) {
>> >> + ? ? /* save state for resume */
>> >> + ? ? if (last_VFP_context[ti->cpu]) {
>> >
>> > I'm not entirely happy with this.
>> >
>> > It is true that last_VFP_context[] when non-NULL indicates who owns the
>> > hardware VFP state, so saving it would seem logical. ?However, this new
>> > code now saves the state with the saved fpexc indicating that it's disabled.
>> >
>> > This will cause a VFP exception to misbehave by reloading the state, and
>> > then disabling the VFP unit. ?That will cause another VFP exception which
>> > will find the VFP unit disabled, and re-enable it. ?All in all, this is
>> > rather wasteful.
>> >
>> > So...
>> > ? ? ? ?/* If lazy disable, re-enable the VFP ready for it to be saved */
>> > ? ? ? ?if (last_VFP_context[ti->cpu] != &ti->vfpstate) {
>> > ? ? ? ? ? ? ? ?fpexc |= FPEXC_EN;
>> > ? ? ? ? ? ? ? ?fmxr(FPEXC, fpexc);
>> > ? ? ? ?}
>> > ? ? ? ?/* If VFP is on, then save state for resumption */
>> > ? ? ? ?if (fpexc & FPEXC_EN) {
>> > ? ? ? ? ? ? ? ?...
>>
>> I think v2 of the patch handles this case correctly:
>> ? ? ? /* save state for resume */
>> ? ? ? if (last_VFP_context[ti->cpu]) {
>> ? ? ? ? ? ? ? printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
>> ? ? ? ? ? ? ? fmxr(FPEXC, fpexc | FPEXC_EN);
>> ? ? ? ? ? ? ? vfp_save_state(last_VFP_context[ti->cpu], fpexc);
>
> This saves fpexc with the enable flag possibly clear.
>
>> ? ? ? ? ? ? ? last_VFP_context[ti->cpu] = NULL;
>> ? ? ? ? ? ? ? fmxr(FPEXC, fpexc & ~FPEXC_EN);
>> ? ? ? }
>>
>> This version enables the VFP if it was not enabled, but saves the
>> original fpexc value.
>
> Which is wrong as I said above.
>
Sorry, I misunderstood. I'll repost with your changes after I get a
chance to test it, or you can commit it yourself.
^ permalink raw reply
* bug in arm_kprobe_decode_insn
From: Marcin Slusarz @ 2011-02-20 18:24 UTC (permalink / raw)
To: linux-arm-kernel
Hi
In arch/arm/kernel/kprobes-decode.c there's a function arm_kprobe_decode_insn
which does:
} else if ((insn & 0x0e000000) == 0x0c400000) {
...
This is always false, so code below is dead.
I'm not sure about what was intended here, but I think this change might be correct:
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c
index 2c1f005..8f6ed43 100644
--- a/arch/arm/kernel/kprobes-decode.c
+++ b/arch/arm/kernel/kprobes-decode.c
@@ -1437,7 +1437,7 @@ arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return space_cccc_1100_010x(insn, asi);
- } else if ((insn & 0x0e000000) == 0x0c400000) {
+ } else if ((insn & 0x0e000000) == 0x0c000000) {
return space_cccc_110x(insn, asi);
--
I found this bug by coccinelle (http://coccinelle.lip6.fr/).
(The script is ugly, so I'm not attaching it here)
Marcin Slusarz
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox