* [PATCH 2/5 v2] clk: qcom: Elaborate on "active" clocks in the RPM clock bindings
From: Linus Walleij @ 2017-04-19 9:13 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd
Cc: linux-clk, linux-arm-msm, Linus Walleij, devicetree
In-Reply-To: <20170419091326.11226-1-linus.walleij@linaro.org>
The concept of "active" clocks is just explained in a bried comment in the
device driver, let's explain it a bit more in the device tree bindings
so everyone understands this.
Cc: devicetree@vger.kernel.org
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Reword slighty in accordance with Stephens feedback.
---
Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
index d470a0187035..833a89f06ae0 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
@@ -18,6 +18,14 @@ Required properties :
- #clock-cells : shall contain 1
+The clock enumerators are defined in <dt-bindings/clock/qcom,rpmcc.h>
+and come in pairs: FOO_CLK followed by FOO_A_CLK. The latter clock
+is an "active" clock, which means that the consumer only care that the
+clock is available when the apps CPU subsystem is active, i.e. not
+suspended or in deep idle. If it is important that the clock keeps running
+during system suspend, you need to specify the non-active clock, the one
+not containing *_A_* in the enumerator name.
+
Example:
smd {
compatible = "qcom,smd";
--
2.9.3
^ permalink raw reply related
* [PATCH 1/5 v2] clk: qcom: Update DT bindings for the MSM8660/APQ8060 RPMCC
From: Linus Walleij @ 2017-04-19 9:13 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd
Cc: linux-clk, linux-arm-msm, Linus Walleij, devicetree
These compatible strings need to be added to extend support
for the RPM CC to cover MSM8660/APQ8060. We also need to add
enumberators to the include file for a few clocks that were
missing.
Cc: devicetree@vger.kernel.org
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Collect Rob's ACK
---
Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | 2 ++
include/dt-bindings/clock/qcom,rpmcc.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
index a7235e9e1c97..d470a0187035 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
@@ -10,6 +10,8 @@ Required properties :
- compatible : shall contain only one of the following. The generic
compatible "qcom,rpmcc" should be also included.
+ "qcom,rpmcc-msm8660", "qcom,rpmcc"
+ "qcom,rpmcc-apq8060", "qcom,rpmcc"
"qcom,rpmcc-msm8916", "qcom,rpmcc"
"qcom,rpmcc-msm8974", "qcom,rpmcc"
"qcom,rpmcc-apq8064", "qcom,rpmcc"
diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h
index 96b63c00249e..44358562a031 100644
--- a/include/dt-bindings/clock/qcom,rpmcc.h
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
@@ -37,6 +37,10 @@
#define RPM_SYS_FABRIC_A_CLK 19
#define RPM_SFPB_CLK 20
#define RPM_SFPB_A_CLK 21
+#define RPM_PLL4_CLK 22
+#define RPM_PLL4_A_CLK 23
+#define RPM_SMI_CLK 24
+#define RPM_SMI_A_CLK 25
/* SMD RPM clocks */
#define RPM_SMD_XO_CLK_SRC 0
--
2.9.3
^ permalink raw reply related
* Re: [PATCH v13 03/10] mux: minimal mux subsystem and gpio-based mux controller
From: Philipp Zabel @ 2017-04-19 9:06 UTC (permalink / raw)
To: Peter Rosin
Cc: linux-kernel, Greg Kroah-Hartman, Wolfram Sang, Rob Herring,
Mark Rutland, Jonathan Cameron, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
linux-i2c, devicetree, linux-iio, linux-doc, Andrew Morton,
Colin Ian King, Paul Gortmaker, kernel
In-Reply-To: <1492101794-13444-4-git-send-email-peda@axentia.se>
On Thu, 2017-04-13 at 18:43 +0200, Peter Rosin wrote:
> Add a new minimalistic subsystem that handles multiplexer controllers.
> When multiplexers are used in various places in the kernel, and the
> same multiplexer controller can be used for several independent things,
> there should be one place to implement support for said multiplexer
> controller.
>
> A single multiplexer controller can also be used to control several
> parallel multiplexers, that are in turn used by different subsystems
> in the kernel, leading to a need to coordinate multiplexer accesses.
> The multiplexer subsystem handles this coordination.
>
> This new mux controller subsystem initially comes with a single backend
> driver that controls gpio based multiplexers. Even though not needed by
> this initial driver, the mux controller subsystem is prepared to handle
> chips with multiple (independent) mux controllers.
>
> Reviewed-by: Jonathan Cameron <jic23@kernel.org>
> Signed-off-by: Peter Rosin <peda@axentia.se>
> ---
> Documentation/driver-model/devres.txt | 8 +
> MAINTAINERS | 2 +
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/mux/Kconfig | 34 +++
> drivers/mux/Makefile | 6 +
> drivers/mux/mux-core.c | 422 ++++++++++++++++++++++++++++++++++
> drivers/mux/mux-gpio.c | 114 +++++++++
> include/linux/mux.h | 252 ++++++++++++++++++++
> 9 files changed, 841 insertions(+)
> create mode 100644 drivers/mux/Kconfig
> create mode 100644 drivers/mux/Makefile
> create mode 100644 drivers/mux/mux-core.c
> create mode 100644 drivers/mux/mux-gpio.c
> create mode 100644 include/linux/mux.h
>
> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> index efb8200819d6..e2343d9cbec7 100644
> --- a/Documentation/driver-model/devres.txt
> +++ b/Documentation/driver-model/devres.txt
> @@ -337,6 +337,14 @@ MEM
> MFD
> devm_mfd_add_devices()
>
> +MUX
> + devm_mux_chip_alloc()
> + devm_mux_chip_free()
> + devm_mux_chip_register()
> + devm_mux_chip_unregister()
> + devm_mux_control_get()
> + devm_mux_control_put()
> +
> PER-CPU MEM
> devm_alloc_percpu()
> devm_free_percpu()
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7fc06739c8ad..591eba737678 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8563,6 +8563,8 @@ M: Peter Rosin <peda@axentia.se>
> S: Maintained
> F: Documentation/devicetree/bindings/mux/
> F: include/linux/dt-bindings/mux/
> +F: include/linux/mux.h
> +F: drivers/mux/
>
> MULTISOUND SOUND DRIVER
> M: Andrew Veliath <andrewtv@usa.net>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 117ca14ccf85..a7ea13e1b869 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -204,4 +204,6 @@ source "drivers/fpga/Kconfig"
>
> source "drivers/fsi/Kconfig"
>
> +source "drivers/mux/Kconfig"
> +
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 2eced9afba53..c0436f6dd5a9 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -177,3 +177,4 @@ obj-$(CONFIG_ANDROID) += android/
> obj-$(CONFIG_NVMEM) += nvmem/
> obj-$(CONFIG_FPGA) += fpga/
> obj-$(CONFIG_FSI) += fsi/
> +obj-$(CONFIG_MULTIPLEXER) += mux/
> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
> new file mode 100644
> index 000000000000..41dfe08ead84
> --- /dev/null
> +++ b/drivers/mux/Kconfig
> @@ -0,0 +1,34 @@
> +#
> +# Multiplexer devices
> +#
> +
> +menuconfig MULTIPLEXER
> + tristate "Multiplexer subsystem"
> + help
> + Multiplexer controller subsystem. Multiplexers are used in a
> + variety of settings, and this subsystem abstracts their use
> + so that the rest of the kernel sees a common interface. When
> + multiple parallel multiplexers are controlled by one single
> + multiplexer controller, this subsystem also coordinates the
> + multiplexer accesses.
> +
> + To compile the subsystem as a module, choose M here: the module will
> + be called mux-core.
> +
> +if MULTIPLEXER
> +
> +config MUX_GPIO
> + tristate "GPIO-controlled Multiplexer"
> + depends on OF && GPIOLIB
> + help
> + GPIO-controlled Multiplexer controller.
> +
> + The driver builds a single multiplexer controller using a number
> + of gpio pins. For N pins, there will be 2^N possible multiplexer
> + states. The GPIO pins can be connected (by the hardware) to several
> + multiplexers, which in that case will be operated in parallel.
> +
> + To compile the driver as a module, choose M here: the module will
> + be called mux-gpio.
> +
> +endif
> diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile
> new file mode 100644
> index 000000000000..bb16953f6290
> --- /dev/null
> +++ b/drivers/mux/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for multiplexer devices.
> +#
> +
> +obj-$(CONFIG_MULTIPLEXER) += mux-core.o
> +obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
> diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c
> new file mode 100644
> index 000000000000..66a8bccfc3d7
> --- /dev/null
> +++ b/drivers/mux/mux-core.c
> @@ -0,0 +1,422 @@
> +/*
> + * Multiplexer subsystem
> + *
> + * Copyright (C) 2017 Axentia Technologies AB
> + *
> + * Author: Peter Rosin <peda@axentia.se>
> + *
> + * 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.
> + */
> +
> +#define pr_fmt(fmt) "mux-core: " fmt
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/mux.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +
> +/*
> + * The idle-as-is "state" is not an actual state that may be selected, it
> + * only implies that the state should not be changed. So, use that state
> + * as indication that the cached state of the multiplexer is unknown.
> + */
> +#define MUX_CACHE_UNKNOWN MUX_IDLE_AS_IS
> +
> +static struct class mux_class = {
> + .name = "mux",
> + .owner = THIS_MODULE,
> +};
> +
> +static int __init mux_init(void)
> +{
> + return class_register(&mux_class);
> +}
> +
> +static DEFINE_IDA(mux_ida);
> +
> +static void mux_chip_release(struct device *dev)
> +{
> + struct mux_chip *mux_chip = to_mux_chip(dev);
> +
> + ida_simple_remove(&mux_ida, mux_chip->id);
> + kfree(mux_chip);
> +}
> +
> +static struct device_type mux_type = {
> + .name = "mux-chip",
> + .release = mux_chip_release,
> +};
> +
> +struct mux_chip *mux_chip_alloc(struct device *dev,
> + unsigned int controllers, size_t sizeof_priv)
> +{
> + struct mux_chip *mux_chip;
> + int i;
> +
> + if (WARN_ON(!dev || !controllers))
> + return NULL;
> +
> + mux_chip = kzalloc(sizeof(*mux_chip) +
> + controllers * sizeof(*mux_chip->mux) +
> + sizeof_priv, GFP_KERNEL);
> + if (!mux_chip)
> + return NULL;
> +
> + mux_chip->mux = (struct mux_control *)(mux_chip + 1);
> + mux_chip->dev.class = &mux_class;
> + mux_chip->dev.type = &mux_type;
> + mux_chip->dev.parent = dev;
> + mux_chip->dev.of_node = dev->of_node;
> + dev_set_drvdata(&mux_chip->dev, mux_chip);
> +
> + mux_chip->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
> + if (mux_chip->id < 0) {
> + pr_err("muxchipX failed to get a device id\n");
> + kfree(mux_chip);
> + return NULL;
> + }
> + dev_set_name(&mux_chip->dev, "muxchip%d", mux_chip->id);
> +
> + mux_chip->controllers = controllers;
> + for (i = 0; i < controllers; ++i) {
> + struct mux_control *mux = &mux_chip->mux[i];
> +
> + mux->chip = mux_chip;
> + init_rwsem(&mux->lock);
> + mux->cached_state = MUX_CACHE_UNKNOWN;
> + mux->idle_state = MUX_IDLE_AS_IS;
> + }
> +
> + device_initialize(&mux_chip->dev);
> +
> + return mux_chip;
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_alloc);
> +
> +static int mux_control_set(struct mux_control *mux, int state)
> +{
> + int ret = mux->chip->ops->set(mux, state);
> +
> + mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state;
> +
> + return ret;
> +}
> +
> +int mux_chip_register(struct mux_chip *mux_chip)
> +{
> + int i;
> + int ret;
> +
> + for (i = 0; i < mux_chip->controllers; ++i) {
> + struct mux_control *mux = &mux_chip->mux[i];
> +
> + if (mux->idle_state == mux->cached_state)
> + continue;
> +
> + ret = mux_control_set(mux, mux->idle_state);
> + if (ret < 0) {
> + dev_err(&mux_chip->dev, "unable to set idle state\n");
> + return ret;
> + }
> + }
> +
> + ret = device_add(&mux_chip->dev);
> + if (ret < 0)
> + dev_err(&mux_chip->dev,
> + "device_add failed in mux_chip_register: %d\n", ret);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_register);
> +
> +void mux_chip_unregister(struct mux_chip *mux_chip)
> +{
> + device_del(&mux_chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_unregister);
> +
> +void mux_chip_free(struct mux_chip *mux_chip)
> +{
> + if (!mux_chip)
> + return;
> +
> + put_device(&mux_chip->dev);
> +}
> +EXPORT_SYMBOL_GPL(mux_chip_free);
> +
> +static void devm_mux_chip_release(struct device *dev, void *res)
> +{
> + struct mux_chip *mux_chip = *(struct mux_chip **)res;
> +
> + mux_chip_free(mux_chip);
> +}
> +
> +struct mux_chip *devm_mux_chip_alloc(struct device *dev,
> + unsigned int controllers,
> + size_t sizeof_priv)
> +{
> + struct mux_chip **ptr, *mux_chip;
> +
> + ptr = devres_alloc(devm_mux_chip_release, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return NULL;
> +
> + mux_chip = mux_chip_alloc(dev, controllers, sizeof_priv);
> + if (!mux_chip) {
> + devres_free(ptr);
> + return NULL;
> + }
> +
> + *ptr = mux_chip;
> + devres_add(dev, ptr);
> +
> + return mux_chip;
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_chip_alloc);
> +
> +static int devm_mux_chip_match(struct device *dev, void *res, void *data)
> +{
> + struct mux_chip **r = res;
> +
> + if (WARN_ON(!r || !*r))
> + return 0;
> +
> + return *r == data;
> +}
> +
> +void devm_mux_chip_free(struct device *dev, struct mux_chip *mux_chip)
> +{
> + WARN_ON(devres_release(dev, devm_mux_chip_release,
> + devm_mux_chip_match, mux_chip));
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_chip_free);
> +
> +static void devm_mux_chip_reg_release(struct device *dev, void *res)
> +{
> + struct mux_chip *mux_chip = *(struct mux_chip **)res;
> +
> + mux_chip_unregister(mux_chip);
> +}
> +
> +int devm_mux_chip_register(struct device *dev,
> + struct mux_chip *mux_chip)
> +{
> + struct mux_chip **ptr;
> + int res;
> +
> + ptr = devres_alloc(devm_mux_chip_reg_release, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + res = mux_chip_register(mux_chip);
> + if (res) {
> + devres_free(ptr);
> + return res;
> + }
> +
> + *ptr = mux_chip;
> + devres_add(dev, ptr);
> +
> + return res;
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_chip_register);
> +
> +void devm_mux_chip_unregister(struct device *dev, struct mux_chip *mux_chip)
> +{
> + WARN_ON(devres_release(dev, devm_mux_chip_reg_release,
> + devm_mux_chip_match, mux_chip));
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_chip_unregister);
> +
> +int mux_control_select(struct mux_control *mux, int state)
If we let two of these race, ...
> +{
> + int ret;
> +
> + if (down_read_trylock(&mux->lock)) {
> + if (mux->cached_state == state)
> + return 0;
> + /* Sigh, the mux needs updating... */
> + up_read(&mux->lock);
... and both decide the mux needs updating ...
> + }
> +
> + /* ...or it's just contended. */
> + down_write(&mux->lock);
... then the last to get to down_write will just wait here forever (or
until the first consumer calls mux_control_deselect, which may never
happen)?
> +
> + if (mux->cached_state == state) {
> + /*
> + * Hmmm, someone else changed the mux to my liking.
> + * That makes me wonder how long I waited for nothing?
> + */
> + downgrade_write(&mux->lock);
> + return 0;
> + }
> +
> + ret = mux_control_set(mux, state);
> + if (ret < 0) {
> + if (mux->idle_state != MUX_IDLE_AS_IS)
> + mux_control_set(mux, mux->idle_state);
> +
> + up_write(&mux->lock);
> + return ret;
> + }
> +
> + downgrade_write(&mux->lock);
> +
> + return 1;
> +}
> +EXPORT_SYMBOL_GPL(mux_control_select);
I wonder if these should be called mux_control_lock/unlock instead,
which would allow for try_lock and lock_timeout variants.
regards
Philipp
^ permalink raw reply
* Re: [PATCH v3 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Heiko Stuebner @ 2017-04-19 9:02 UTC (permalink / raw)
To: Guillaume Tucker
Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Neil Armstrong,
Sjoerd Simons, Wookey, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, John Reitan,
Rob Herring, Enric Balletbo i Serra,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <96021d989387d9aaff039efebd66aa3bfe667941.1492588180.git.guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Am Mittwoch, 19. April 2017, 09:06:17 CEST schrieb Guillaume Tucker:
> The ARM Mali Midgard GPU family is present in a number of SoCs
> from many different vendors such as Samsung Exynos and Rockchip.
>
> Import the device tree bindings documentation from the r16p0
> release of the Mali Midgard GPU kernel driver:
>
> https://developer.arm.com/-/media/Files/downloads/mali-drivers/kernel/mali-midgard-gpu/TX011-SW-99002-r16p0-00rel0.tgz
>
> The "compatible" property strings have been redesigned to explicitly
> list all the Mali Midgard GPU types and include optional vendor ones.
>
> The "clock-names" property has been dropped as only one clock is used
> by the Mali Midgard driver which now needs to call clk_get with NULL.
>
> The "interrupt-names" property values have been converted to
> lower-case: "job", "mmu" and "gpu".
>
> The following optional bindings have been omitted in this initial
> version as they are only used in very specific cases:
>
> * snoop_enable_smc
> * snoop_disable_smc
> * jm_config
> * power_model
> * system-coherency
> * ipa-model
>
> The example has been simplified accordingly.
>
> The copyright and GPL licence header has been removed as deemed not
> necessary.
>
> CC: John Reitan <john.reitan-5wv7dgnIgG8@public.gmane.org>
> Tested-by: Enric Balletbo i Serra <enric.balletbo-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
> Signed-off-by: Guillaume Tucker <guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
> ---
> .../devicetree/bindings/gpu/arm,mali-midgard.txt | 57 ++++++++++++++++++++++
> 1 file changed, 57 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
>
> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
> new file mode 100644
> index 000000000000..917c4f8d178f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
> @@ -0,0 +1,57 @@
> +ARM Mali Midgard GPU
> +====================
> +
> +Required properties:
> +
> +- compatible :
> + * Must be one of the following:
> + + "arm,mali-t60x"
> + + "arm,mali-t62x"
> + + "arm,mali-t720"
> + + "arm,mali-t760"
> + + "arm,mali-t820"
> + + "arm,mali-t830"
> + + "arm,mali-t860"
> + + "arm,mali-t880"
> + * And, optionally, one of the vendor specific compatible:
> + + "amlogic,meson-gxm-mali"
Please add a "rockchip,rk3288-mali" as well :-) , as I don't trust that the
generic compatible will be enough for all time and having that already
defined makes fixing the per soc things later a lot easier.
Thanks
Heiko
> +
> +- reg : Physical base address of the device and length of the register area.
> +
> +- interrupts : Contains the three IRQ lines required by Mali Midgard devices.
> +
> +- interrupt-names : Contains the names of IRQ resources in the order they were
> + provided in the interrupts property. Must contain: "job", "mmu", "gpu".
> +
> +
> +Optional properties:
> +
> +- clocks : Phandle to clock for the Mali Midgard device.
> +
> +- mali-supply : Phandle to regulator for the Mali device. Refer to
> + Documentation/devicetree/bindings/regulator/regulator.txt for details.
> +
> +- operating-points : Refer to Documentation/devicetree/bindings/power/opp.txt
> + for details.
> +
> +
> +Example for a Mali-T602:
> +
> +gpu@fc010000 {
> + compatible = "arm,mali-t60x", "arm,mali-midgard";
> + reg = <0xfc010000 0x4000>;
> + interrupts = <0 36 4>, <0 37 4>, <0 38 4>;
> + interrupt-names = "job", "mmu", "gpu";
> + clocks = <&pclk_mali>;
> + mali-supply = <&vdd_mali>;
> + operating-points = <
> + /* KHz uV */
> + 533000 1250000,
> + 450000 1150000,
> + 400000 1125000,
> + 350000 1075000,
> + 266000 1025000,
> + 160000 925000,
> + 100000 912500,
> + >;
> +};
>
^ permalink raw reply
* [PATCH 2/2] mmc: dw_mmc-rockchip: parse rockchip,default-num-phases from DT
From: Shawn Lin @ 2017-04-19 9:00 UTC (permalink / raw)
To: Jaehoon Chung, Ulf Hansson
Cc: Rob Herring, linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson,
Ziyuan Xu, linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Shawn Lin
In-Reply-To: <1492592434-81312-1-git-send-email-shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Currently we unconditionally do tuning for each degree, which
costs 900ms for each boot and resume.
May someone argue that this is a question of accuracy VS time. But I
would say it's a trick of how we need to do decision for our boards.
If we don't care the time we spend at all, we could definitely do tuning
for each degree. But when we need to improve the user experience, for
instance, speed up resuming from S3, we should also have the right to
do that. This patch add parsing "rockchip,default-num-phases", for folks
to specify the number of doing tuning. If not specified, 360 will be used
as before.
Signed-off-by: Shawn Lin <shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
drivers/mmc/host/dw_mmc-rockchip.c | 48 ++++++++++++++++++++++++--------------
1 file changed, 30 insertions(+), 18 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 372fb6e..c535526 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -25,6 +25,7 @@ struct dw_mci_rockchip_priv_data {
struct clk *drv_clk;
struct clk *sample_clk;
int default_sample_phase;
+ int num_phases;
};
static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
@@ -133,8 +134,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
}
}
-#define NUM_PHASES 360
-#define TUNING_ITERATION_TO_PHASE(i) (DIV_ROUND_UP((i) * 360, NUM_PHASES))
+#define TUNING_ITERATION_TO_PHASE(i, num_phases) \
+ (DIV_ROUND_UP((i) * 360, num_phases))
static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
{
@@ -159,13 +160,15 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
return -EIO;
}
- ranges = kmalloc_array(NUM_PHASES / 2 + 1, sizeof(*ranges), GFP_KERNEL);
+ ranges = kmalloc_array(priv->num_phases / 2 + 1,
+ sizeof(*ranges), GFP_KERNEL);
if (!ranges)
return -ENOMEM;
/* Try each phase and extract good ranges */
- for (i = 0; i < NUM_PHASES; ) {
- clk_set_phase(priv->sample_clk, TUNING_ITERATION_TO_PHASE(i));
+ for (i = 0; i < priv->num_phases; ) {
+ clk_set_phase(priv->sample_clk,
+ TUNING_ITERATION_TO_PHASE(i, priv->num_phases));
v = !mmc_send_tuning(mmc, opcode, NULL);
@@ -179,7 +182,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
if (v) {
ranges[range_count-1].end = i;
i++;
- } else if (i == NUM_PHASES - 1) {
+ } else if (i == priv->num_phases - 1) {
/* No extra skipping rules if we're at the end */
i++;
} else {
@@ -188,11 +191,11 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
* one since testing bad phases is slow. Skip
* 20 degrees.
*/
- i += DIV_ROUND_UP(20 * NUM_PHASES, 360);
+ i += DIV_ROUND_UP(20 * priv->num_phases, 360);
/* Always test the last one */
- if (i >= NUM_PHASES)
- i = NUM_PHASES - 1;
+ if (i >= priv->num_phases)
+ i = priv->num_phases - 1;
}
prev_v = v;
@@ -210,7 +213,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
range_count--;
}
- if (ranges[0].start == 0 && ranges[0].end == NUM_PHASES - 1) {
+ if (ranges[0].start == 0 && ranges[0].end == priv->num_phases - 1) {
clk_set_phase(priv->sample_clk, priv->default_sample_phase);
dev_info(host->dev, "All phases work, using default phase %d.",
priv->default_sample_phase);
@@ -222,7 +225,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
int len = (ranges[i].end - ranges[i].start + 1);
if (len < 0)
- len += NUM_PHASES;
+ len += priv->num_phases;
if (longest_range_len < len) {
longest_range_len = len;
@@ -230,25 +233,30 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
}
dev_dbg(host->dev, "Good phase range %d-%d (%d len)\n",
- TUNING_ITERATION_TO_PHASE(ranges[i].start),
- TUNING_ITERATION_TO_PHASE(ranges[i].end),
+ TUNING_ITERATION_TO_PHASE(ranges[i].start,
+ priv->num_phases),
+ TUNING_ITERATION_TO_PHASE(ranges[i].end,
+ priv->num_phases),
len
);
}
dev_dbg(host->dev, "Best phase range %d-%d (%d len)\n",
- TUNING_ITERATION_TO_PHASE(ranges[longest_range].start),
- TUNING_ITERATION_TO_PHASE(ranges[longest_range].end),
+ TUNING_ITERATION_TO_PHASE(ranges[longest_range].start,
+ priv->num_phases),
+ TUNING_ITERATION_TO_PHASE(ranges[longest_range].end,
+ priv->num_phases),
longest_range_len
);
middle_phase = ranges[longest_range].start + longest_range_len / 2;
- middle_phase %= NUM_PHASES;
+ middle_phase %= priv->num_phases;
dev_info(host->dev, "Successfully tuned phase to %d\n",
- TUNING_ITERATION_TO_PHASE(middle_phase));
+ TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases));
clk_set_phase(priv->sample_clk,
- TUNING_ITERATION_TO_PHASE(middle_phase));
+ TUNING_ITERATION_TO_PHASE(middle_phase,
+ priv->num_phases));
free:
kfree(ranges);
@@ -264,6 +272,10 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
if (!priv)
return -ENOMEM;
+ if (of_property_read_u32(np, "rockchip,default-num-phases",
+ &priv->num_phases))
+ priv->num_phases = 360;
+
if (of_property_read_u32(np, "rockchip,default-sample-phase",
&priv->default_sample_phase))
priv->default_sample_phase = 0;
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: rockchip-dw-mshc: add optional rockchip, default-num-phases
From: Shawn Lin @ 2017-04-19 9:00 UTC (permalink / raw)
To: Jaehoon Chung, Ulf Hansson
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Shawn Lin, Ziyuan Xu,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring
In-Reply-To: <1492592434-81312-1-git-send-email-shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
By default, dw_mmc-rockchip will execute tuning for each degree.
So we won't miss every point of the good sample windows. However,
probably the phases are linear inside the good sample window.
Actually we don't need to do tuning for each degree so that we could
save some time, for instance, probe the driver or resume from S3.
Signed-off-by: Shawn Lin <shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
index 520d61d..ea47ec0 100644
--- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
@@ -31,6 +31,10 @@ Optional Properties:
probing, low speeds or in case where all phases work at tuning time.
If not specified 0 deg will be used.
+* rockchip,default-num-phases: The default number of times that the host
+ execute tuning when needed. If not specified, the host will do tuning
+ for 360 times, namely tuning for each degree.
+
Example:
rkdwmmc0@12200000 {
--
1.9.1
^ permalink raw reply related
* [PATCH 0/2] Specify tuning count for individual board
From: Shawn Lin @ 2017-04-19 9:00 UTC (permalink / raw)
To: Jaehoon Chung, Ulf Hansson
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Shawn Lin, Ziyuan Xu,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring
I was seeing that it spends almost 700ms to init eMMC on RK3368 platform.
It also happened when resuming from S3 for each time. dw_mmc-rockchip
was trying to scan all degrees. However I wonder whether it's worth
doing that? Look at how the other host drivers do, for instance,
sdhci. At least sdhci-of-arasan on RK3399 platform only do tuning
for 32 times executed by PHY. Anyway, 360 times by default looks crazy
to me. I hope we could vote for it in DT instead of hard-coding it in
the code.
[ 1.193248] dwmmc_rockchip ff0f0000.dwmmc: IDMAC supports 32-bit
address mode.
[ 1.193316] dwmmc_rockchip ff0f0000.dwmmc: Using internal DMA
controller.
[ 1.193332] dwmmc_rockchip ff0f0000.dwmmc: Version ID is 270a
[ 1.193387] dwmmc_rockchip ff0f0000.dwmmc: DW MMC controller at irq
18,32 bit host data width,256 deep fifo
[ 1.193410] dwmmc_rockchip ff0f0000.dwmmc: 'clock-freq-min-max'
property was deprecated.
[ 1.193446] dwmmc_rockchip ff0f0000.dwmmc: No vmmc regulator found
[ 1.193458] dwmmc_rockchip ff0f0000.dwmmc: No vqmmc regulator found
[ 1.205185] mmc_host mmc1: Bus speed (slot 0) = 400000Hz (slot req
400000Hz, actual 400000HZ div = 0)
[ 1.219611] dwmmc_rockchip ff0f0000.dwmmc: 1 slots initialized
....
[ 1.912482] dwmmc_rockchip ff0f0000.dwmmc: Successfully tuned phase
to 182
[ 1.912683] mmc1: new HS200 MMC card at address 0001
[ 1.914511] mmcblk1: mmc1:0001 M8G1GC 7.28 GiB
[ 1.915527] mmcblk1boot0: mmc1:0001 M8G1GC partition 1 4.00 MiB
[ 1.916388] mmcblk1boot1: mmc1:0001 M8G1GC partition 2 4.00 MiB
[ 1.917232] mmcblk1rpmb: mmc1:0001 M8G1GC partition 3 512 KiB
Shawn Lin (2):
dt-bindings: rockchip-dw-mshc: add optional
rockchip,default-num-phases
mmc: dw_mmc-rockchip: parse rockchip,default-num-phases from DT
.../devicetree/bindings/mmc/rockchip-dw-mshc.txt | 4 ++
drivers/mmc/host/dw_mmc-rockchip.c | 48 ++++++++++++++--------
2 files changed, 34 insertions(+), 18 deletions(-)
--
1.9.1
^ permalink raw reply
* Re: [PATCH v3 2/5] ARM: dts: rockchip: add ARM Mali GPU node for rk3288
From: Heiko Stuebner @ 2017-04-19 8:59 UTC (permalink / raw)
To: Guillaume Tucker
Cc: Rob Herring, Mark Rutland, Neil Armstrong, Sjoerd Simons,
Enric Balletbo i Serra, John Reitan, Wookey,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <f6f127efb44fb1012f18c483fa522918f8f82d7c.1492588180.git.guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Am Mittwoch, 19. April 2017, 09:06:18 CEST schrieb Guillaume Tucker:
> Add Mali GPU device tree node for the rk3288 SoC, with devfreq
> opp table.
>
> Tested-by: Enric Balletbo i Serra <enric.balletbo-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
> Signed-off-by: Guillaume Tucker <guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
> ---
> arch/arm/boot/dts/rk3288.dtsi | 22 ++++++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
> index df8a0dbe9d91..187eed528f83 100644
> --- a/arch/arm/boot/dts/rk3288.dtsi
> +++ b/arch/arm/boot/dts/rk3288.dtsi
> @@ -43,6 +43,7 @@
> #include <dt-bindings/interrupt-controller/arm-gic.h>
> #include <dt-bindings/pinctrl/rockchip.h>
> #include <dt-bindings/clock/rk3288-cru.h>
> +#include <dt-bindings/power/rk3288-power.h>
> #include <dt-bindings/thermal/thermal.h>
> #include <dt-bindings/power/rk3288-power.h>
> #include <dt-bindings/soc/rockchip,boot-mode.h>
> @@ -227,6 +228,27 @@
> ports = <&vopl_out>, <&vopb_out>;
> };
>
> + gpu: mali@ffa30000 {
please sort nodes by address. ffa30000 should be placed below hdmi@ff980000
and above qos@ffaa0000 .
> + compatible = "arm,mali-t760", "arm,mali-midgard";
As indicated before I don't trust that a generic binding will work for
everything, so I would feel safer if we had a "rockchip,rk3288-mali" in
front for future purposes, making it a
compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
> + reg = <0xffa30000 0x10000>;
> + interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "job", "mmu", "gpu";
> + clocks = <&cru ACLK_GPU>;
> + operating-points = <
> + /* KHz uV */
> + 100000 950000
> + 200000 950000
> + 300000 1000000
> + 400000 1100000
> + 500000 1200000
> + 600000 1250000
> + >;
Wasn't there a wish for opp-v2 in a previous version?
Thanks
Heiko
> + power-domains = <&power RK3288_PD_GPU>;
> + status = "disabled";
> + };
> +
> sdmmc: dwmmc@ff0c0000 {
> compatible = "rockchip,rk3288-dw-mshc";
> max-frequency = <150000000>;
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2] ARM: dts: at91: sama5d2: add m_can nodes
From: Wenyou Yang @ 2017-04-19 8:46 UTC (permalink / raw)
To: Nicolas.Ferre, Alexandre Belloni, Rob Herring, Pawel Moll,
Mark Rutland, Ian Campbell, Kumar Gala, Russell King
Cc: devicetree, Wenyou Yang, Oliver Hartkopp, linux-kernel, linux-can,
Quentin Schulz, Wenyou Yang, linux-arm-kernel
Add nodes to support the Controller Area Network(M_CAN) on SAMA5D2.
The version of M_CAN IP core is 3.1.0 (CREL = 0x31040730).
As said in SAMA5D2 datasheet, the CAN clock is recommended to use
frequencies of 20, 40 or 80 MHz. To achieve these frequencies,
PMC GCLK3 must select the UPLLCK(480 MHz) as source clock and
divide by 24, 12, or 6. So, the "assigned-clock-rates" property
has three options: 20000000, 40000000, and 80000000.
The "assigned-clock-parents" property should be referred to utmi
fixedly.
The MSBs [bits 31:16] of the CAN Message RAM for CAN0 and CAN1 are
default configured in 0x00200000. To avoid conflict with SRAM map
for PM, change them to 0x00210000 in the AT91Bootstrap via setting
the CAN Memories Address-based Register(SFR_CAN) of SFR.
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
---
The patch is tested on SAMA5D2 Xplained and based on the patch set,
1. [PATCH v4 1/7] can: m_can: Disabled Interrupt Line 1
http://marc.info/?l=linux-can&m=149165343604033&w=2
Changes in v2:
- Configures 10 TX Event FIFO elements and 10 TX Buffers/FIFO slots,
because the TXE FIFO is needed to be configured.
- Configure the offset of Message RAM for CAN1 followed from CAN0's.
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 24 +++++++++++++
arch/arm/boot/dts/sama5d2.dtsi | 56 +++++++++++++++++++++++++++++
2 files changed, 80 insertions(+)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 9f7f8a7d8ff9..2f19b08dc226 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -257,6 +257,12 @@
status = "okay";
};
+ can0: can@f8054000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_can0_default>;
+ status = "okay";
+ };
+
uart3: serial@fc008000 {
atmel,use-dma-rx;
atmel,use-dma-tx;
@@ -321,6 +327,18 @@
bias-disable;
};
+ pinctrl_can0_default: can0_default {
+ pinmux = <PIN_PC10__CANTX0>,
+ <PIN_PC11__CANRX0>;
+ bias-disable;
+ };
+
+ pinctrl_can1_default: can1_default {
+ pinmux = <PIN_PC26__CANTX1>,
+ <PIN_PC27__CANRX1>;
+ bias-disable;
+ };
+
pinctrl_charger_chglev: charger_chglev {
pinmux = <PIN_PA12__GPIO>;
bias-disable;
@@ -468,6 +486,12 @@
};
};
+
+ can1: can@fc050000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_can1_default>;
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 22332be72140..383ca9307edf 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -762,6 +762,18 @@
atmel,clk-output-range = <0 83000000>;
};
+ can0_clk: can0_clk {
+ #clock-cells = <0>;
+ reg = <56>;
+ atmel,clk-output-range = <0 83000000>;
+ };
+
+ can1_clk: can1_clk {
+ #clock-cells = <0>;
+ reg = <57>;
+ atmel,clk-output-range = <0 83000000>;
+ };
+
classd_clk: classd_clk {
#clock-cells = <0>;
reg = <59>;
@@ -890,6 +902,18 @@
#clock-cells = <0>;
reg = <55>;
};
+
+ can0_gclk: can0_gclk {
+ #clock-cells = <0>;
+ reg = <56>;
+ atmel,clk-output-range = <0 80000000>;
+ };
+
+ can1_gclk: can1_gclk {
+ #clock-cells = <0>;
+ reg = <57>;
+ atmel,clk-output-range = <0 80000000>;
+ };
};
};
@@ -1144,6 +1168,22 @@
clocks = <&clk32k>;
};
+ can0: can@f8054000 {
+ compatible = "bosch,m_can";
+ reg = <0xf8054000 0x4000>, <0x210000 0x4000>;
+ reg-names = "m_can", "message_ram";
+ interrupts = <56 IRQ_TYPE_LEVEL_HIGH 7>,
+ <64 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-names = "int0", "int1";
+ clocks = <&can0_clk>, <&can0_gclk>;
+ clock-names = "hclk", "cclk";
+ assigned-clocks = <&can0_gclk>;
+ assigned-clock-parents = <&utmi>;
+ assigned-clock-rates = <40000000>;
+ bosch,mram-cfg = <0x0 0 0 32 0 0 10 10>;
+ status = "disabled";
+ };
+
spi1: spi@fc000000 {
compatible = "atmel,at91rm9200-spi";
reg = <0xfc000000 0x100>;
@@ -1305,6 +1345,22 @@
status = "okay";
};
+ can1: can@fc050000 {
+ compatible = "bosch,m_can";
+ reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
+ reg-names = "m_can", "message_ram";
+ interrupts = <57 IRQ_TYPE_LEVEL_HIGH 7>,
+ <65 IRQ_TYPE_LEVEL_HIGH 7>;
+ interrupt-names = "int0", "int1";
+ clocks = <&can1_clk>, <&can1_gclk>;
+ clock-names = "hclk", "cclk";
+ assigned-clocks = <&can1_gclk>;
+ assigned-clock-parents = <&utmi>;
+ assigned-clock-rates = <40000000>;
+ bosch,mram-cfg = <0x1100 0 0 32 0 0 10 10>;
+ status = "disabled";
+ };
+
chipid@fc069000 {
compatible = "atmel,sama5d2-chipid";
reg = <0xfc069000 0x8>;
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v5 4/4] arm64: dts: salvator-x: Add current sense amplifiers
From: Geert Uytterhoeven @ 2017-04-19 8:40 UTC (permalink / raw)
To: Jacopo Mondi
Cc: Wolfram Sang, Magnus Damm, Laurent Pinchart, Jonathan Cameron,
Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
Mark Rutland, linux-iio, Linux-Renesas,
devicetree@vger.kernel.org
In-Reply-To: <1491488454-22468-5-git-send-email-jacopo+renesas@jmondi.org>
Hi Jacopo,
On Thu, Apr 6, 2017 at 4:20 PM, Jacopo Mondi <jacopo+renesas@jmondi.org> wrote:
> [PATCH v5 4/4] arm64: dts: salvator-x: Add current sense amplifiers
This should be "arm64: dts: r8a7796: salvator-x: Add current sense amplifiers".
Perhaps Simon can just fix that himself while applying?
> Add device nodes for two Maxim max961x current sense amplifiers
> sensing VDD_08 and DVFS_08 lines.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH] iio: stm32 trigger: fix sampling_frequency read
From: Benjamin Gaignard @ 2017-04-19 8:30 UTC (permalink / raw)
To: Fabrice Gasnier
Cc: Jonathan Cameron, Russell King - ARM Linux, Rob Herring,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
linux-iio-u79uwXL29TY76Z2rM5mHXA, Mark Rutland, Maxime Coquelin,
Alexandre Torgue, Lars-Peter Clausen, Hartmut Knaack,
Peter Meerwald-Stadler, Benjamin GAIGNARD
In-Reply-To: <1491566026-302-1-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
2017-04-07 13:53 GMT+02:00 Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>:
> When prescaler (PSC) is 0, it means div factor is 1: counter clock
> frequency is equal to input clk / (PSC + 1).
> When reload value is 8 for example, counter counts 9 cycles, from 0 to 8.
> This is handled in frequency write routine, by writing respectively:
> - prescaler - 1 to PSC
> - reload value - 1 to ARR
> This fix does the opposite when reading the frequency from PSC and ARR:
> - prescaler is PSC + 1
> - reload value is ARR + 1
>
> Thus, PSC may be 0, depending on requested sampling frequency (div 1).
> In this case, reading freq wrongly reports 0, instead of computing and
> reporting correct value.
> Remove test on !psc and !arr.
>
> Small test on stm32f4 (example on tim1_trgo), before this fix:
> $ cd /sys/bus/iio/devices/triggerX
> $ echo 10000 > sampling_frequency
> $ cat sampling_frequency
> 0
>
> After this fix:
> $ echo 10000 > sampling_frequency
> $ cat sampling_frequency
> 10000
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
> ---
> drivers/iio/trigger/stm32-timer-trigger.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
> index 994b96d..5ee362b 100644
> --- a/drivers/iio/trigger/stm32-timer-trigger.c
> +++ b/drivers/iio/trigger/stm32-timer-trigger.c
> @@ -152,10 +152,10 @@ static ssize_t stm32_tt_read_frequency(struct device *dev,
> regmap_read(priv->regmap, TIM_PSC, &psc);
> regmap_read(priv->regmap, TIM_ARR, &arr);
>
> - if (psc && arr && (cr1 & TIM_CR1_CEN)) {
> + if (cr1 & TIM_CR1_CEN) {
> freq = (unsigned long long)clk_get_rate(priv->clk);
> - do_div(freq, psc);
> - do_div(freq, arr);
> + do_div(freq, psc + 1);
> + do_div(freq, arr + 1);
> }
>
> return sprintf(buf, "%d\n", (unsigned int)freq);
> --
> 1.9.1
>
Acked-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
^ permalink raw reply
* [PATCH 3/3] iio: tools: generic_buffer: increase trigger length
From: Eugen Hristev @ 2017-04-19 8:20 UTC (permalink / raw)
To: nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
lars-Qo5EllUWu/uELgA04lAiVw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA,
eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA
In-Reply-To: <1492590045-17329-1-git-send-email-eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Increased trigger length to 50 in order to cope with trigger names like
fc030000.adc-dev0-external-rising
Signed-off-by: Eugen Hristev <eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
---
tools/iio/iio_utils.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h
index 780f201..9d59771 100644
--- a/tools/iio/iio_utils.h
+++ b/tools/iio/iio_utils.h
@@ -13,7 +13,7 @@
#include <stdint.h>
/* Made up value to limit allocation sizes */
-#define IIO_MAX_NAME_LENGTH 30
+#define IIO_MAX_NAME_LENGTH 50
#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements"
#define FORMAT_TYPE_FILE "%s_type"
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/3] iio: adc: at91-sama5d2_adc: add hw trigger and buffer support
From: Eugen Hristev @ 2017-04-19 8:20 UTC (permalink / raw)
To: nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
lars-Qo5EllUWu/uELgA04lAiVw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA,
eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA
In-Reply-To: <1492590045-17329-1-git-send-email-eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Added support for the external hardware trigger on pin ADTRG,
integrated the three possible edge triggers into the subsystem
and created buffer management for data retrieval
Signed-off-by: Eugen Hristev <eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
---
drivers/iio/adc/at91-sama5d2_adc.c | 207 ++++++++++++++++++++++++++++++++++++-
1 file changed, 204 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index e10dca3..09a8c3d 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -23,8 +23,15 @@
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/slab.h>
+
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
#include <linux/regulator/consumer.h>
/* Control Register */
@@ -132,6 +139,17 @@
#define AT91_SAMA5D2_PRESSR 0xbc
/* Trigger Register */
#define AT91_SAMA5D2_TRGR 0xc0
+/* Mask for TRGMOD field of TRGR register */
+#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0)
+/* No trigger, only software trigger can start conversions */
+#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0
+/* Trigger Mode external trigger rising edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1
+/* Trigger Mode external trigger falling edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2
+/* Trigger Mode external trigger any edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3
+
/* Correction Select Register */
#define AT91_SAMA5D2_COSR 0xd0
/* Correction Value Register */
@@ -145,14 +163,20 @@
/* Version Register */
#define AT91_SAMA5D2_VERSION 0xfc
+#define AT91_SAMA5D2_HW_TRIG_CNT 3
+#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12
+#define AT91_SAMA5D2_DIFF_CHAN_CNT 6
+
#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \
{ \
.type = IIO_VOLTAGE, \
.channel = num, \
.address = addr, \
+ .scan_index = num, \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
+ .storagebits = 16, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
@@ -168,9 +192,11 @@
.channel = num, \
.channel2 = num2, \
.address = addr, \
+ .scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
+ .storagebits = 16, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
@@ -188,18 +214,26 @@ struct at91_adc_soc_info {
unsigned max_sample_rate;
};
+struct at91_adc_trigger {
+ char *name;
+ unsigned int trgmod_value;
+};
+
struct at91_adc_state {
void __iomem *base;
int irq;
struct clk *per_clk;
struct regulator *reg;
struct regulator *vref;
+ u16 *buffer;
int vref_uv;
const struct iio_chan_spec *chan;
bool conversion_done;
u32 conversion_value;
struct at91_adc_soc_info soc_info;
wait_queue_head_t wq_data_available;
+ struct iio_trigger **trig;
+ const struct at91_adc_trigger *trigger_list;
/*
* lock to prevent concurrent 'single conversion' requests through
* sysfs.
@@ -207,6 +241,21 @@ struct at91_adc_state {
struct mutex lock;
};
+static const struct at91_adc_trigger at91_adc_trigger_list[] = {
+ {
+ .name = "external-rising",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE,
+ },
+ {
+ .name = "external-falling",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL,
+ },
+ {
+ .name = "external-any",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY,
+ },
+};
+
static const struct iio_chan_spec at91_adc_channels[] = {
AT91_SAMA5D2_CHAN_SINGLE(0, 0x50),
AT91_SAMA5D2_CHAN_SINGLE(1, 0x54),
@@ -226,8 +275,141 @@ static const struct iio_chan_spec at91_adc_channels[] = {
AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68),
AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70),
AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78),
+ IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_SINGLE_CHAN_CNT
+ + AT91_SAMA5D2_DIFF_CHAN_CNT + 1),
};
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(indio);
+ u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR);
+ u8 bit;
+ int i;
+
+ /* clear TRGMOD */
+ status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK;
+
+ /* if we are disabling the trigger, it's enough to clear TRGMOD */
+ if (!state) {
+ at91_adc_writel(st, AT91_SAMA5D2_TRGR, status);
+ kfree(st->buffer);
+ return 0;
+ }
+
+ st->buffer = kmalloc(indio->scan_bytes, GFP_KERNEL);
+ if (!st->buffer)
+ return -ENOMEM;
+
+ for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT; i++) {
+ if (!strstr(trig->name, st->trigger_list[i].name)) {
+ status |= st->trigger_list[i].trgmod_value;
+ break;
+ }
+ }
+
+ /* setup hw trigger */
+ at91_adc_writel(st, AT91_SAMA5D2_TRGR, status);
+
+ for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) {
+ struct iio_chan_spec const *chan = indio->channels + bit;
+
+ at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
+ at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &at91_adc_configure_trigger,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
+ char *trigger_name)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name,
+ indio->id, trigger_name);
+ if (!trig)
+ return NULL;
+
+ trig->dev.parent = indio->dev.parent;
+ iio_trigger_set_drvdata(trig, indio);
+ trig->ops = &at91_adc_trigger_ops;
+
+ ret = devm_iio_trigger_register(&indio->dev, trig);
+
+ if (ret)
+ return NULL;
+
+ return trig;
+}
+
+static int at91_adc_trigger_init(struct iio_dev *indio)
+{
+ struct at91_adc_state *st = iio_priv(indio);
+ int i;
+
+ st->trig = devm_kzalloc(&indio->dev,
+ AT91_SAMA5D2_HW_TRIG_CNT * sizeof(*st->trig),
+ GFP_KERNEL);
+
+ if (!st->trig) {
+ dev_err(&indio->dev, "could not allocate trig list memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT; i++) {
+ st->trig[i] = at91_adc_allocate_trigger(indio,
+ st->trigger_list[i].name);
+ if (!st->trig[i]) {
+ dev_err(&indio->dev,
+ "could not allocate trigger %d\n", i);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio = pf->indio_dev;
+ struct at91_adc_state *st = iio_priv(indio);
+ int i = 0;
+ u8 bit;
+
+ for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) {
+ struct iio_chan_spec const *chan = indio->channels + bit;
+
+ st->buffer[i] = at91_adc_readl(st, chan->address);
+ i++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio, st->buffer, pf->timestamp);
+
+ iio_trigger_notify_done(indio->trig);
+
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, AT91_SAMA5D2_LCDR);
+
+ enable_irq(st->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int at91_adc_buffer_init(struct iio_dev *indio)
+{
+ return devm_iio_triggered_buffer_setup(&indio->dev, indio,
+ &iio_pollfunc_store_time,
+ &at91_adc_trigger_handler, NULL);
+}
+
static unsigned at91_adc_startup_time(unsigned startup_time_min,
unsigned adc_clk_khz)
{
@@ -293,14 +475,19 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private)
u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
- if (status & imr) {
+ if (!(status & imr))
+ return IRQ_NONE;
+
+ if (iio_buffer_enabled(indio)) {
+ disable_irq_nosync(irq);
+ iio_trigger_poll(indio->trig);
+ } else {
st->conversion_value = at91_adc_readl(st, st->chan->address);
st->conversion_done = true;
wake_up_interruptible(&st->wq_data_available);
- return IRQ_HANDLED;
}
- return IRQ_NONE;
+ return IRQ_HANDLED;
}
static int at91_adc_read_raw(struct iio_dev *indio_dev,
@@ -406,6 +593,8 @@ static int at91_adc_probe(struct platform_device *pdev)
st = iio_priv(indio_dev);
+ st->trigger_list = at91_adc_trigger_list;
+
ret = of_property_read_u32(pdev->dev.of_node,
"atmel,min-sample-rate-hz",
&st->soc_info.min_sample_rate);
@@ -499,6 +688,18 @@ static int at91_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
+ ret = at91_adc_buffer_init(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "couldn't initialize the buffer.\n");
+ goto per_clk_disable_unprepare;
+ }
+
+ ret = at91_adc_trigger_init(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "couldn't setup the triggers.\n");
+ goto per_clk_disable_unprepare;
+ }
+
ret = iio_device_register(indio_dev);
if (ret < 0)
goto per_clk_disable_unprepare;
--
2.7.4
^ permalink raw reply related
* [PATCH 1/3] ARM: dts: at91: sama5d2_xplained: enable ADTRG pin
From: Eugen Hristev @ 2017-04-19 8:20 UTC (permalink / raw)
To: nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
lars-Qo5EllUWu/uELgA04lAiVw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA,
eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA
In-Reply-To: <1492590045-17329-1-git-send-email-eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
Enable pinctrl for ADTRG pin (PD31) for ADC hardware trigger support.
Signed-off-by: Eugen Hristev <eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
---
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 0bef9e0..04754b1 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -303,7 +303,7 @@
vddana-supply = <&vdd_3v3_lp_reg>;
vref-supply = <&vdd_3v3_lp_reg>;
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_adc_default>;
+ pinctrl-0 = <&pinctrl_adc_default &pinctrl_adtrg_default>;
status = "okay";
};
@@ -322,6 +322,20 @@
bias-disable;
};
+ /*
+ * The ADTRG pin can work on any edge type.
+ * In here it's being pulled up, so need to
+ * connect it to ground to get an edge e.g.
+ * Trigger can be configured on falling, rise
+ * or any edge, and the pull-up can be changed
+ * to pull-down or left floating according to
+ * needs.
+ */
+ pinctrl_adtrg_default: adtrg_default {
+ pinmux = <PIN_PD31__ADTRG>;
+ bias-pull-up;
+ };
+
pinctrl_charger_chglev: charger_chglev {
pinmux = <PIN_PA12__GPIO>;
bias-disable;
--
2.7.4
^ permalink raw reply related
* [PATCH 0/3] iio: adc: sama5d2_adc hw triggers and buffers
From: Eugen Hristev @ 2017-04-19 8:20 UTC (permalink / raw)
To: nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
lars-Qo5EllUWu/uELgA04lAiVw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA,
eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA
This patch implements the hardware triggers support and
buffer management for sama5d2.
The DT modifications ( [PATCH 1/3] ARM: dts: at91: sama5d2_xplained:
enable ADTRG pin) are for demonstration purposes of the feature,
setting the pinctrl for the ADC hw trigger pin,should go through at91
maintainers.
I also increased the buffer size for the trigger name in the generic_buffer
application to cope with longer names and avoid stack smashing problem.
This is in patch [PATCH 3/3] iio: tools: generic_buffer: increase trigger length
Eugen Hristev (3):
ARM: dts: at91: sama5d2_xplained: enable ADTRG pin
iio: adc: at91-sama5d2_adc: add hw trigger and buffer support
iio: tools: generic_buffer: increase trigger length
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 16 ++-
drivers/iio/adc/at91-sama5d2_adc.c | 208 +++++++++++++++++++++++++++-
tools/iio/iio_utils.h | 2 +-
3 files changed, 220 insertions(+), 6 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH v3 5/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker
In-Reply-To: <cover.1492588180.git.guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
From: Enric Balletbo i Serra <enric.balletbo-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Add reference to the Mali GPU device tree node on rk3288-veyron.
Tested on Minnie and Jerry boards.
Signed-off-by: Enric Balletbo i Serra <enric.balletbo-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Signed-off-by: Guillaume Tucker <guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
---
arch/arm/boot/dts/rk3288-veyron.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 5d1eb0a25827..9847d5c6db3b 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -447,6 +447,11 @@
status = "okay";
};
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
+
&wdt {
status = "okay";
};
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 4/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
linux-rockchip, John Reitan, Enric Balletbo i Serra,
linux-arm-kernel
In-Reply-To: <cover.1492588180.git.guillaume.tucker@collabora.com>
Add reference to the Mali GPU device tree node on rk3288-firefly.
Tested on Firefly board.
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288-firefly.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 10793ac18599..f520589493b4 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -594,3 +594,8 @@
&wdt {
status = "okay";
};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v3 3/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
linux-rockchip, John Reitan, Enric Balletbo i Serra,
linux-arm-kernel
In-Reply-To: <cover.1492588180.git.guillaume.tucker@collabora.com>
Add reference to the Mali GPU device tree node on the
rk3288-rock2-som platform. Tested on a Radxa Rock2 Square board.
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 1c0bbc9b928b..f694867fa46a 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -301,3 +301,8 @@
&wdt {
status = "okay";
};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v3 2/5] ARM: dts: rockchip: add ARM Mali GPU node for rk3288
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
Guillaume Tucker
In-Reply-To: <cover.1492588180.git.guillaume.tucker@collabora.com>
Add Mali GPU device tree node for the rk3288 SoC, with devfreq
opp table.
Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288.dtsi | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index df8a0dbe9d91..187eed528f83 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -43,6 +43,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/clock/rk3288-cru.h>
+#include <dt-bindings/power/rk3288-power.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/power/rk3288-power.h>
#include <dt-bindings/soc/rockchip,boot-mode.h>
@@ -227,6 +228,27 @@
ports = <&vopl_out>, <&vopb_out>;
};
+ gpu: mali@ffa30000 {
+ compatible = "arm,mali-t760", "arm,mali-midgard";
+ reg = <0xffa30000 0x10000>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "job", "mmu", "gpu";
+ clocks = <&cru ACLK_GPU>;
+ operating-points = <
+ /* KHz uV */
+ 100000 950000
+ 200000 950000
+ 300000 1000000
+ 400000 1100000
+ 500000 1200000
+ 600000 1250000
+ >;
+ power-domains = <&power RK3288_PD_GPU>;
+ status = "disabled";
+ };
+
sdmmc: dwmmc@ff0c0000 {
compatible = "rockchip,rk3288-dw-mshc";
max-frequency = <150000000>;
--
2.11.0
^ permalink raw reply related
* [PATCH v3 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker
In-Reply-To: <cover.1492588180.git.guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
The ARM Mali Midgard GPU family is present in a number of SoCs
from many different vendors such as Samsung Exynos and Rockchip.
Import the device tree bindings documentation from the r16p0
release of the Mali Midgard GPU kernel driver:
https://developer.arm.com/-/media/Files/downloads/mali-drivers/kernel/mali-midgard-gpu/TX011-SW-99002-r16p0-00rel0.tgz
The "compatible" property strings have been redesigned to explicitly
list all the Mali Midgard GPU types and include optional vendor ones.
The "clock-names" property has been dropped as only one clock is used
by the Mali Midgard driver which now needs to call clk_get with NULL.
The "interrupt-names" property values have been converted to
lower-case: "job", "mmu" and "gpu".
The following optional bindings have been omitted in this initial
version as they are only used in very specific cases:
* snoop_enable_smc
* snoop_disable_smc
* jm_config
* power_model
* system-coherency
* ipa-model
The example has been simplified accordingly.
The copyright and GPL licence header has been removed as deemed not
necessary.
CC: John Reitan <john.reitan-5wv7dgnIgG8@public.gmane.org>
Tested-by: Enric Balletbo i Serra <enric.balletbo-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Signed-off-by: Guillaume Tucker <guillaume.tucker-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
---
.../devicetree/bindings/gpu/arm,mali-midgard.txt | 57 ++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
new file mode 100644
index 000000000000..917c4f8d178f
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
@@ -0,0 +1,57 @@
+ARM Mali Midgard GPU
+====================
+
+Required properties:
+
+- compatible :
+ * Must be one of the following:
+ + "arm,mali-t60x"
+ + "arm,mali-t62x"
+ + "arm,mali-t720"
+ + "arm,mali-t760"
+ + "arm,mali-t820"
+ + "arm,mali-t830"
+ + "arm,mali-t860"
+ + "arm,mali-t880"
+ * And, optionally, one of the vendor specific compatible:
+ + "amlogic,meson-gxm-mali"
+
+- reg : Physical base address of the device and length of the register area.
+
+- interrupts : Contains the three IRQ lines required by Mali Midgard devices.
+
+- interrupt-names : Contains the names of IRQ resources in the order they were
+ provided in the interrupts property. Must contain: "job", "mmu", "gpu".
+
+
+Optional properties:
+
+- clocks : Phandle to clock for the Mali Midgard device.
+
+- mali-supply : Phandle to regulator for the Mali device. Refer to
+ Documentation/devicetree/bindings/regulator/regulator.txt for details.
+
+- operating-points : Refer to Documentation/devicetree/bindings/power/opp.txt
+ for details.
+
+
+Example for a Mali-T602:
+
+gpu@fc010000 {
+ compatible = "arm,mali-t60x", "arm,mali-midgard";
+ reg = <0xfc010000 0x4000>;
+ interrupts = <0 36 4>, <0 37 4>, <0 38 4>;
+ interrupt-names = "job", "mmu", "gpu";
+ clocks = <&pclk_mali>;
+ mali-supply = <&vdd_mali>;
+ operating-points = <
+ /* KHz uV */
+ 533000 1250000,
+ 450000 1150000,
+ 400000 1125000,
+ 350000 1075000,
+ 266000 1025000,
+ 160000 925000,
+ 100000 912500,
+ >;
+};
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 0/5] Add ARM Mali Midgard device tree bindings and gpu node for rk3288
From: Guillaume Tucker @ 2017-04-19 8:06 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker
The ARM Mali Midgard GPU kernel driver is only available
out-of-tree and is not going to be merged in its current form.
However, it would be useful to have its device tree bindings
merged. In particular, this would enable distributions to create
working driver packages (dkms...) without having to patch the
kernel.
The bindings for the earlier Mali Utgard GPU family have already
been merged, so this is essentially the same scenario but for
newer GPUs (Mali-T604 ~ Mali-T880).
This series of patches first imports the bindings from the latest
driver release with some clean-up then adds a gpu node for the
rk3288 SoC. This was successfully tested on Radxa Rock2 Square,
Firefly, Veyron Minnie and Jerry boards board using Mali kernel
driver r16p0 and r12p0 user-space binary.
Changes since v1:
- enabled gpu on rk3288-veyron boards
Changes since v2:
- removed "clk-names" property and "clk_mali" name
- converted values of "interrupt-names" property to
lower-case: "job", "mmu" and "gpu"
- replaced dt compatible strings with list of all Midgard GPU variants and
optional vendors
- cleaned up gpu node example
Enric Balletbo i Serra (1):
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
Guillaume Tucker (4):
dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
ARM: dts: rockchip: add ARM Mali GPU node for rk3288
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
.../devicetree/bindings/gpu/arm,mali-midgard.txt | 57 ++++++++++++++++++++++
arch/arm/boot/dts/rk3288-firefly.dtsi | 5 ++
arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 ++
arch/arm/boot/dts/rk3288-veyron.dtsi | 5 ++
arch/arm/boot/dts/rk3288.dtsi | 22 +++++++++
5 files changed, 94 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 2/2] clk: rockchip: fix the incorrect id for clk_testout1
From: Eddie Cai @ 2017-04-19 7:30 UTC (permalink / raw)
To: robh+dt, mark.rutland, heiko, zhengxing, zyw, hl, mturquette,
sboyd
Cc: devicetree, linux-kernel, linux-clk, linux-arm-kernel,
linux-rockchip, Eddie Cai
In-Reply-To: <20170419073023.1921-1-eddie.cai.linux@gmail.com>
the id for clk_testout1 is wrong, so correct it.
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
---
drivers/clk/rockchip/clk-rk3399.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 73121b14..322611d 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -1066,7 +1066,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
/* cif_testout */
MUX(0, "clk_testout1_pll_src", mux_pll_src_cpll_gpll_npll_p, 0,
RK3399_CLKSEL_CON(38), 6, 2, MFLAGS),
- COMPOSITE(0, "clk_testout1", mux_clk_testout1_p, 0,
+ COMPOSITE(SCLK_TESTCLKOUT1, "clk_testout1", mux_clk_testout1_p, 0,
RK3399_CLKSEL_CON(38), 5, 1, MFLAGS, 0, 5, DFLAGS,
RK3399_CLKGATE_CON(13), 14, GFLAGS),
--
2.10.2
^ permalink raw reply related
* [PATCH 1/2] clk: rockchip: add SCLK_TESTCLKOUT1 id
From: Eddie Cai @ 2017-04-19 7:30 UTC (permalink / raw)
To: robh+dt, mark.rutland, heiko, zhengxing, zyw, hl, mturquette,
sboyd
Cc: devicetree, linux-kernel, linux-clk, linux-arm-kernel,
linux-rockchip, Eddie Cai
In-Reply-To: <20170419073023.1921-1-eddie.cai.linux@gmail.com>
we use SCLK_TESTCLKOUT1 for camera, so add this id.
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
---
include/dt-bindings/clock/rk3399-cru.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 220a60f..c505a98 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -132,6 +132,7 @@
#define SCLK_RMII_SRC 166
#define SCLK_PCIEPHY_REF100M 167
#define SCLK_DDRC 168
+#define SCLK_TESTCLKOUT1 169
#define DCLK_VOP0 180
#define DCLK_VOP1 181
--
2.10.2
^ permalink raw reply related
* [PATCH 0/2] correct id for clk_testout1
From: Eddie Cai @ 2017-04-19 7:30 UTC (permalink / raw)
To: robh+dt, mark.rutland, heiko, zhengxing, zyw, hl, mturquette,
sboyd
Cc: devicetree, linux-kernel, linux-clk, linux-arm-kernel,
linux-rockchip, Eddie Cai
the id for clk_testout1 is wrong, it's time to correct it.
Eddie Cai (2):
clk: rockchip: add SCLK_TESTCLKOUT1 id
clk: rockchip: fix the incorrect id for clk_testout1
drivers/clk/rockchip/clk-rk3399.c | 2 +-
include/dt-bindings/clock/rk3399-cru.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
--
2.10.2
^ permalink raw reply
* [PATCH 2/2] mtd: nand: denali: show how to use generic helpers (do not apply)
From: Masahiro Yamada @ 2017-04-19 7:06 UTC (permalink / raw)
To: linux-mtd
Cc: Mark Rutland, Boris Brezillon, Richard Weinberger, Dinh Nguyen,
Masahiro Yamada, Artem Bityutskiy, Cyrille Pitchen, linux-kernel,
Marek Vasut, devicetree, Rob Herring, Masami Hiramatsu,
Chuanxiao Dong, Jassi Brar, Brian Norris, Enrico Jorns,
David Woodhouse, Graham Moore
In-Reply-To: <1492585618-13655-1-git-send-email-yamada.masahiro@socionext.com>
This patch shows how the previous commit is used in a driver.
Please do not apply this.
(This is applicable on linux next-20170418)
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
.../devicetree/bindings/mtd/denali-nand.txt | 17 +
drivers/mtd/nand/denali.c | 1693 +++++++++-----------
drivers/mtd/nand/denali.h | 297 ++--
drivers/mtd/nand/denali_dt.c | 29 +-
drivers/mtd/nand/denali_pci.c | 11 +-
5 files changed, 969 insertions(+), 1078 deletions(-)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index e593bbe..0b08ea5 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -3,10 +3,27 @@
Required properties:
- compatible : should be one of the following:
"altr,socfpga-denali-nand" - for Altera SOCFPGA
+ "socionext,uniphier-denali-nand-v5a" - for Socionext UniPhier (v5a)
+ "socionext,uniphier-denali-nand-v5b" - for Socionext UniPhier (v5b)
- reg : should contain registers location and length for data and reg.
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
- interrupts : The interrupt number.
+Optional properties:
+ - nand-ecc-step-size: must be 512 or 1024. If not specified, default to:
+ 512 for "altr,socfpga-denali-nand"
+ 1024 for "socionext,uniphier-denali-nand-v5a"
+ 1024 for "socionext,uniphier-denali-nand-v5b"
+ see nand.txt for details.
+ - nand-ecc-strength: see nand.txt for details. Available values are:
+ 8, 15 for "altr,socfpga-denali-nand"
+ 8, 16, 24 for "socionext,uniphier-denali-nand-v5a"
+ 8, 16 for "socionext,uniphier-denali-nand-v5b"
+ - nand-ecc-maximize: see nand.txt for details
+
+Note:
+Either nand-ecc-strength or nand-ecc-maximize should be specified.
+
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 16634df..15fefd9 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -23,52 +23,21 @@
#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "denali.h"
MODULE_LICENSE("GPL");
-/*
- * We define a module parameter that allows the user to override
- * the hardware and decide what timing mode should be used.
- */
-#define NAND_DEFAULT_TIMINGS -1
-
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
-module_param(onfi_timing_mode, int, S_IRUGO);
-MODULE_PARM_DESC(onfi_timing_mode,
- "Overrides default ONFI setting. -1 indicates use default timings");
-
#define DENALI_NAND_NAME "denali-nand"
/*
- * We define a macro here that combines all interrupts this driver uses into
- * a single constant value, for convenience.
- */
-#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
- INTR__ECC_TRANSACTION_DONE | \
- INTR__ECC_ERR | \
- INTR__PROGRAM_FAIL | \
- INTR__LOAD_COMP | \
- INTR__PROGRAM_COMP | \
- INTR__TIME_OUT | \
- INTR__ERASE_FAIL | \
- INTR__RST_COMP | \
- INTR__ERASE_COMP)
-
-/*
* indicates whether or not the internal value for the flash bank is
* valid or not
*/
#define CHIP_SELECT_INVALID -1
/*
- * This macro divides two integers and rounds fractional values up
- * to the nearest integer value.
- */
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
-
-/*
* this macro allows us to convert from an MTD structure to our own
* device context (denali) structure.
*/
@@ -85,8 +54,7 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
#define MAIN_ACCESS 0x42
#define MAIN_SPARE_ACCESS 0x43
-#define DENALI_READ 0
-#define DENALI_WRITE 0x100
+#define DENALI_NR_BANKS 4
/*
* this is a helper macro that allows us to
@@ -94,13 +62,13 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
*/
#define BANK(x) ((x) << 24)
-/* forward declarations */
-static void clear_interrupts(struct denali_nand_info *denali);
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
- uint32_t irq_mask);
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask);
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
+/*
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
+ * clk_x is an integral multiple N of the core clk. The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
+ * to the largest value to make it work with any possible configuration.
+ */
+#define DENALI_CLK_X_MULT 6
/*
* Certain operations for the denali NAND controller use an indexed mode to
@@ -115,595 +83,244 @@ static void index_addr(struct denali_nand_info *denali,
iowrite32(data, denali->flash_mem + 0x10);
}
-/* Perform an indexed read of the device */
-static void index_addr_read_data(struct denali_nand_info *denali,
- uint32_t address, uint32_t *pdata)
-{
- iowrite32(address, denali->flash_mem);
- *pdata = ioread32(denali->flash_mem + 0x10);
-}
-
/*
- * We need to buffer some data for some of the NAND core routines.
- * The operations manage buffering that data.
+ * Use the configuration feature register to determine the maximum number of
+ * banks that the hardware supports.
*/
-static void reset_buf(struct denali_nand_info *denali)
-{
- denali->buf.head = denali->buf.tail = 0;
-}
-
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
-{
- denali->buf.buf[denali->buf.tail++] = byte;
-}
-
-/* reads the status of the device */
-static void read_status(struct denali_nand_info *denali)
+static void detect_max_banks(struct denali_nand_info *denali)
{
- uint32_t cmd;
+ uint32_t features = ioread32(denali->flash_reg + FEATURES);
- /* initialize the data buffer to store status */
- reset_buf(denali);
+ denali->max_banks = 1 << (features & FEATURES__N_BANKS);
- cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
- if (cmd)
- write_byte_to_buf(denali, NAND_STATUS_WP);
- else
- write_byte_to_buf(denali, 0);
+ /* the encoding changed from rev 5.0 to 5.1 */
+ if (denali->revision < 0x0501)
+ denali->max_banks <<= 1;
}
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
+static void denali_enable_irq(struct denali_nand_info *denali)
{
- uint32_t irq_status;
- uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
-
- clear_interrupts(denali);
-
- iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
- irq_status = wait_for_irq(denali, irq_mask);
+ int i;
- if (irq_status & INTR__TIME_OUT)
- dev_err(denali->dev, "reset bank failed.\n");
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(U32_MAX, denali->flash_reg + INTR_EN(i));
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->flash_reg + GLOBAL_INT_ENABLE);
}
-/* Reset the flash controller */
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
+static void denali_disable_irq(struct denali_nand_info *denali)
{
int i;
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- for (i = 0; i < denali->max_banks; i++) {
- iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
- while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
- (INTR__RST_COMP | INTR__TIME_OUT)))
- cpu_relax();
- if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
- INTR__TIME_OUT)
- dev_dbg(denali->dev,
- "NAND Reset operation timed out on bank %d\n", i);
- }
-
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- return PASS;
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(0, denali->flash_reg + INTR_EN(i));
+ iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
}
-/*
- * this routine calculates the ONFI timing values for a given mode and
- * programs the clocking register accordingly. The mode is determined by
- * the get_onfi_nand_para routine.
- */
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
- uint16_t mode)
+static void denali_clear_irq(struct denali_nand_info *denali,
+ int bank, uint32_t irq_status)
{
- uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
- uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
- uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
- uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
- uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
- uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
- uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
- uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
- uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
- uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
-
- uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
- uint16_t dv_window = 0;
- uint16_t en_lo, en_hi;
- uint16_t acc_clks;
- uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
-
- en_lo = CEIL_DIV(Trp[mode], CLK_X);
- en_hi = CEIL_DIV(Treh[mode], CLK_X);
-#if ONFI_BLOOM_TIME
- if ((en_hi * CLK_X) < (Treh[mode] + 2))
- en_hi++;
-#endif
-
- if ((en_lo + en_hi) * CLK_X < Trc[mode])
- en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
-
- if ((en_lo + en_hi) < CLK_MULTI)
- en_lo += CLK_MULTI - en_lo - en_hi;
-
- while (dv_window < 8) {
- data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
-
- data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
-
- data_invalid = data_invalid_rhoh < data_invalid_rloh ?
- data_invalid_rhoh : data_invalid_rloh;
-
- dv_window = data_invalid - Trea[mode];
-
- if (dv_window < 8)
- en_lo++;
- }
-
- acc_clks = CEIL_DIV(Trea[mode], CLK_X);
-
- while (acc_clks * CLK_X - Trea[mode] < 3)
- acc_clks++;
-
- if (data_invalid - acc_clks * CLK_X < 2)
- dev_warn(denali->dev, "%s, Line %d: Warning!\n",
- __FILE__, __LINE__);
-
- addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
- re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
- re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
- we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
- cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
- if (cs_cnt == 0)
- cs_cnt = 1;
-
- if (Tcea[mode]) {
- while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
- cs_cnt++;
- }
-
-#if MODE5_WORKAROUND
- if (mode == 5)
- acc_clks = 5;
-#endif
-
- /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
- if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
- ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
- acc_clks = 6;
-
- iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
- iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
- iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
- iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
- iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
- iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
+ /* write one to clear bits */
+ iowrite32(irq_status, denali->flash_reg + INTR_STATUS(bank));
}
-/* queries the NAND device to see what ONFI modes it supports. */
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
+static void denali_clear_irq_all(struct denali_nand_info *denali)
{
int i;
- /*
- * we needn't to do a reset here because driver has already
- * reset all the banks before
- */
- if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- ONFI_TIMING_MODE__VALUE))
- return FAIL;
-
- for (i = 5; i > 0; i--) {
- if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- (0x01 << i))
- break;
- }
-
- nand_onfi_timing_set(denali, i);
-
- /*
- * By now, all the ONFI devices we know support the page cache
- * rw feature. So here we enable the pipeline_rw_ahead feature
- */
- /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
- /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */
-
- return PASS;
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ denali_clear_irq(denali, i, U32_MAX);
}
-static void get_samsung_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
+static irqreturn_t denali_isr(int irq, void *dev_id)
{
- if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
- /* Set timing register values according to datasheet */
- iowrite32(5, denali->flash_reg + ACC_CLKS);
- iowrite32(20, denali->flash_reg + RE_2_WE);
- iowrite32(12, denali->flash_reg + WE_2_RE);
- iowrite32(14, denali->flash_reg + ADDR_2_DATA);
- iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
- }
-}
+ struct denali_nand_info *denali = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ uint32_t irq_status;
+ int i;
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
-{
- /*
- * Workaround to fix a controller bug which reports a wrong
- * spare area size for some kind of Toshiba NAND device
- */
- if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
- (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
- iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-}
+ spin_lock(&denali->irq_lock);
-static void get_hynix_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
-{
- switch (device_id) {
- case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
- case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
- iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
- iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
- iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
- iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
- break;
- default:
- dev_warn(denali->dev,
- "Unknown Hynix NAND (Device ID: 0x%x).\n"
- "Will use default parameter values instead.\n",
- device_id);
- }
-}
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
+ irq_status = ioread32(denali->flash_reg + INTR_STATUS(i));
+ if (irq_status)
+ ret = IRQ_HANDLED;
-/*
- * determines how many NAND chips are connected to the controller. Note for
- * Intel CE4100 devices we don't support more than one device.
- */
-static void find_valid_banks(struct denali_nand_info *denali)
-{
- uint32_t id[denali->max_banks];
- int i;
+ denali_clear_irq(denali, i, irq_status);
- denali->total_used_banks = 1;
- for (i = 0; i < denali->max_banks; i++) {
- index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
- index_addr(denali, MODE_11 | (i << 24) | 1, 0);
- index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
+ if (i != denali->flash_bank)
+ continue;
- dev_dbg(denali->dev,
- "Return 1st ID for bank[%d]: %x\n", i, id[i]);
+ denali->irq_status |= irq_status;
- if (i == 0) {
- if (!(id[i] & 0x0ff))
- break; /* WTF? */
- } else {
- if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
- denali->total_used_banks++;
- else
- break;
- }
- }
-
- if (denali->platform == INTEL_CE4100) {
- /*
- * Platform limitations of the CE4100 device limit
- * users to a single chip solution for NAND.
- * Multichip support is not enabled.
- */
- if (denali->total_used_banks != 1) {
- dev_err(denali->dev,
- "Sorry, Intel CE4100 only supports a single NAND device.\n");
- BUG();
- }
+ if (denali->irq_status & denali->irq_mask)
+ complete(&denali->complete);
}
- dev_dbg(denali->dev,
- "denali->total_used_banks: %d\n", denali->total_used_banks);
-}
-
-/*
- * Use the configuration feature register to determine the maximum number of
- * banks that the hardware supports.
- */
-static void detect_max_banks(struct denali_nand_info *denali)
-{
- uint32_t features = ioread32(denali->flash_reg + FEATURES);
- denali->max_banks = 1 << (features & FEATURES__N_BANKS);
+ spin_unlock(&denali->irq_lock);
- /* the encoding changed from rev 5.0 to 5.1 */
- if (denali->revision < 0x0501)
- denali->max_banks <<= 1;
+ return ret;
}
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_nand_info *denali)
{
- uint16_t status = PASS;
- uint32_t id_bytes[8], addr;
- uint8_t maf_id, device_id;
- int i;
-
- /*
- * Use read id method to get device ID and other params.
- * For some NAND chips, controller can't report the correct
- * device ID by reading from DEVICE_ID register
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, 0);
- for (i = 0; i < 8; i++)
- index_addr_read_data(denali, addr | 2, &id_bytes[i]);
- maf_id = id_bytes[0];
- device_id = id_bytes[1];
-
- if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
- ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
- if (FAIL == get_onfi_nand_para(denali))
- return FAIL;
- } else if (maf_id == 0xEC) { /* Samsung NAND */
- get_samsung_nand_para(denali, device_id);
- } else if (maf_id == 0x98) { /* Toshiba NAND */
- get_toshiba_nand_para(denali);
- } else if (maf_id == 0xAD) { /* Hynix NAND */
- get_hynix_nand_para(denali, device_id);
- }
-
- dev_info(denali->dev,
- "Dump timing register values:\n"
- "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
- "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
- "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
- ioread32(denali->flash_reg + ACC_CLKS),
- ioread32(denali->flash_reg + RE_2_WE),
- ioread32(denali->flash_reg + RE_2_RE),
- ioread32(denali->flash_reg + WE_2_RE),
- ioread32(denali->flash_reg + ADDR_2_DATA),
- ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
- ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
- ioread32(denali->flash_reg + CS_SETUP_CNT));
-
- find_valid_banks(denali);
-
- /*
- * If the user specified to override the default timings
- * with a specific ONFI mode, we apply those changes here.
- */
- if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
- nand_onfi_timing_set(denali, onfi_timing_mode);
+ unsigned long flags;
- return status;
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ denali->irq_status = 0;
+ denali->irq_mask = 0;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
}
-static void denali_set_intr_modes(struct denali_nand_info *denali,
- uint16_t INT_ENABLE)
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+ uint32_t irq_mask)
{
- if (INT_ENABLE)
- iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
- else
- iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
-}
+ unsigned long time_left, flags;
+ uint32_t irq_status;
-/*
- * validation function to verify that the controlling software is making
- * a valid request
- */
-static inline bool is_flash_bank_valid(int flash_bank)
-{
- return flash_bank >= 0 && flash_bank < 4;
-}
+ spin_lock_irqsave(&denali->irq_lock, flags);
-static void denali_irq_init(struct denali_nand_info *denali)
-{
- uint32_t int_mask;
- int i;
+ irq_status = denali->irq_status;
- /* Disable global interrupts */
- denali_set_intr_modes(denali, false);
+ if (irq_mask & irq_status) {
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+ return irq_status;
+ }
- int_mask = DENALI_IRQ_ALL;
+ denali->irq_mask = irq_mask;
+ reinit_completion(&denali->complete);
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
- /* Clear all status bits */
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
+ time_left = wait_for_completion_timeout(&denali->complete,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+ denali->irq_mask);
+ return 0;
+ }
- denali_irq_enable(denali, int_mask);
+ return denali->irq_status;
}
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
{
- denali_set_intr_modes(denali, false);
-}
+ unsigned long flags;
+ uint32_t irq_status;
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask)
-{
- int i;
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ irq_status = denali->irq_status;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
+ return irq_status;
}
/*
- * This function only returns when an interrupt that this driver cares about
- * occurs. This is to reduce the overhead of servicing interrupts
+ * This helper function setups the registers for ECC and whether or not
+ * the spare area will be transferred.
*/
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
+static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
+ bool transfer_spare)
{
- return read_interrupt_status(denali) & DENALI_IRQ_ALL;
+ int ecc_en_flag, transfer_spare_flag;
+
+ /* set ECC, transfer spare bits if needed */
+ ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0;
+ transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
+
+ /* Enable spare area/ECC per user's request. */
+ iowrite32(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
+ iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
}
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
-static inline void clear_interrupt(struct denali_nand_info *denali,
- uint32_t irq_mask)
+static uint8_t denali_read_byte(struct mtd_info *mtd)
{
- uint32_t intr_status_reg;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
- iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
+ return ioread32(denali->flash_mem + 0x10);
}
-static void clear_interrupts(struct denali_nand_info *denali)
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
{
- uint32_t status;
-
- spin_lock_irq(&denali->irq_lock);
-
- status = read_interrupt_status(denali);
- clear_interrupt(denali, status);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
- denali->irq_status = 0x0;
- spin_unlock_irq(&denali->irq_lock);
+ index_addr(denali, MODE_11 | BANK(denali->flash_bank) | 2, byte);
}
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- uint32_t intr_status_reg;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int i;
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
- return ioread32(denali->flash_reg + intr_status_reg);
+ for (i = 0; i < len; i++)
+ buf[i] = ioread32(denali->flash_mem + 0x10);
}
-/*
- * This is the interrupt service routine. It handles all interrupts
- * sent to this device. Note that on CE4100, this is a shared interrupt.
- */
-static irqreturn_t denali_isr(int irq, void *dev_id)
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct denali_nand_info *denali = dev_id;
- uint32_t irq_status;
- irqreturn_t result = IRQ_NONE;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int i;
- spin_lock(&denali->irq_lock);
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
- /* check to see if a valid NAND chip has been selected. */
- if (is_flash_bank_valid(denali->flash_bank)) {
- /*
- * check to see if controller generated the interrupt,
- * since this is a shared interrupt
- */
- irq_status = denali_irq_detected(denali);
- if (irq_status != 0) {
- /* handle interrupt */
- /* first acknowledge it */
- clear_interrupt(denali, irq_status);
- /*
- * store the status in the device context for someone
- * to read
- */
- denali->irq_status |= irq_status;
- /* notify anyone who cares that it happened */
- complete(&denali->complete);
- /* tell the OS that we've handled this */
- result = IRQ_HANDLED;
- }
- }
- spin_unlock(&denali->irq_lock);
- return result;
+ for (i = 0; i < len; i++)
+ iowrite32(buf[i], denali->flash_mem + 0x10);
}
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
- unsigned long comp_res;
- uint32_t intr_status;
- unsigned long timeout = msecs_to_jiffies(1000);
-
- do {
- comp_res =
- wait_for_completion_timeout(&denali->complete, timeout);
- spin_lock_irq(&denali->irq_lock);
- intr_status = denali->irq_status;
-
- if (intr_status & irq_mask) {
- denali->irq_status &= ~irq_mask;
- spin_unlock_irq(&denali->irq_lock);
- /* our interrupt was detected */
- break;
- }
-
- /*
- * these are not the interrupts you are looking for -
- * need to wait again
- */
- spin_unlock_irq(&denali->irq_lock);
- } while (comp_res != 0);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint16_t *buf16 = (uint16_t *)buf;
+ int i;
- if (comp_res == 0) {
- /* timeout */
- pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
- intr_status, irq_mask);
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
- intr_status = 0;
- }
- return intr_status;
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = ioread32(denali->flash_mem + 0x10);
}
-/*
- * This helper function setups the registers for ECC and whether or not
- * the spare area will be transferred.
- */
-static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
- bool transfer_spare)
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
{
- int ecc_en_flag, transfer_spare_flag;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ const uint16_t *buf16 = (const uint16_t *)buf;
+ int i;
- /* set ECC, transfer spare bits if needed */
- ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0;
- transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
- /* Enable spare area/ECC per user's request. */
- iowrite32(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
- iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
+ for (i = 0; i < len / 2; i++)
+ iowrite32(buf16[i], denali->flash_mem + 0x10);
}
-/*
- * sends a pipeline command operation to the controller. See the Denali NAND
- * controller's user guide for more information (section 4.2.3.6).
- */
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
- bool ecc_en, bool transfer_spare,
- int access_type, int op)
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
- int status = PASS;
- uint32_t addr, cmd;
-
- setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t type;
- clear_interrupts(denali);
+ if (ctrl & NAND_CLE)
+ type = 0;
+ else if (ctrl & NAND_ALE)
+ type = 1;
+ else
+ return;
- addr = BANK(denali->flash_bank) | denali->page;
+ /*
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
+ * irq_status must be cleared here to catch the R/B# interrupt later.
+ */
+ if (ctrl & NAND_CTRL_CHANGE)
+ denali_reset_irq(denali);
- if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
- /* read spare area */
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, access_type);
+ index_addr(denali, MODE_11 | BANK(denali->flash_bank) | type, dat);
+}
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_READ) {
- /* setup page read request for access type */
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, access_type);
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- }
- return status;
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
}
/* helper function that simply writes a buffer to the flash */
@@ -748,71 +365,6 @@ static int read_data_from_flash_mem(struct denali_nand_info *denali,
return i * 4; /* intent is to return the number of bytes read */
}
-/* writes OOB data to the device */
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_status;
- uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
- int status = 0;
-
- denali->page = page;
-
- if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
- DENALI_WRITE) == PASS) {
- write_data_to_flash_mem(denali, buf, mtd->oobsize);
-
- /* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0) {
- dev_err(denali->dev, "OOB write failed\n");
- status = -EIO;
- }
- } else {
- dev_err(denali->dev, "unable to send pipeline command\n");
- status = -EIO;
- }
- return status;
-}
-
-/* reads OOB data from the device */
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_mask = INTR__LOAD_COMP;
- uint32_t irq_status, addr, cmd;
-
- denali->page = page;
-
- if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
- DENALI_READ) == PASS) {
- read_data_from_flash_mem(denali, buf, mtd->oobsize);
-
- /*
- * wait for command to be accepted
- * can always use status0 bit as the
- * mask is identical for each bank.
- */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0)
- dev_err(denali->dev, "page on OOB timeout %d\n",
- denali->page);
-
- /*
- * We set the device back to MAIN_ACCESS here as I observed
- * instability with the controller if you do a block erase
- * and the last transaction was a SPARE_ACCESS. Block erase
- * is reliable (according to the MTD test infrastructure)
- * if you are in MAIN_ACCESS.
- */
- addr = BANK(denali->flash_bank) | denali->page;
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, MAIN_ACCESS);
- }
-}
-
static int denali_check_erased_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
unsigned long uncor_ecc_flags,
@@ -886,8 +438,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
return max_bitflips;
}
-#define ECC_SECTOR_SIZE 512
-
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
@@ -899,15 +449,16 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
struct denali_nand_info *denali,
unsigned long *uncor_ecc_flags, uint8_t *buf)
{
+ unsigned int ecc_size = denali->nand.ecc.size;
unsigned int bitflips = 0;
unsigned int max_bitflips = 0;
uint32_t err_addr, err_cor_info;
unsigned int err_byte, err_sector, err_device;
uint8_t err_cor_value;
unsigned int prev_sector = 0;
+ uint32_t irq_status;
- /* read the ECC errors. we'll ignore them for now */
- denali_set_intr_modes(denali, false);
+ denali_reset_irq(denali);
do {
err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
@@ -928,9 +479,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* an erased sector.
*/
*uncor_ecc_flags |= BIT(err_sector);
- } else if (err_byte < ECC_SECTOR_SIZE) {
+ } else if (err_byte < ecc_size) {
/*
- * If err_byte is larger than ECC_SECTOR_SIZE, means error
+ * If err_byte is larger than ecc_size, means error
* happened in OOB, so we ignore it. It's no need for
* us to correct it err_device is represented the NAND
* error bits are happened in if there are more than
@@ -939,7 +490,7 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
int offset;
unsigned int flips_in_byte;
- offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
+ offset = (err_sector * ecc_size + err_byte) *
denali->devnum + err_device;
/* correct the ECC error */
@@ -959,10 +510,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* ECC_TRANSACTION_DONE interrupt, so here just wait for
* a while for this interrupt
*/
- while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
- cpu_relax();
- clear_interrupts(denali);
- denali_set_intr_modes(denali, true);
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+ return -EIO;
return max_bitflips;
}
@@ -974,13 +524,13 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
ioread32(denali->flash_reg + DMA_ENABLE);
}
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
- uint64_t addr = denali->buf.dma_buf;
- mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+ mode = MODE_10 | BANK(denali->flash_bank) | page;
/* DMA is a three step process */
@@ -988,191 +538,352 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
* 1. setup transfer type, interrupt when complete,
* burst len = 64 bytes, the number of pages
*/
- index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+ index_addr(denali, mode,
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
/* 2. set memory low address */
- index_addr(denali, mode, addr);
+ index_addr(denali, mode, dma_addr);
/* 3. set memory high address */
- index_addr(denali, mode, addr >> 32);
+ index_addr(denali, mode, (uint64_t)dma_addr >> 32);
}
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
+static void denali_setup_dma32(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
- uint32_t addr = denali->buf.dma_buf;
mode = MODE_10 | BANK(denali->flash_bank);
/* DMA is a four step process */
/* 1. setup transfer type and # of pages */
- index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+ index_addr(denali, mode | page, 0x2000 | (write << 8) | page_count);
/* 2. set memory high address bits 23:8 */
- index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
+ index_addr(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
/* 3. set memory low address bits 23:8 */
- index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
+ index_addr(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
/* 4. interrupt when complete, burst len = 64 bytes */
index_addr(denali, mode | 0x14000, 0x2400);
}
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
if (denali->caps & DENALI_CAP_DMA_64BIT)
- denali_setup_dma64(denali, op);
+ denali_setup_dma64(denali, dma_addr, page, write);
else
- denali_setup_dma32(denali, op);
+ denali_setup_dma32(denali, dma_addr, page, write);
}
-/*
- * writes a page. user specifies type, and this function handles the
- * configuration details.
- */
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, bool raw_xfer)
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
+ uint32_t addr = BANK(denali->flash_bank) | page;
+ uint32_t irq_status, ecc_err_mask;
+
+ /* setup page read request for access type */
+ index_addr(denali, MODE_10 | addr,
+ raw ? MAIN_SPARE_ACCESS : MAIN_ACCESS);
+
+ iowrite32(MODE_01 | addr, denali->flash_mem);
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ else
+ ecc_err_mask = INTR__ECC_ERR;
+
+ denali_reset_irq(denali);
+
+ read_data_from_flash_mem(denali, buf, size);
+
+ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+ if (!(irq_status & INTR__PAGE_XFER_INC))
+ return -EIO;
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+ const void *buf, size_t size, int page, int raw)
+{
+ uint32_t addr = BANK(denali->flash_bank) | page;
uint32_t irq_status;
- uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
- /*
- * if it is a raw xfer, we want to disable ecc and send the spare area.
- * !raw_xfer - enable ecc
- * raw_xfer - transfer spare
- */
- setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+ /* setup page read request for access type */
+ index_addr(denali, MODE_10 | addr,
+ raw ? MAIN_SPARE_ACCESS : MAIN_ACCESS);
+
+ iowrite32(MODE_01 | addr, denali->flash_mem);
+
+ denali_reset_irq(denali);
+
+ write_data_to_flash_mem(denali, buf, size);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+ if (!(irq_status & INTR__PROGRAM_COMP))
+ return -EIO;
+
+ return 0;
+}
- /* copy buffer into DMA buffer */
- memcpy(denali->buf.buf, buf, mtd->writesize);
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ if (write)
+ return denali_pio_write(denali, buf, size, page, raw);
+ else
+ return denali_pio_read(denali, buf, size, page, raw);
+}
- if (raw_xfer) {
- /* transfer the data to the spare area */
- memcpy(denali->buf.buf + mtd->writesize,
- chip->oob_poi,
- mtd->oobsize);
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ dma_addr_t dma_addr;
+ uint32_t irq_mask, irq_status, ecc_err_mask;
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ int ret = 0;
+
+ dma_addr = dma_map_single(denali->dev, buf, size, dir);
+ if (dma_mapping_error(denali->dev, dma_addr)) {
+ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
}
- dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
+ if (write) {
+ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ ecc_err_mask = 0;
+ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ } else {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_ERR;
+ }
- clear_interrupts(denali);
denali_enable_dma(denali, true);
- denali_setup_dma(denali, DENALI_WRITE);
+ denali_reset_irq(denali);
+ denali_setup_dma(denali, dma_addr, page, write);
/* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0) {
- dev_err(denali->dev, "timeout on write_page (type = %d)\n",
- raw_xfer);
- denali->status = NAND_STATUS_FAIL;
- }
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (!(irq_status & INTR__DMA_CMD_COMP))
+ ret = -EIO;
+ else if (irq_status & ecc_err_mask)
+ ret = -EBADMSG;
denali_enable_dma(denali, false);
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
+ dma_unmap_single(denali->dev, dma_addr, size, dir);
- return 0;
-}
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
-/* NAND core entry points */
+ return ret;
+}
-/*
- * this is the callback that the NAND core calls to write a page. Since
- * writing a page with ECC or without is similar, all the work is done
- * by write_page above.
- */
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
{
- /*
- * for regular page writes, we let HW handle all the ECC
- * data written to the device.
- */
- return write_page(mtd, chip, buf, false);
+ setup_ecc_for_xfer(denali, !raw, raw);
+
+ if (denali->dma_avail)
+ return denali_dma_xfer(denali, buf, size, page, raw, write);
+ else
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
}
-/*
- * This is the callback that the NAND core calls to write a page without ECC.
- * raw access is similar to ECC page writes, so all the work is done in the
- * write_page() function above.
- */
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int write)
{
- /*
- * for raw page writes, we want to disable ECC and simply write
- * whatever data is in the buffer.
- */
- return write_page(mtd, chip, buf, true);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0;
+ unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT;
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ uint8_t *bufpoi = chip->oob_poi;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int bbm_skip = denali->bbtskipbytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /* BBM at the beginning of the OOB area */
+ chip->cmdfunc(mtd, start_cmd, writesize, page);
+ if (write)
+ chip->write_buf(mtd, bufpoi, bbm_skip);
+ else
+ chip->read_buf(mtd, bufpoi, bbm_skip);
+ bufpoi += bbm_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += bbm_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ chip->cmdfunc(mtd, rnd_cmd, pos, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ chip->cmdfunc(mtd, rnd_cmd, writesize + bbm_skip, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (bufpoi - chip->oob_poi);
+ chip->cmdfunc(mtd, rnd_cmd, size - len, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
}
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
- return write_oob_data(mtd, chip->oob_poi, page);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *dma_buf = denali->buf;
+ int bbm_skip = denali->bbtskipbytes;
+ size_t size = writesize + oobsize;
+ int ret, i, pos, len;
+
+ ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += bbm_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(buf, dma_buf + pos, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(buf, dma_buf + writesize + bbm_skip,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(oob, dma_buf + writesize, bbm_skip);
+ oob += bbm_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += bbm_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(oob, dma_buf + pos, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(oob, dma_buf + writesize + bbm_skip,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(oob, dma_buf + size - len, len);
+ }
+
+ return 0;
}
static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- read_oob_data(mtd, chip->oob_poi, page);
+ denali_oob_xfer(mtd, chip, page, 0);
return 0;
}
-static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
- uint32_t irq_status;
- uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
- INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
- INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
- unsigned long uncor_ecc_flags = 0;
- int stat = 0;
-
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
+ int status;
- setup_ecc_for_xfer(denali, true, false);
+ denali_reset_irq(denali);
- denali_enable_dma(denali, true);
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
+ denali_oob_xfer(mtd, chip, page, 1);
- clear_interrupts(denali);
- denali_setup_dma(denali, DENALI_READ);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
- /* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned long uncor_ecc_flags = 0;
+ int stat = 0;
+ int ret;
- memcpy(buf, denali->buf.buf, mtd->writesize);
+ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+ if (ret && ret != -EBADMSG)
+ return ret;
if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
- else if (irq_status & INTR__ECC_ERR)
+ else if (ret == -EBADMSG)
stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
- denali_enable_dma(denali, false);
if (stat < 0)
return stat;
if (uncor_ecc_flags) {
- read_oob_data(mtd, chip->oob_poi, denali->page);
+ ret = denali_read_oob(mtd, chip, page);
+ if (ret)
+ return ret;
stat = denali_check_erased_page(mtd, chip, buf,
uncor_ecc_flags, stat);
@@ -1181,137 +892,268 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return stat;
}
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
- uint32_t irq_mask = INTR__DMA_CMD_COMP;
-
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
-
- setup_ecc_for_xfer(denali, false, true);
- denali_enable_dma(denali, true);
-
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
-
- clear_interrupts(denali);
- denali_setup_dma(denali, DENALI_READ);
-
- /* wait for operation to complete */
- wait_for_irq(denali, irq_mask);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *dma_buf = denali->buf;
+ int bbm_skip = denali->bbtskipbytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+ /*
+ * Fill the buffer with 0xff first except the full page transfer.
+ * This simplifies the logic.
+ */
+ if (!buf || !oob_required)
+ memset(dma_buf, 0xff, size);
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += bbm_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(dma_buf + pos, buf, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(dma_buf + writesize + bbm_skip, buf,
+ len);
+ buf += len;
+ }
+ }
+ }
- denali_enable_dma(denali, false);
+ if (oob_required) {
+ const uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(dma_buf + writesize, oob, bbm_skip);
+ oob += bbm_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += bbm_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(dma_buf + pos, oob, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(dma_buf + writesize + bbm_skip, oob,
+ len);
+ oob += len;
+ }
+ }
- memcpy(buf, denali->buf.buf, mtd->writesize);
- memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize);
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(dma_buf + size - len, oob, len);
+ }
- return 0;
+ return denali_data_xfer(denali, dma_buf, size, page, 1, 1);
}
-static uint8_t denali_read_byte(struct mtd_info *mtd)
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint8_t result = 0xff;
- if (denali->buf.head < denali->buf.tail)
- result = denali->buf.buf[denali->buf.head++];
-
- return result;
+ return denali_data_xfer(denali, (void *)buf, mtd->writesize,
+ page, 0, 1);
}
static void denali_select_chip(struct mtd_info *mtd, int chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- spin_lock_irq(&denali->irq_lock);
denali->flash_bank = chip;
- spin_unlock_irq(&denali->irq_lock);
}
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- int status = denali->status;
+ uint32_t irq_status;
- denali->status = 0;
+ /* R/B# pin transitioned from low to high? */
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
- return status;
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
}
static int denali_erase(struct mtd_info *mtd, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
-
uint32_t cmd, irq_status;
- clear_interrupts(denali);
+ denali_reset_irq(denali);
/* setup page read request for access type */
cmd = MODE_10 | BANK(denali->flash_bank) | page;
index_addr(denali, cmd, 0x1);
/* wait for erase to complete or failure to occur */
- irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
+ irq_status = denali_wait_for_irq(denali,
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
- return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
+ return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
}
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
- int page)
+#define DIV_ROUND_DOWN_ULL(ll, d) \
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
+
+static int denali_setup_data_interface(struct mtd_info *mtd,
+ const struct nand_data_interface *conf,
+ bool check_only)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t addr, id;
+ const struct nand_sdr_timings *timings;
+ unsigned long t_clk;
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
+ int addr_2_data_mask;
+ uint32_t tmp;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ /* clk_x period in picoseconds */
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+ if (!t_clk)
+ return -EINVAL;
+
+ if (check_only)
+ return 0;
+
+ /* tREA -> ACC_CLKS */
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+ tmp = ioread32(denali->flash_reg + ACC_CLKS);
+ tmp &= ~ACC_CLKS__VALUE;
+ tmp |= acc_clks;
+ iowrite32(tmp, denali->flash_reg + ACC_CLKS);
+
+ /* tRWH -> RE_2_WE */
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RE_2_WE);
+ tmp &= ~RE_2_WE__VALUE;
+ tmp |= re_2_we;
+ iowrite32(tmp, denali->flash_reg + RE_2_WE);
+
+ /* tRHZ -> RE_2_RE */
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RE_2_RE);
+ tmp &= ~RE_2_RE__VALUE;
+ tmp |= re_2_re;
+ iowrite32(tmp, denali->flash_reg + RE_2_RE);
+
+ /* tWHR -> WE_2_RE */
+ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
+
+ tmp = ioread32(denali->flash_reg + TWHR2_AND_WE_2_RE);
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+ tmp |= we_2_re;
+ iowrite32(tmp, denali->flash_reg + TWHR2_AND_WE_2_RE);
+
+ /* tADL -> ADDR_2_DATA */
+
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ if (denali->revision < 0x0501)
+ addr_2_data_mask >>= 1;
+
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+ tmp = ioread32(denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+ tmp &= ~addr_2_data_mask;
+ tmp |= addr_2_data;
+ iowrite32(tmp, denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+ t_clk);
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RDWR_EN_HI_CNT);
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
+ tmp |= rdwr_en_hi;
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_HI_CNT);
+
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
+ t_clk);
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
+ t_clk);
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RDWR_EN_LO_CNT);
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
+ tmp |= rdwr_en_lo;
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_LO_CNT);
+
+ /* tCS, tCEA -> CS_SETUP_CNT */
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+ 0);
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + CS_SETUP_CNT);
+ tmp &= ~CS_SETUP_CNT__VALUE;
+ tmp |= cs_setup;
+ iowrite32(tmp, denali->flash_reg + CS_SETUP_CNT);
+
+ return 0;
+}
+
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+ u32 irq_status;
int i;
- switch (cmd) {
- case NAND_CMD_PAGEPROG:
- break;
- case NAND_CMD_STATUS:
- read_status(denali);
- break;
- case NAND_CMD_READID:
- case NAND_CMD_PARAM:
- reset_buf(denali);
- /*
- * sometimes ManufactureId read from register is not right
- * e.g. some of Micron MT29F32G08QAA MLC NAND chips
- * So here we send READID cmd to NAND insteand
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, col);
- for (i = 0; i < 8; i++) {
- index_addr_read_data(denali, addr | 2, &id);
- write_byte_to_buf(denali, id);
- }
- break;
- case NAND_CMD_READ0:
- case NAND_CMD_SEQIN:
- denali->page = page;
- break;
- case NAND_CMD_RESET:
- reset_bank(denali);
- break;
- case NAND_CMD_READOOB:
- /* TODO: Read OOB data */
- break;
- default:
- pr_err(": unsupported command received 0x%x\n", cmd);
- break;
+ for (i = 0; i < denali->max_banks; i++) {
+ denali->flash_bank = i;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DEVICE_RESET__BANK(i),
+ denali->flash_reg + DEVICE_RESET);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+ if (!(irq_status & INTR__INT_ACT))
+ break;
}
+
+ dev_dbg(denali->dev, "%d chips connected\n", i);
+ denali->max_banks = i;
}
-/* end NAND core entry points */
-/* Initialization code to bring the device up to a known good state */
static void denali_hw_init(struct denali_nand_info *denali)
{
/*
@@ -1331,7 +1173,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
denali->bbtskipbytes = ioread32(denali->flash_reg +
SPARE_AREA_SKIP_BYTES);
detect_max_banks(denali);
- denali_nand_reset(denali);
iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
iowrite32(CHIP_EN_DONT_CARE__FLAG,
denali->flash_reg + CHIP_ENABLE_DONT_CARE);
@@ -1341,17 +1182,25 @@ static void denali_hw_init(struct denali_nand_info *denali)
/* Should set value for these registers when init */
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
iowrite32(1, denali->flash_reg + ECC_ENABLE);
- denali_nand_timing_set(denali);
- denali_irq_init(denali);
}
-/*
- * Althogh controller spec said SLC ECC is forceb to be 4bit,
- * but denali controller in MRST only support 15bit and 8bit ECC
- * correction
- */
-#define ECC_8BITS 14
-#define ECC_15BITS 26
+static int denali_calc_ecc_bytes(const struct nand_ecc_setting *setting)
+{
+ int coef;
+
+ switch (setting->step) {
+ case 512:
+ coef = 13;
+ break;
+ case 1024:
+ coef = 14;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return DIV_ROUND_UP(setting->strength * coef, 16) * 2;
+}
static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
@@ -1388,29 +1237,6 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
.free = denali_ooblayout_free,
};
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
/* initialize driver data structures */
static void denali_drv_init(struct denali_nand_info *denali)
{
@@ -1425,12 +1251,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
* element that might be access shared data (interrupt status)
*/
spin_lock_init(&denali->irq_lock);
-
- /* indicate that MTD has not selected a valid bank yet */
- denali->flash_bank = CHIP_SELECT_INVALID;
-
- /* initialize our irq_status variable to indicate no interrupts */
- denali->irq_status = 0;
}
static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1488,29 +1308,15 @@ int denali_init(struct denali_nand_info *denali)
{
struct nand_chip *chip = &denali->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_engine_caps ecc_engine_caps;
int ret;
- if (denali->platform == INTEL_CE4100) {
- /*
- * Due to a silicon limitation, we can only support
- * ONFI timing mode 1 and below.
- */
- if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
- pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
- return -EINVAL;
- }
- }
-
- /* allocate a temporary buffer for nand_scan_ident() */
- denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
- GFP_DMA | GFP_KERNEL);
- if (!denali->buf.buf)
- return -ENOMEM;
-
mtd->dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
+ denali_clear_irq_all(denali);
+
/* Request IRQ after all the hardware initialization is finished */
ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
IRQF_SHARED, DENALI_NAND_NAME, denali);
@@ -1519,8 +1325,11 @@ int denali_init(struct denali_nand_info *denali)
return ret;
}
- /* now that our ISR is registered, we can enable interrupts */
- denali_set_intr_modes(denali, true);
+ denali_enable_irq(denali);
+ denali_reset_banks(denali);
+
+ denali->flash_bank = CHIP_SELECT_INVALID;
+
nand_set_flash_node(chip, denali->dev->of_node);
/* Fallback to the default name if DT did not give "label" property */
if (!mtd->name)
@@ -1528,9 +1337,14 @@ int denali_init(struct denali_nand_info *denali)
/* register the driver with the NAND core subsystem */
chip->select_chip = denali_select_chip;
- chip->cmdfunc = denali_cmdfunc;
chip->read_byte = denali_read_byte;
+ chip->write_byte = denali_write_byte;
+ chip->cmd_ctrl = denali_cmd_ctrl;
+ chip->dev_ready = denali_dev_ready;
chip->waitfunc = denali_waitfunc;
+ /* clk rate info is needed for setup_data_interface */
+ if (denali->clk_x_rate)
+ chip->setup_data_interface = denali_setup_data_interface;
/*
* scan for NAND devices attached to the controller
@@ -1539,33 +1353,24 @@ int denali_init(struct denali_nand_info *denali)
*/
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
if (ret)
- goto failed_req_irq;
-
- /* allocate the right size buffer now */
- devm_kfree(denali->dev, denali->buf.buf);
- denali->buf.buf = devm_kzalloc(denali->dev,
- mtd->writesize + mtd->oobsize,
- GFP_KERNEL);
- if (!denali->buf.buf) {
- ret = -ENOMEM;
- goto failed_req_irq;
- }
+ goto disable_irq;
- ret = dma_set_mask(denali->dev,
- DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
- 64 : 32));
- if (ret) {
- dev_err(denali->dev, "No usable DMA configuration\n");
- goto failed_req_irq;
+ if (ioread32(denali->flash_reg + FEATURES) & FEATURES__DMA)
+ denali->dma_avail = 1;
+
+ if (denali->dma_avail) {
+ int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+ if (ret) {
+ dev_info(denali->dev, "Failed to set DMA mask. Disabling DMA.\n");
+ denali->dma_avail = 0;
+ }
}
- denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
- mtd->writesize + mtd->oobsize,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
- dev_err(denali->dev, "Failed to map DMA buffer\n");
- ret = -EIO;
- goto failed_req_irq;
+ if (denali->dma_avail) {
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+ chip->buf_align = 16;
}
/*
@@ -1574,46 +1379,74 @@ int denali_init(struct denali_nand_info *denali)
* bad block management.
*/
- /* Bad block management */
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
-
- /* skip the scan for now until we have OOB read and write support */
chip->bbt_options |= NAND_BBT_USE_FLASH;
- chip->options |= NAND_SKIP_BBTSCAN;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
/* no subpage writes on denali */
chip->options |= NAND_NO_SUBPAGE_WRITE;
+ ecc_engine_caps.ecc_settings = denali->avail_ecc_settings;
+ ecc_engine_caps.calc_ecc_bytes = denali_calc_ecc_bytes;
+ ecc_engine_caps.avail_oobsize = mtd->oobsize - denali->bbtskipbytes;
+
+ ret = -ENOTSUPP;
+
+ /* If both .size and .strength are set (by DT), we check if supported */
+ ret = nand_check_ecc_caps(mtd, chip, &ecc_engine_caps);
+ if (ret && ret != -ENODATA)
+ dev_info(denali->dev, "try to find other ECC settings\n");
+
/*
- * Denali Controller only support 15bit and 8bit ECC in MRST,
- * so just let controller do 15bit ECC for MLC and 8bit ECC for
- * SLC if possible.
- * */
- if (!nand_is_slc(chip) &&
- (mtd->oobsize > (denali->bbtskipbytes +
- ECC_15BITS * (mtd->writesize /
- ECC_SECTOR_SIZE)))) {
- /* if MLC OOB size is large enough, use 15bit ECC*/
- chip->ecc.strength = 15;
- chip->ecc.bytes = ECC_15BITS;
- iowrite32(15, denali->flash_reg + ECC_CORRECTION);
- } else if (mtd->oobsize < (denali->bbtskipbytes +
- ECC_8BITS * (mtd->writesize /
- ECC_SECTOR_SIZE))) {
- pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
- goto failed_req_irq;
- } else {
- chip->ecc.strength = 8;
- chip->ecc.bytes = ECC_8BITS;
- iowrite32(8, denali->flash_reg + ECC_CORRECTION);
+ * We want .size and .strength closest to the chip's requirement
+ * unless NAND_ECC_MAXIMIZE is requested.
+ */
+ if (ret && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+ ret = nand_try_to_match_ecc_req(mtd, chip, &ecc_engine_caps);
+ if (ret)
+ dev_info(denali->dev, "try to maximize ECC setting\n");
}
+ /* The last thing we can do is to try the max ECC strength */
+ if (ret)
+ ret = nand_try_to_maximize_ecc(mtd, chip, &ecc_engine_caps);
+
+ if (ret) {
+ dev_err(denali->dev, "failed to choose ECC size/strength\n");
+ goto disable_irq;
+ }
+
+ dev_dbg(denali->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength,
+ chip->ecc.strength + 1),
+ denali->flash_reg + ECC_CORRECTION);
+ iowrite32(mtd->erasesize / mtd->writesize,
+ denali->flash_reg + PAGES_PER_BLOCK);
+ iowrite32(denali->nand.options & NAND_BUSWIDTH_16 ? 1 : 0,
+ denali->flash_reg + DEVICE_WIDTH);
+ iowrite32(mtd->writesize, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
+ iowrite32(mtd->oobsize, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
+
+ iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
+ iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
+ iowrite32(mtd->writesize / chip->ecc.size,
+ denali->flash_reg + CFG_NUM_DATA_BLOCKS);
+
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
- /* override the default read operations */
- chip->ecc.size = ECC_SECTOR_SIZE;
+ if (denali->nand.options & NAND_BUSWIDTH_16) {
+ chip->read_buf = denali_read_buf16;
+ chip->write_buf = denali_write_buf16;
+ } else {
+ chip->read_buf = denali_read_buf;
+ chip->write_buf = denali_write_buf;
+ }
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
chip->ecc.read_page = denali_read_page;
chip->ecc.read_page_raw = denali_read_page_raw;
chip->ecc.write_page = denali_write_page;
@@ -1624,21 +1457,34 @@ int denali_init(struct denali_nand_info *denali)
ret = denali_multidev_fixup(denali);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
+
+ /*
+ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
+ * use devm_kmalloc() because the memory allocated by devm_ does not
+ * guarantee DMA-safe alignment.
+ */
+ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!denali->buf) {
+ ret = -ENOMEM;
+ goto disable_irq;
+ }
ret = nand_scan_tail(mtd);
if (ret)
- goto failed_req_irq;
+ goto free_buf;
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto failed_req_irq;
+ goto free_buf;
}
return 0;
-failed_req_irq:
- denali_irq_cleanup(denali->irq, denali);
+free_buf:
+ kfree(denali->buf);
+disable_irq:
+ denali_disable_irq(denali);
return ret;
}
@@ -1656,8 +1502,9 @@ void denali_remove(struct denali_nand_info *denali)
int bufsize = mtd->writesize + mtd->oobsize;
nand_release(mtd);
- denali_irq_cleanup(denali->irq, denali);
- dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
+ kfree(denali->buf);
+ denali_disable_irq(denali);
+ dma_unmap_single(denali->dev, denali->dma_addr, bufsize,
DMA_BIDIRECTIONAL);
}
EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index ec00485..46406c6 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -24,325 +24,318 @@
#include <linux/mtd/nand.h>
#define DEVICE_RESET 0x0
-#define DEVICE_RESET__BANK0 0x0001
-#define DEVICE_RESET__BANK1 0x0002
-#define DEVICE_RESET__BANK2 0x0004
-#define DEVICE_RESET__BANK3 0x0008
+#define DEVICE_RESET__BANK(bank) BIT(bank)
#define TRANSFER_SPARE_REG 0x10
-#define TRANSFER_SPARE_REG__FLAG 0x0001
+#define TRANSFER_SPARE_REG__FLAG BIT(0)
#define LOAD_WAIT_CNT 0x20
-#define LOAD_WAIT_CNT__VALUE 0xffff
+#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0)
#define PROGRAM_WAIT_CNT 0x30
-#define PROGRAM_WAIT_CNT__VALUE 0xffff
+#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0)
#define ERASE_WAIT_CNT 0x40
-#define ERASE_WAIT_CNT__VALUE 0xffff
+#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0)
#define INT_MON_CYCCNT 0x50
-#define INT_MON_CYCCNT__VALUE 0xffff
+#define INT_MON_CYCCNT__VALUE GENMASK(15, 0)
#define RB_PIN_ENABLED 0x60
-#define RB_PIN_ENABLED__BANK0 0x0001
-#define RB_PIN_ENABLED__BANK1 0x0002
-#define RB_PIN_ENABLED__BANK2 0x0004
-#define RB_PIN_ENABLED__BANK3 0x0008
+#define RB_PIN_ENABLED__BANK(bank) BIT(bank)
#define MULTIPLANE_OPERATION 0x70
-#define MULTIPLANE_OPERATION__FLAG 0x0001
+#define MULTIPLANE_OPERATION__FLAG BIT(0)
#define MULTIPLANE_READ_ENABLE 0x80
-#define MULTIPLANE_READ_ENABLE__FLAG 0x0001
+#define MULTIPLANE_READ_ENABLE__FLAG BIT(0)
#define COPYBACK_DISABLE 0x90
-#define COPYBACK_DISABLE__FLAG 0x0001
+#define COPYBACK_DISABLE__FLAG BIT(0)
#define CACHE_WRITE_ENABLE 0xa0
-#define CACHE_WRITE_ENABLE__FLAG 0x0001
+#define CACHE_WRITE_ENABLE__FLAG BIT(0)
#define CACHE_READ_ENABLE 0xb0
-#define CACHE_READ_ENABLE__FLAG 0x0001
+#define CACHE_READ_ENABLE__FLAG BIT(0)
#define PREFETCH_MODE 0xc0
-#define PREFETCH_MODE__PREFETCH_EN 0x0001
-#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0
+#define PREFETCH_MODE__PREFETCH_EN BIT(0)
+#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4)
#define CHIP_ENABLE_DONT_CARE 0xd0
-#define CHIP_EN_DONT_CARE__FLAG 0x01
+#define CHIP_EN_DONT_CARE__FLAG BIT(0)
#define ECC_ENABLE 0xe0
-#define ECC_ENABLE__FLAG 0x0001
+#define ECC_ENABLE__FLAG BIT(0)
#define GLOBAL_INT_ENABLE 0xf0
-#define GLOBAL_INT_EN_FLAG 0x01
+#define GLOBAL_INT_EN_FLAG BIT(0)
-#define WE_2_RE 0x100
-#define WE_2_RE__VALUE 0x003f
+#define TWHR2_AND_WE_2_RE 0x100
+#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8)
-#define ADDR_2_DATA 0x110
-#define ADDR_2_DATA__VALUE 0x003f
+#define TCWAW_AND_ADDR_2_DATA 0x110
+/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */
+#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0)
+#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8)
#define RE_2_WE 0x120
-#define RE_2_WE__VALUE 0x003f
+#define RE_2_WE__VALUE GENMASK(5, 0)
#define ACC_CLKS 0x130
-#define ACC_CLKS__VALUE 0x000f
+#define ACC_CLKS__VALUE GENMASK(3, 0)
#define NUMBER_OF_PLANES 0x140
-#define NUMBER_OF_PLANES__VALUE 0x0007
+#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0)
#define PAGES_PER_BLOCK 0x150
-#define PAGES_PER_BLOCK__VALUE 0xffff
+#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0)
#define DEVICE_WIDTH 0x160
-#define DEVICE_WIDTH__VALUE 0x0003
+#define DEVICE_WIDTH__VALUE GENMASK(1, 0)
#define DEVICE_MAIN_AREA_SIZE 0x170
-#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff
+#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0)
#define DEVICE_SPARE_AREA_SIZE 0x180
-#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff
+#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0)
#define TWO_ROW_ADDR_CYCLES 0x190
-#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001
+#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0)
#define MULTIPLANE_ADDR_RESTRICT 0x1a0
-#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001
+#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0)
#define ECC_CORRECTION 0x1b0
-#define ECC_CORRECTION__VALUE 0x001f
+#define ECC_CORRECTION__VALUE GENMASK(4, 0)
+#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16)
+#define MAKE_ECC_CORRECTION(val, thresh) \
+ (((val) & (ECC_CORRECTION__VALUE)) | \
+ (((thresh) << 16) & (ECC_CORRECTION__ERASE_THRESHOLD)))
#define READ_MODE 0x1c0
-#define READ_MODE__VALUE 0x000f
+#define READ_MODE__VALUE GENMASK(3, 0)
#define WRITE_MODE 0x1d0
-#define WRITE_MODE__VALUE 0x000f
+#define WRITE_MODE__VALUE GENMASK(3, 0)
#define COPYBACK_MODE 0x1e0
-#define COPYBACK_MODE__VALUE 0x000f
+#define COPYBACK_MODE__VALUE GENMASK(3, 0)
#define RDWR_EN_LO_CNT 0x1f0
-#define RDWR_EN_LO_CNT__VALUE 0x001f
+#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0)
#define RDWR_EN_HI_CNT 0x200
-#define RDWR_EN_HI_CNT__VALUE 0x001f
+#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0)
#define MAX_RD_DELAY 0x210
-#define MAX_RD_DELAY__VALUE 0x000f
+#define MAX_RD_DELAY__VALUE GENMASK(3, 0)
#define CS_SETUP_CNT 0x220
-#define CS_SETUP_CNT__VALUE 0x001f
+#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
+#define CS_SETUP_CNT__TWB GENMASK(17, 12)
#define SPARE_AREA_SKIP_BYTES 0x230
-#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f
+#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
#define SPARE_AREA_MARKER 0x240
-#define SPARE_AREA_MARKER__VALUE 0xffff
+#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0)
#define DEVICES_CONNECTED 0x250
-#define DEVICES_CONNECTED__VALUE 0x0007
+#define DEVICES_CONNECTED__VALUE GENMASK(2, 0)
#define DIE_MASK 0x260
-#define DIE_MASK__VALUE 0x00ff
+#define DIE_MASK__VALUE GENMASK(7, 0)
#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
-#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff
+#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0)
#define WRITE_PROTECT 0x280
-#define WRITE_PROTECT__FLAG 0x0001
+#define WRITE_PROTECT__FLAG BIT(0)
#define RE_2_RE 0x290
-#define RE_2_RE__VALUE 0x003f
+#define RE_2_RE__VALUE GENMASK(5, 0)
#define MANUFACTURER_ID 0x300
-#define MANUFACTURER_ID__VALUE 0x00ff
+#define MANUFACTURER_ID__VALUE GENMASK(7, 0)
#define DEVICE_ID 0x310
-#define DEVICE_ID__VALUE 0x00ff
+#define DEVICE_ID__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_0 0x320
-#define DEVICE_PARAM_0__VALUE 0x00ff
+#define DEVICE_PARAM_0__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_1 0x330
-#define DEVICE_PARAM_1__VALUE 0x00ff
+#define DEVICE_PARAM_1__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_2 0x340
-#define DEVICE_PARAM_2__VALUE 0x00ff
+#define DEVICE_PARAM_2__VALUE GENMASK(7, 0)
#define LOGICAL_PAGE_DATA_SIZE 0x350
-#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0)
#define LOGICAL_PAGE_SPARE_SIZE 0x360
-#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0)
#define REVISION 0x370
-#define REVISION__VALUE 0xffff
+#define REVISION__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_FEATURES 0x380
-#define ONFI_DEVICE_FEATURES__VALUE 0x003f
+#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0)
#define ONFI_OPTIONAL_COMMANDS 0x390
-#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f
+#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0)
#define ONFI_TIMING_MODE 0x3a0
-#define ONFI_TIMING_MODE__VALUE 0x003f
+#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
-#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f
+#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
-#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff
-#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100
+#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0)
+#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff
-
-#define FEATURES 0x3f0
-#define FEATURES__N_BANKS 0x0003
-#define FEATURES__ECC_MAX_ERR 0x003c
-#define FEATURES__DMA 0x0040
-#define FEATURES__CMD_DMA 0x0080
-#define FEATURES__PARTITION 0x0100
-#define FEATURES__XDMA_SIDEBAND 0x0200
-#define FEATURES__GPREG 0x0400
-#define FEATURES__INDEX_ADDR 0x0800
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0)
+
+#define FEATURES 0x3f0
+#define FEATURES__N_BANKS GENMASK(1, 0)
+#define FEATURES__ECC_MAX_ERR GENMASK(5, 2)
+#define FEATURES__DMA BIT(6)
+#define FEATURES__CMD_DMA BIT(7)
+#define FEATURES__PARTITION BIT(8)
+#define FEATURES__XDMA_SIDEBAND BIT(9)
+#define FEATURES__GPREG BIT(10)
+#define FEATURES__INDEX_ADDR BIT(11)
#define TRANSFER_MODE 0x400
-#define TRANSFER_MODE__VALUE 0x0003
+#define TRANSFER_MODE__VALUE GENMASK(1, 0)
-#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
-#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
+#define INTR_STATUS(bank) (0x410 + (bank) * 0x50)
+#define INTR_EN(bank) (0x420 + (bank) * 0x50)
/* bit[1:0] is used differently depending on IP version */
-#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
-#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
-#define INTR__ECC_ERR 0x0002 /* old IP */
-#define INTR__DMA_CMD_COMP 0x0004
-#define INTR__TIME_OUT 0x0008
-#define INTR__PROGRAM_FAIL 0x0010
-#define INTR__ERASE_FAIL 0x0020
-#define INTR__LOAD_COMP 0x0040
-#define INTR__PROGRAM_COMP 0x0080
-#define INTR__ERASE_COMP 0x0100
-#define INTR__PIPE_CPYBCK_CMD_COMP 0x0200
-#define INTR__LOCKED_BLK 0x0400
-#define INTR__UNSUP_CMD 0x0800
-#define INTR__INT_ACT 0x1000
-#define INTR__RST_COMP 0x2000
-#define INTR__PIPE_CMD_ERR 0x4000
-#define INTR__PAGE_XFER_INC 0x8000
-
-#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50))
-#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50))
-#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
+#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */
+#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */
+#define INTR__ECC_ERR BIT(1) /* old IP */
+#define INTR__DMA_CMD_COMP BIT(2)
+#define INTR__TIME_OUT BIT(3)
+#define INTR__PROGRAM_FAIL BIT(4)
+#define INTR__ERASE_FAIL BIT(5)
+#define INTR__LOAD_COMP BIT(6)
+#define INTR__PROGRAM_COMP BIT(7)
+#define INTR__ERASE_COMP BIT(8)
+#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9)
+#define INTR__LOCKED_BLK BIT(10)
+#define INTR__UNSUP_CMD BIT(11)
+#define INTR__INT_ACT BIT(12)
+#define INTR__RST_COMP BIT(13)
+#define INTR__PIPE_CMD_ERR BIT(14)
+#define INTR__PAGE_XFER_INC BIT(15)
+#define INTR__ERASED_PAGE BIT(16)
+
+#define PAGE_CNT(bank) (0x430 + (bank) * 0x50)
+#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50)
+#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50)
#define ECC_THRESHOLD 0x600
-#define ECC_THRESHOLD__VALUE 0x03ff
+#define ECC_THRESHOLD__VALUE GENMASK(9, 0)
#define ECC_ERROR_BLOCK_ADDRESS 0x610
-#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff
+#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0)
#define ECC_ERROR_PAGE_ADDRESS 0x620
-#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff
-#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000
+#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0)
+#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12)
#define ECC_ERROR_ADDRESS 0x630
-#define ECC_ERROR_ADDRESS__OFFSET 0x0fff
-#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000
+#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0)
+#define ECC_ERROR_ADDRESS__SECTOR_NR GENMASK(15, 12)
#define ERR_CORRECTION_INFO 0x640
-#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff
-#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00
-#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
-#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
+#define ERR_CORRECTION_INFO__BYTEMASK GENMASK(7, 0)
+#define ERR_CORRECTION_INFO__DEVICE_NR GENMASK(11, 8)
+#define ERR_CORRECTION_INFO__ERROR_TYPE BIT(14)
+#define ERR_CORRECTION_INFO__LAST_ERR_INFO BIT(15)
#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
-#define ECC_COR_INFO__MAX_ERRORS 0x007f
-#define ECC_COR_INFO__UNCOR_ERR 0x0080
+#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0)
+#define ECC_COR_INFO__UNCOR_ERR BIT(7)
+
+#define CFG_DATA_BLOCK_SIZE 0x6b0
+
+#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0
+
+#define CFG_NUM_DATA_BLOCKS 0x6d0
+
+#define CFG_META_DATA_SIZE 0x6e0
#define DMA_ENABLE 0x700
-#define DMA_ENABLE__FLAG 0x0001
+#define DMA_ENABLE__FLAG BIT(0)
#define IGNORE_ECC_DONE 0x710
-#define IGNORE_ECC_DONE__FLAG 0x0001
+#define IGNORE_ECC_DONE__FLAG BIT(0)
#define DMA_INTR 0x720
#define DMA_INTR_EN 0x730
-#define DMA_INTR__TARGET_ERROR 0x0001
-#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002
-#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004
-#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008
-#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010
-#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
+#define DMA_INTR__TARGET_ERROR BIT(0)
+#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1)
+#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2)
+#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3)
+#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4)
+#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5)
#define TARGET_ERR_ADDR_LO 0x740
-#define TARGET_ERR_ADDR_LO__VALUE 0xffff
+#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0)
#define TARGET_ERR_ADDR_HI 0x750
-#define TARGET_ERR_ADDR_HI__VALUE 0xffff
+#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0)
#define CHNL_ACTIVE 0x760
-#define CHNL_ACTIVE__CHANNEL0 0x0001
-#define CHNL_ACTIVE__CHANNEL1 0x0002
-#define CHNL_ACTIVE__CHANNEL2 0x0004
-#define CHNL_ACTIVE__CHANNEL3 0x0008
+#define CHNL_ACTIVE__CHANNEL0 BIT(0)
+#define CHNL_ACTIVE__CHANNEL1 BIT(1)
+#define CHNL_ACTIVE__CHANNEL2 BIT(2)
+#define CHNL_ACTIVE__CHANNEL3 BIT(3)
#define FAIL 1 /*failed flag*/
#define PASS 0 /*success flag*/
-#define CLK_X 5
-#define CLK_MULTI 4
-
-#define ONFI_BLOOM_TIME 1
-#define MODE5_WORKAROUND 0
-
-
#define MODE_00 0x00000000
#define MODE_01 0x04000000
#define MODE_10 0x08000000
#define MODE_11 0x0C000000
-#define ECC_SECTOR_SIZE 512
-
-struct nand_buf {
- int head;
- int tail;
- uint8_t *buf;
- dma_addr_t dma_buf;
-};
-
-#define INTEL_CE4100 1
-#define INTEL_MRST 2
-#define DT 3
-
struct denali_nand_info {
struct nand_chip nand;
+ unsigned long clk_x_rate; /* bus interface clock rate */
int flash_bank; /* currently selected chip */
- int status;
- int platform;
- struct nand_buf buf;
struct device *dev;
- int total_used_banks;
- int page;
void __iomem *flash_reg; /* Register Interface */
void __iomem *flash_mem; /* Host Data/Command Interface */
/* elements used by ISR */
struct completion complete;
spinlock_t irq_lock;
+ uint32_t irq_mask;
uint32_t irq_status;
int irq;
+ void *buf;
+ dma_addr_t dma_addr;
+ int dma_avail;
int devnum; /* represent how many nands connected */
int bbtskipbytes;
int max_banks;
unsigned int revision;
+ unsigned long ecc_strength_avail;
unsigned int caps;
+ const struct nand_ecc_setting *avail_ecc_settings;
};
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index df9ef36..a6d2e58 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -29,13 +29,30 @@ struct denali_dt {
struct clk *clk;
};
+#define DENALI_MAX_ECC_SETTINGS 4
+
struct denali_dt_data {
unsigned int revision;
unsigned int caps;
+ struct nand_ecc_setting avail_ecc_settings[DENALI_MAX_ECC_SETTINGS];
};
static const struct denali_dt_data denali_socfpga_data = {
.caps = DENALI_CAP_HW_ECC_FIXUP,
+ .avail_ecc_settings = {{512, 8}, {512, 15}},
+};
+
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .avail_ecc_settings = {{1024, 8}, {1024, 16}, {1024, 24}},
+};
+
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+ .revision = 0x0501,
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .avail_ecc_settings = {{1024, 8}, {1024, 16}},
};
static const struct of_device_id denali_nand_dt_ids[] = {
@@ -43,6 +60,14 @@ static const struct of_device_id denali_nand_dt_ids[] = {
.compatible = "altr,socfpga-denali-nand",
.data = &denali_socfpga_data,
},
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5a",
+ .data = &denali_uniphier_v5a_data,
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5b",
+ .data = &denali_uniphier_v5b_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
@@ -64,9 +89,9 @@ static int denali_dt_probe(struct platform_device *pdev)
if (data) {
denali->revision = data->revision;
denali->caps = data->caps;
+ denali->avail_ecc_settings = data->avail_ecc_settings;
}
- denali->platform = DT;
denali->dev = &pdev->dev;
denali->irq = platform_get_irq(pdev, 0);
if (denali->irq < 0) {
@@ -93,6 +118,8 @@ static int denali_dt_probe(struct platform_device *pdev)
}
clk_prepare_enable(dt->clk);
+ denali->clk_x_rate = clk_get_rate(dt->clk);
+
ret = denali_init(denali);
if (ret)
goto out_disable_clk;
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index ac84323..e9c77d6 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -19,6 +19,9 @@
#define DENALI_NAND_NAME "denali-nand-pci"
+#define INTEL_CE4100 1
+#define INTEL_MRST 2
+
/* List of platforms this NAND controller has be integrated into */
static const struct pci_device_id denali_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 },
@@ -27,6 +30,10 @@ static const struct pci_device_id denali_pci_ids[] = {
};
MODULE_DEVICE_TABLE(pci, denali_pci_ids);
+static const struct nand_ecc_setting denali_pci_avail_ecc_settings[] = {
+ {512, 8}, {512, 15}, {/* sentinel */}
+};
+
static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int ret;
@@ -45,13 +52,11 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (id->driver_data == INTEL_CE4100) {
- denali->platform = INTEL_CE4100;
mem_base = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 1);
csr_base = pci_resource_start(dev, 1);
csr_len = pci_resource_len(dev, 1);
} else {
- denali->platform = INTEL_MRST;
csr_base = pci_resource_start(dev, 0);
csr_len = pci_resource_len(dev, 0);
mem_base = pci_resource_start(dev, 1);
@@ -65,6 +70,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_set_master(dev);
denali->dev = &dev->dev;
denali->irq = dev->irq;
+ denali->clk_x_rate = 200000000; /* 200 MHz */
+ denali->avail_ecc_settings = denali_pci_avail_ecc_settings;
ret = pci_request_regions(dev, DENALI_NAND_NAME);
if (ret) {
--
2.7.4
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ 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