* Re: [PATCH v6 18/18] mtd: rawnand: Move generic bits to the ECC framework
From: Boris Brezillon @ 2020-05-28 15:56 UTC (permalink / raw)
To: Miquel Raynal
Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-19-miquel.raynal@bootlin.com>
On Thu, 28 May 2020 13:31:13 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> +/**
> + * nanddev_get_flash_node() - Get the device node attached to a NAND device
> + * @nand: NAND device
> + *
> + * Return: the device node linked to @nand.
> + */
> +static inline struct device_node *nanddev_get_flash_node(struct nand_device *nand)
> +{
> + return mtd_get_of_node(nanddev_to_mtd(nand));
> +}
> +
Can we name that one nanddev_get_of_node(). We'll probably want to
expose fwnode at some point, and get_flash_node() is a bit too generic
IMO.
^ permalink raw reply
* Re: [PATCH v6 18/18] mtd: rawnand: Move generic bits to the ECC framework
From: Boris Brezillon @ 2020-05-28 15:55 UTC (permalink / raw)
To: Miquel Raynal
Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd,
Rob Herring, Mark Rutland, devicetree, Thomas Petazzoni,
Paul Cercueil, Chuanhong Guo, Weijie Gao, linux-arm-kernel,
Mason Yang, Julien Su
In-Reply-To: <20200528113113.9166-19-miquel.raynal@bootlin.com>
On Thu, 28 May 2020 13:31:13 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> Many OOB layouts and helpers are generic to all NAND chips, they
> should not be restricted to be only used by raw NAND controller
> drivers. They might later be used by generic ECC engines and SPI-NAND
> devices as well so move them into a more generic place.
>
> To avoid moving all the raw NAND core "history" into the generic NAND
> layer, we already moved certain bits into legacy helpers in the raw
> NAND core to ensure backward compatibility.
>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
> drivers/mtd/nand/Kconfig | 11 +
> drivers/mtd/nand/Makefile | 1 +
> .../mtd/nand/{raw/nand_bch.c => ecc-sw-bch.c} | 2 +-
> drivers/mtd/nand/ecc.c | 299 +++++++++++++++++
> drivers/mtd/nand/raw/Kconfig | 11 +-
> drivers/mtd/nand/raw/Makefile | 1 -
> drivers/mtd/nand/raw/atmel/nand-controller.c | 3 +-
> drivers/mtd/nand/raw/denali.c | 3 +
> drivers/mtd/nand/raw/nand_base.c | 313 +-----------------
> drivers/mtd/nand/raw/nand_toshiba.c | 2 +
> drivers/mtd/nand/raw/nandsim.c | 2 +-
> drivers/mtd/nand/raw/omap2.c | 2 +-
> drivers/mtd/nand/raw/sunxi_nand.c | 3 +-
> drivers/mtd/nand/raw/tegra_nand.c | 3 +-
> .../mtd/{nand_bch.h => nand-ecc-sw-bch.h} | 6 +-
> include/linux/mtd/nand.h | 20 ++
> include/linux/mtd/rawnand.h | 17 +-
> 17 files changed, 364 insertions(+), 335 deletions(-)
> rename drivers/mtd/nand/{raw/nand_bch.c => ecc-sw-bch.c} (99%)
> rename include/linux/mtd/{nand_bch.h => nand-ecc-sw-bch.h} (92%)
I feel like this patch could be split a little.
^ permalink raw reply
* Re: [PATCH v3 10/10] dmaengine: dw: Initialize max_sg_nents with nollp flag
From: Serge Semin @ 2020-05-28 15:50 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Serge Semin, Vinod Koul, Viresh Kumar, Dan Williams,
Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, Rob Herring,
linux-mips, devicetree, dmaengine, linux-kernel
In-Reply-To: <20200528145630.GV1634618@smile.fi.intel.com>
On Thu, May 28, 2020 at 05:56:30PM +0300, Andy Shevchenko wrote:
> On Wed, May 27, 2020 at 01:50:21AM +0300, Serge Semin wrote:
> > Multi-block support provides a way to map the kernel-specific SG-table so
> > the DW DMA device would handle it as a whole instead of handling the
> > SG-list items or so called LLP block items one by one. So if true LLP
> > list isn't supported by the DW DMA engine, then soft-LLP mode will be
> > utilized to load and execute each LLP-block one by one. The soft-LLP mode
> > of the DMA transactions execution might not work well for some DMA
> > consumers like SPI due to its Tx and Rx buffers inter-dependency. Let's
> > expose the nollp flag indicating the soft-LLP mode by means of the
> > max_sg_nents capability, so the DMA consumer would be ready to somehow
> > workaround errors caused by such mode being utilized.
> >
>
> In principal I agree, one nit below.
> If you are okay with it, feel free to add my Rb tag.
>
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> > Cc: Arnd Bergmann <arnd@arndb.de>
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: linux-mips@vger.kernel.org
> > Cc: devicetree@vger.kernel.org
> >
> > ---
> >
> > Changelog v3:
> > - This is a new patch created as a result of the discussion with Vinud and
> > Andy in the framework of DW DMA burst and LLP capabilities.
> > ---
> > drivers/dma/dw/core.c | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
> > index 29c4ef08311d..b850eb7fd084 100644
> > --- a/drivers/dma/dw/core.c
> > +++ b/drivers/dma/dw/core.c
> > @@ -1054,6 +1054,15 @@ static void dwc_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
> > struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
> >
> > caps->max_burst = dwc->max_burst;
> > +
> > + /*
> > + * It might be crucial for some devices to have the hardware
> > + * accelerated multi-block transfers supported, aka LLPs in DW DMAC
> > + * notation. So if LLPs are supported then max_sg_nents is set to
> > + * zero which means unlimited number of SG entries can be handled in a
> > + * single DMA transaction, otherwise it's just one SG entry.
> > + */
>
> > + caps->max_sg_nents = dwc->nollp;
>
> To be on the safer side I would explicitly do it like
>
> if (dwc->nollp)
> /* your nice comment */
> = 1;
> else
> /* Unlimited */
> = 0;
>
> type or content of nollp theoretically can be changed and this will affect maximum segments.
Agree. Though I don't like formatting you suggested. If I add my nice comment
between if-statement and assignment the the former will be look detached from
the if-statement, which seems a bit ugly. So I'd leave the comment above the
whole if-else statement, especially seeing I've already mentioned there about
the unlimited number of SG entries there.
/*
* It might be crucial for some devices to have the hardware
* accelerated multi-block transfers supported, aka LLPs in DW DMAC
* notation. So if LLPs are supported then max_sg_nents is set to
* zero which means unlimited number of SG entries can be handled in a
* single DMA transaction, otherwise it's just one SG entry.
*/
if (dwc->nollp)
caps->max_sg_nents = 1;
else
caps->max_sg_nents = 0;
-Sergey
>
> > }
> >
> > int do_dma_probe(struct dw_dma_chip *chip)
> > --
> > 2.26.2
> >
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply
* [PATCH v3 4/5] regulator: qcom: Add labibb driver
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
In-Reply-To: <20200528154625.17742-1-sumit.semwal@linaro.org>
From: Nisha Kumari <nishakumari@codeaurora.org>
Qualcomm platforms have LAB(LCD AMOLED Boost)/IBB(Inverting Buck Boost)
regulators, labibb for short, which are used as power supply for
LCD Mode displays.
This patch adds labibb regulator driver for pmi8998 PMIC, found on
SDM845 platforms.
Signed-off-by: Nisha Kumari <nishakumari@codeaurora.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
--
v2: sumits: reworked the driver for more common code, and addressed
review comments from v1
v3: sumits: addressed review comments from v2; moved to use core
regulator features like enable_time, off_on_delay, and the newly
added poll_enabled_time. Moved the check_enabled functionality
to core framework via poll_enabled_time.
---
drivers/regulator/Kconfig | 10 +
drivers/regulator/Makefile | 1 +
drivers/regulator/qcom-labibb-regulator.c | 224 ++++++++++++++++++++++
3 files changed, 235 insertions(+)
create mode 100644 drivers/regulator/qcom-labibb-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f4b72cb098ef..58704a9fd05d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1167,5 +1167,15 @@ config REGULATOR_WM8994
This driver provides support for the voltage regulators on the
WM8994 CODEC.
+config REGULATOR_QCOM_LABIBB
+ tristate "QCOM LAB/IBB regulator support"
+ depends on SPMI || COMPILE_TEST
+ help
+ This driver supports Qualcomm's LAB/IBB regulators present on the
+ Qualcomm's PMIC chip pmi8998. QCOM LAB and IBB are SPMI
+ based PMIC implementations. LAB can be used as positive
+ boost regulator and IBB can be used as a negative boost regulator
+ for LCD display panel.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 6610ee001d9a..5b313786c0e8 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c
new file mode 100644
index 000000000000..634d08461c6e
--- /dev/null
+++ b/drivers/regulator/qcom-labibb-regulator.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define REG_PERPH_TYPE 0x04
+#define QCOM_LAB_TYPE 0x24
+#define QCOM_IBB_TYPE 0x20
+
+#define REG_LABIBB_STATUS1 0x08
+#define REG_LABIBB_ENABLE_CTL 0x46
+#define LABIBB_STATUS1_VREG_OK_BIT BIT(7)
+#define LABIBB_CONTROL_ENABLE BIT(7)
+
+#define LAB_ENABLE_CTL_MASK BIT(7)
+#define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6))
+
+#define LABIBB_ENABLE_TIME 1000
+#define LAB_POLL_ENABLED_TIME (LABIBB_ENABLE_TIME * 2)
+#define IBB_POLL_ENABLED_TIME (LABIBB_ENABLE_TIME * 10)
+#define LABIBB_OFF_ON_DELAY (8200)
+
+struct labibb_regulator {
+ struct regulator_desc desc;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev;
+ u16 base;
+ u8 type;
+};
+
+struct labibb_regulator_data {
+ u16 base;
+ const char *name;
+ u8 type;
+ unsigned int poll_enabled_time;
+};
+
+static int qcom_labibb_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ unsigned int val;
+ struct labibb_regulator *reg = rdev_get_drvdata(rdev);
+
+ ret = regmap_read(reg->regmap, reg->base + REG_LABIBB_STATUS1, &val);
+ if (ret < 0) {
+ dev_err(reg->dev, "Read register failed ret = %d\n", ret);
+ return ret;
+ }
+ return !!(val & LABIBB_STATUS1_VREG_OK_BIT);
+}
+
+static int qcom_labibb_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct labibb_regulator *reg = rdev_get_drvdata(rdev);
+
+ ret = regulator_enable_regmap(rdev);
+ if (ret < 0)
+ dev_err(reg->dev, "Write failed: enable %s regulator\n",
+ reg->desc.name);
+
+ return ret;
+}
+
+static int qcom_labibb_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+ struct labibb_regulator *reg = rdev_get_drvdata(rdev);
+
+ ret = regulator_disable_regmap(rdev);
+ if (ret < 0)
+ dev_err(reg->dev, "Disable failed: disable %s\n",
+ reg->desc.name);
+
+ return ret;
+}
+
+static struct regulator_ops qcom_labibb_ops = {
+ .enable = qcom_labibb_regulator_enable,
+ .disable = qcom_labibb_regulator_disable,
+ .is_enabled = qcom_labibb_regulator_is_enabled,
+};
+
+static int register_labibb_regulator(struct labibb_regulator *reg,
+ const struct labibb_regulator_data *reg_data,
+ struct device_node *of_node)
+{
+ struct regulator_config cfg = {};
+
+ reg->base = reg_data->base;
+ reg->type = reg_data->type;
+ reg->desc.enable_reg = reg->base + REG_LABIBB_ENABLE_CTL;
+ reg->desc.enable_val = LABIBB_CONTROL_ENABLE;
+ reg->desc.of_match = reg_data->name;
+ reg->desc.name = reg_data->name;
+ reg->desc.owner = THIS_MODULE;
+ reg->desc.type = REGULATOR_VOLTAGE;
+ reg->desc.ops = &qcom_labibb_ops;
+
+ reg->desc.enable_time = LABIBB_ENABLE_TIME;
+ reg->desc.poll_enabled_time = reg_data->poll_enabled_time;
+ reg->desc.off_on_delay = LABIBB_OFF_ON_DELAY;
+
+ cfg.dev = reg->dev;
+ cfg.driver_data = reg;
+ cfg.regmap = reg->regmap;
+ cfg.of_node = of_node;
+
+ reg->rdev = devm_regulator_register(reg->dev, ®->desc, &cfg);
+ return PTR_ERR_OR_ZERO(reg->rdev);
+}
+
+static const struct labibb_regulator_data pmi8998_labibb_data[] = {
+ {0xde00, "lab", QCOM_LAB_TYPE, LAB_POLL_ENABLED_TIME},
+ {0xdc00, "ibb", QCOM_IBB_TYPE, IBB_POLL_ENABLED_TIME},
+ { },
+};
+
+static const struct of_device_id qcom_labibb_match[] = {
+ { .compatible = "qcom,pmi8998-lab-ibb", .data = &pmi8998_labibb_data},
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_labibb_match);
+
+static int qcom_labibb_regulator_probe(struct platform_device *pdev)
+{
+ struct labibb_regulator *labibb_reg;
+ struct device *dev;
+ struct device_node *child;
+ const struct of_device_id *match;
+ const struct labibb_regulator_data *reg_data;
+ struct regmap *reg_regmap;
+ unsigned int type;
+ int ret;
+
+ reg_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!reg_regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -ENODEV;
+ }
+
+ dev = &pdev->dev;
+
+ match = of_match_device(qcom_labibb_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ for (reg_data = match->data; reg_data->name; reg_data++) {
+ child = of_get_child_by_name(pdev->dev.of_node, reg_data->name);
+
+ /* TODO: This validates if the type of regulator is indeed
+ * what's mentioned in DT.
+ * I'm not sure if this is needed, but we'll keep it for now.
+ */
+ ret = regmap_read(reg_regmap, reg_data->base + REG_PERPH_TYPE,
+ &type);
+ if (ret < 0) {
+ dev_err(dev,
+ "Peripheral type read failed ret=%d\n",
+ ret);
+ return -EINVAL;
+ }
+
+ if ((type != QCOM_LAB_TYPE) && (type != QCOM_IBB_TYPE)) {
+ dev_err(dev,
+ "qcom_labibb: unknown peripheral type\n");
+ return -EINVAL;
+ } else if (type != reg_data->type) {
+ dev_err(dev,
+ "qcom_labibb: type %x doesn't match DT %x\n",
+ type, reg_data->type);
+ return -EINVAL;
+ }
+
+ labibb_reg = devm_kzalloc(&pdev->dev, sizeof(*labibb_reg),
+ GFP_KERNEL);
+ if (!labibb_reg)
+ return -ENOMEM;
+
+ labibb_reg->regmap = reg_regmap;
+ labibb_reg->dev = dev;
+
+ switch (reg_data->type) {
+ case QCOM_LAB_TYPE:
+ labibb_reg->desc.enable_mask = LAB_ENABLE_CTL_MASK;
+ break;
+
+ case QCOM_IBB_TYPE:
+ labibb_reg->desc.enable_mask = IBB_ENABLE_CTL_MASK;
+ break;
+ }
+
+ dev_info(dev, "Registering %s regulator\n", child->full_name);
+
+ ret = register_labibb_regulator(labibb_reg, reg_data, child);
+ if (ret < 0) {
+ dev_err(dev,
+ "qcom_labibb: error registering %s : %d\n",
+ child->full_name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver qcom_labibb_regulator_driver = {
+ .driver = {
+ .name = "qcom-lab-ibb-regulator",
+ .of_match_table = qcom_labibb_match,
+ },
+ .probe = qcom_labibb_regulator_probe,
+};
+module_platform_driver(qcom_labibb_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm labibb driver");
+MODULE_LICENSE("GPL v2");
--
2.26.2
^ permalink raw reply related
* [PATCH v3 5/5] regulator: qcom: labibb: Add SC interrupt handling
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
In-Reply-To: <20200528154625.17742-1-sumit.semwal@linaro.org>
From: Nisha Kumari <nishakumari@codeaurora.org>
Add Short circuit interrupt handling and recovery for the lab and
ibb regulators on qcom platforms.
The client panel drivers need to register for REGULATOR_EVENT_OVER_CURRENT
notification which will be triggered on short circuit. They should
try to enable the regulator once, and if it doesn't get enabled,
handle shutting down the panel accordingly.
Signed-off-by: Nisha Kumari <nishakumari@codeaurora.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
--
v2: sumits: reworked handling to user regmap_read_poll_timeout, and handle it
per-regulator instead of clearing both lab and ibb errors on either irq
triggering. Also added REGULATOR_EVENT_OVER_CURRENT handling and
notification to clients.
v3: sumits: updated as per review comments of v2: removed spurious check for
irq in handler and some unused variables; inlined some of the code,
omitted IRQF_TRIGGER_RISING as it's coming from DT.
---
drivers/regulator/qcom-labibb-regulator.c | 92 +++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c
index 634d08461c6e..695ffac71e81 100644
--- a/drivers/regulator/qcom-labibb-regulator.c
+++ b/drivers/regulator/qcom-labibb-regulator.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2020, The Linux Foundation. All rights reserved.
+#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of.h>
@@ -18,6 +19,7 @@
#define REG_LABIBB_ENABLE_CTL 0x46
#define LABIBB_STATUS1_VREG_OK_BIT BIT(7)
#define LABIBB_CONTROL_ENABLE BIT(7)
+#define LABIBB_STATUS1_SC_DETECT_BIT BIT(6)
#define LAB_ENABLE_CTL_MASK BIT(7)
#define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6))
@@ -27,12 +29,17 @@
#define IBB_POLL_ENABLED_TIME (LABIBB_ENABLE_TIME * 10)
#define LABIBB_OFF_ON_DELAY (8200)
+#define POLLING_SCP_DONE_INTERVAL_US 5000
+#define POLLING_SCP_TIMEOUT 16000
+
struct labibb_regulator {
struct regulator_desc desc;
struct device *dev;
struct regmap *regmap;
struct regulator_dev *rdev;
u16 base;
+ int sc_irq;
+ int vreg_enabled;
u8 type;
};
@@ -65,6 +72,8 @@ static int qcom_labibb_regulator_enable(struct regulator_dev *rdev)
if (ret < 0)
dev_err(reg->dev, "Write failed: enable %s regulator\n",
reg->desc.name);
+ else
+ reg->vreg_enabled = 1;
return ret;
}
@@ -78,6 +87,8 @@ static int qcom_labibb_regulator_disable(struct regulator_dev *rdev)
if (ret < 0)
dev_err(reg->dev, "Disable failed: disable %s\n",
reg->desc.name);
+ else
+ reg->vreg_enabled = 0;
return ret;
}
@@ -88,11 +99,70 @@ static struct regulator_ops qcom_labibb_ops = {
.is_enabled = qcom_labibb_regulator_is_enabled,
};
+static irqreturn_t labibb_sc_err_handler(int irq, void *_reg)
+{
+ int ret;
+ u16 reg;
+ unsigned int val;
+ struct labibb_regulator *labibb_reg = _reg;
+ bool in_sc_err, scp_done = false;
+
+ ret = regmap_read(labibb_reg->regmap,
+ labibb_reg->base + REG_LABIBB_STATUS1, &val);
+ if (ret < 0) {
+ dev_err(labibb_reg->dev, "sc_err_irq: Read failed, ret=%d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(labibb_reg->dev, "%s SC error triggered! STATUS1 = %d\n",
+ labibb_reg->desc.name, val);
+
+ in_sc_err = !!(val & LABIBB_STATUS1_SC_DETECT_BIT);
+
+ /*
+ * The SC(short circuit) fault would trigger PBS(Portable Batch
+ * System) to disable regulators for protection. This would
+ * cause the SC_DETECT status being cleared so that it's not
+ * able to get the SC fault status.
+ * Check if the regulator is enabled in the driver but
+ * disabled in hardware, this means a SC fault had happened
+ * and SCP handling is completed by PBS.
+ */
+ if (!in_sc_err) {
+
+ reg = labibb_reg->base + REG_LABIBB_ENABLE_CTL;
+
+ ret = regmap_read_poll_timeout(labibb_reg->regmap,
+ reg, val,
+ !(val & LABIBB_CONTROL_ENABLE),
+ POLLING_SCP_DONE_INTERVAL_US,
+ POLLING_SCP_TIMEOUT);
+
+ if (!ret && labibb_reg->vreg_enabled) {
+ dev_dbg(labibb_reg->dev,
+ "%s has been disabled by SCP\n",
+ labibb_reg->desc.name);
+ scp_done = true;
+ }
+ }
+
+ if (in_sc_err || scp_done) {
+ regulator_lock(labibb_reg->rdev);
+ regulator_notifier_call_chain(labibb_reg->rdev,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+ regulator_unlock(labibb_reg->rdev);
+ }
+ return IRQ_HANDLED;
+}
+
static int register_labibb_regulator(struct labibb_regulator *reg,
const struct labibb_regulator_data *reg_data,
struct device_node *of_node)
{
struct regulator_config cfg = {};
+ int ret;
reg->base = reg_data->base;
reg->type = reg_data->type;
@@ -108,6 +178,28 @@ static int register_labibb_regulator(struct labibb_regulator *reg,
reg->desc.poll_enabled_time = reg_data->poll_enabled_time;
reg->desc.off_on_delay = LABIBB_OFF_ON_DELAY;
+ reg->sc_irq = -EINVAL;
+ ret = of_irq_get_byname(of_node, "sc-err");
+ if (ret < 0) {
+ dev_err(reg->dev, "Unable to get sc-err, ret = %d\n",
+ ret);
+ return ret;
+ } else
+ reg->sc_irq = ret;
+
+ if (reg->sc_irq > 0) {
+ ret = devm_request_threaded_irq(reg->dev,
+ reg->sc_irq,
+ NULL, labibb_sc_err_handler,
+ IRQF_ONESHOT,
+ "sc-err", reg);
+ if (ret) {
+ dev_err(reg->dev, "Failed to register sc-err irq ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
cfg.dev = reg->dev;
cfg.driver_data = reg;
cfg.regmap = reg->regmap;
--
2.26.2
^ permalink raw reply related
* [PATCH v3 3/5] arm64: dts: qcom: pmi8998: Add nodes for LAB and IBB regulators
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
In-Reply-To: <20200528154625.17742-1-sumit.semwal@linaro.org>
From: Nisha Kumari <nishakumari@codeaurora.org>
This patch adds devicetree nodes for LAB and IBB regulators.
Signed-off-by: Nisha Kumari <nishakumari@codeaurora.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
--
v2: sumits: updated for better compatible string and names
v3: sumits: updated interrupt-names as per review comments
---
arch/arm64/boot/dts/qcom/pmi8998.dtsi | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index 23f9146a161e..1a72fe92f1a6 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -25,5 +25,19 @@ pmi8998_lsid1: pmic@3 {
reg = <0x3 SPMI_USID>;
#address-cells = <1>;
#size-cells = <0>;
+
+ labibb: labibb {
+ compatible = "qcom,pmi8998-lab-ibb";
+
+ ibb: ibb {
+ interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "sc-err";
+ };
+
+ lab: lab {
+ interrupts = <0x3 0xde 0x0 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "sc-err";
+ };
+ };
};
};
--
2.26.2
^ permalink raw reply related
* [PATCH v3 2/5] dt-bindings: regulator: Add labibb regulator
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
In-Reply-To: <20200528154625.17742-1-sumit.semwal@linaro.org>
From: Nisha Kumari <nishakumari@codeaurora.org>
Adding the devicetree binding for labibb regulator.
Signed-off-by: Nisha Kumari <nishakumari@codeaurora.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
--
v2: updated for better compatible string and names.
v3: moved to yaml
---
.../regulator/qcom-labibb-regulator.yaml | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml
diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml
new file mode 100644
index 000000000000..5406601ecd65
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/qcom-labibb-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm's LAB(LCD AMOLED Boost)/IBB(Inverting Buck Boost) Regulator
+
+maintainers:
+ - Sumit Semwal <sumit.semwal@linaro.org>
+
+description:
+ LAB can be used as a positive boost power supply and IBB can be used as a
+ negative boost power supply for display panels. Currently implemented for
+ pmi8998.
+
+allOf:
+ - $ref: "regulator.yaml#"
+
+properties:
+ compatible:
+ const: qcom,pmi8998-lab-ibb
+
+ lab:
+ type: object
+ interrupts:
+ items:
+ - description: Short-circuit interrupt for lab.
+ interrupt-names:
+ maxItems: 1
+ items:
+ - const: sc-err
+
+ ibb:
+ type: object
+ interrupts:
+ items:
+ - description: Short-circuit interrupt for lab.
+ interrupt-names:
+ maxItems: 1
+ items:
+ - const: sc-err
+
+required:
+ - compatible
+
+examples:
+ pmi8998_lsid1: pmic@3 {
+ labibb {
+ compatible = "qcom,pmi8998-lab-ibb";
+
+ lab: lab {
+ interrupts = <0x3 0xde 0x0 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "lab-sc-err";
+ };
+
+ ibb: ibb {
+ interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "ibb-sc-err";
+ };
+
+ };
+ };
--
2.26.2
^ permalink raw reply related
* [PATCH v3 1/5] regulator: Allow regulators to verify enabled during enable()
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
In-Reply-To: <20200528154625.17742-1-sumit.semwal@linaro.org>
Some regulators might need to verify that they have indeed been enabled
after the enable() call is made and enable_time delay has passed.
This is implemented by repeatedly checking is_enabled() upto
poll_enabled_time, waiting for the already calculated enable delay in
each iteration.
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
---
drivers/regulator/core.c | 28 ++++++++++++++++++++++++++++
include/linux/regulator/driver.h | 5 +++++
2 files changed, 33 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 7486f6e4e613..06199f182114 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2409,6 +2409,34 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
_regulator_enable_delay(delay);
+ /* If set, poll upto poll_enabled_time uS to see if the regulator
+ * actually got enabled.
+ * For each iteration, wait for the enable_time delay calculated
+ * above already.
+ * If the regulator isn't enabled after poll_enabled_time has
+ * expired, return -ETIMEDOUT.
+ */
+ if (rdev->desc->poll_enabled_time) {
+ unsigned int time_remaining = rdev->desc->poll_enabled_time;
+
+ while (time_remaining > 0) {
+ /* We've already waited for enable_time above;
+ * so we can start with immediate check of the
+ * status of the regulator.
+ */
+ if (rdev->desc->ops->is_enabled(rdev))
+ break;
+
+ _regulator_enable_delay(delay);
+ time_remaining -= delay;
+ }
+
+ if (time_remaining <= 0) {
+ rdev_err(rdev, "Enabled check failed.\n");
+ return -ETIMEDOUT;
+ }
+ }
+
trace_regulator_enable_complete(rdev_get_name(rdev));
return 0;
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 29d920516e0b..bb50e943010f 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -322,6 +322,9 @@ enum regulator_type {
* @enable_time: Time taken for initial enable of regulator (in uS).
* @off_on_delay: guard time (in uS), before re-enabling a regulator
*
+ * @poll_enabled_time: Maximum time (in uS) to poll if the regulator is
+ * actually enabled, after enable() call
+ *
* @of_map_mode: Maps a hardware mode defined in a DeviceTree to a standard mode
*/
struct regulator_desc {
@@ -389,6 +392,8 @@ struct regulator_desc {
unsigned int off_on_delay;
+ unsigned int poll_enabled_time;
+
unsigned int (*of_map_mode)(unsigned int mode);
};
--
2.26.2
^ permalink raw reply related
* [PATCH v3 0/5] Qualcomm labibb regulator driver
From: Sumit Semwal @ 2020-05-28 15:46 UTC (permalink / raw)
To: agross, bjorn.andersson, lgirdwood, broonie, robh+dt
Cc: nishakumari, linux-arm-msm, linux-kernel, devicetree, kgunda,
rnayak, Sumit Semwal
This series adds a driver for LAB/IBB regulators found on some Qualcomm SoCs.
These regulators provide positive and/or negative boost power supplies
for LCD/LED display panels connected to the SoC.
This series adds the support for pmi8998 PMIC found in SDM845 family of SoCs.
Changes from v2:
- Review comments from v2
- Moved the poll-to-check-enabled functionality to regulator core.
- Used more core features to simplify enable/disable functions.
- Moved the devicetree binding to yaml.
- Updated interrupt-names and simplified handling.
Changes from v1:
- Incorporated review comments from v1
- Changed from virtual-regulator based handling to individual regulator based
handling.
- Reworked the core to merge most of enable/disable functions, combine the
regulator_ops into one and allow for future variations.
- is_enabled() is now _really_ is_enabled()
- Simplified the SC interrupt handling - use regmap_read_poll_timeout,
REGULATOR_EVENT_OVER_CURRENT handling and notification to clients.
Nisha Kumari (4):
dt-bindings: regulator: Add labibb regulator
arm64: dts: qcom: pmi8998: Add nodes for LAB and IBB regulators
regulator: qcom: Add labibb driver
regulator: qcom: labibb: Add SC interrupt handling
Sumit Semwal (1):
regulator: Allow regulators to verify enabled during enable()
.../regulator/qcom-labibb-regulator.yaml | 63 ++++
arch/arm64/boot/dts/qcom/pmi8998.dtsi | 14 +
drivers/regulator/Kconfig | 10 +
drivers/regulator/Makefile | 1 +
drivers/regulator/core.c | 28 ++
drivers/regulator/qcom-labibb-regulator.c | 316 ++++++++++++++++++
include/linux/regulator/driver.h | 5 +
7 files changed, 437 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml
create mode 100644 drivers/regulator/qcom-labibb-regulator.c
--
2.26.2
^ permalink raw reply
* [PATCH v10 2/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:39 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200528153929.46859-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++++++++
3 files changed, 756 insertions(+)
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index a80a46bb5b8b..75ab2afb78cf 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -457,6 +457,14 @@ config MTD_NAND_CADENCE
Enable the driver for NAND flash on platforms using a Cadence NAND
controller.
+config MTD_NAND_INTEL_LGM
+ tristate "Support for NAND controller on Intel LGM SoC"
+ depends on OF || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Enables support for NAND Flash chips on Intel's LGM SoC.
+ NAND flash controller interfaced through the External Bus Unit.
+
comment "Misc"
config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2d136b158fb7..bfc8fe4d2cb0 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o
+obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c
new file mode 100644
index 000000000000..564d28978943
--- /dev/null
+++ b/drivers/mtd/nand/raw/intel-nand-controller.c
@@ -0,0 +1,747 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2020 Intel Corporation. */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand.h>
+#include <linux/resource.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+#define EBU_CLC 0x000
+#define EBU_CLC_RST 0x00000000u
+
+#define EBU_ADDR_SEL(n) (0x20 + (n) * 4)
+/* 5 bits 26:22 included for comparison in the ADDR_SELx */
+#define EBU_ADDR_MASK(x) ((x) << 4)
+#define EBU_ADDR_SEL_REGEN 0x1
+
+#define EBU_BUSCON(n) (0x60 + (n) * 4)
+#define EBU_BUSCON_CMULT_V4 0x1
+#define EBU_BUSCON_RECOVC(n) ((n) << 2)
+#define EBU_BUSCON_HOLDC(n) ((n) << 4)
+#define EBU_BUSCON_WAITRDC(n) ((n) << 6)
+#define EBU_BUSCON_WAITWRC(n) ((n) << 8)
+#define EBU_BUSCON_BCGEN_CS 0x0
+#define EBU_BUSCON_SETUP_EN BIT(22)
+#define EBU_BUSCON_ALEC 0xC000
+
+#define EBU_CON 0x0B0
+#define EBU_CON_NANDM_EN BIT(0)
+#define EBU_CON_NANDM_DIS 0x0
+#define EBU_CON_CSMUX_E_EN BIT(1)
+#define EBU_CON_ALE_P_LOW BIT(2)
+#define EBU_CON_CLE_P_LOW BIT(3)
+#define EBU_CON_CS_P_LOW BIT(4)
+#define EBU_CON_SE_P_LOW BIT(5)
+#define EBU_CON_WP_P_LOW BIT(6)
+#define EBU_CON_PRE_P_LOW BIT(7)
+#define EBU_CON_IN_CS_S(n) ((n) << 8)
+#define EBU_CON_OUT_CS_S(n) ((n) << 10)
+#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18)
+
+#define EBU_WAIT 0x0B4
+#define EBU_WAIT_RDBY BIT(0)
+#define EBU_WAIT_WR_C BIT(3)
+
+#define HSNAND_CTL1 0x110
+#define HSNAND_CTL1_ADDR_SHIFT 24
+
+#define HSNAND_CTL2 0x114
+#define HSNAND_CTL2_ADDR_SHIFT 8
+#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16)
+
+#define HSNAND_INT_MSK_CTL 0x124
+#define HSNAND_INT_MSK_CTL_WR_C BIT(4)
+
+#define HSNAND_INT_STA 0x128
+#define HSNAND_INT_STA_WR_C BIT(4)
+
+#define HSNAND_CTL 0x130
+#define HSNAND_CTL_ENABLE_ECC BIT(0)
+#define HSNAND_CTL_GO BIT(2)
+#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n))
+#define HSNAND_CTL_RW_READ 0x0
+#define HSNAND_CTL_RW_WRITE BIT(10)
+#define HSNAND_CTL_ECC_OFF_V8TH BIT(11)
+#define HSNAND_CTL_CKFF_EN 0x0
+#define HSNAND_CTL_MSG_EN BIT(17)
+
+#define HSNAND_PARA0 0x13c
+#define HSNAND_PARA0_PAGE_V8192 0x3
+#define HSNAND_PARA0_PIB_V256 (0x3 << 4)
+#define HSNAND_PARA0_BYP_EN_NP 0x0
+#define HSNAND_PARA0_BYP_DEC_NP 0x0
+#define HSNAND_PARA0_TYPE_ONFI BIT(18)
+#define HSNAND_PARA0_ADEP_EN BIT(21)
+
+#define HSNAND_CMSG_0 0x150
+#define HSNAND_CMSG_1 0x154
+
+#define HSNAND_ALE_OFFS BIT(2)
+#define HSNAND_CLE_OFFS BIT(3)
+#define HSNAND_CS_OFFS BIT(4)
+
+#define HSNAND_ECC_OFFSET 0x008
+
+#define NAND_DATA_IFACE_CHECK_ONLY -1
+
+#define MAX_CS 2
+
+struct ebu_nand_cs {
+ void __iomem *chipaddr;
+ dma_addr_t nand_pa;
+ u32 addr_sel;
+};
+
+struct ebu_nand_controller {
+ struct nand_controller controller;
+ struct nand_chip chip;
+ struct device *dev;
+ void __iomem *ebu;
+ void __iomem *hsnand;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ struct completion dma_access_complete;
+ unsigned long clk_rate;
+ struct clk *clk;
+ u32 nd_para0;
+ u8 cs_num;
+ struct ebu_nand_cs cs[MAX_CS];
+};
+
+static inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip)
+{
+ return container_of(chip, struct ebu_nand_controller, chip);
+}
+
+static u8 ebu_nand_readb(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+ u8 val;
+
+ val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+
+ return val;
+}
+
+static void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+
+ writeb(value, ebu_host->cs[cs_num].chipaddr + offset);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+}
+
+static void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = ebu_nand_readb(chip);
+}
+
+static void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]);
+}
+
+static void ebu_nand_disable(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+
+ writel(0, ebu_host->ebu + EBU_CON);
+}
+
+static void ebu_select_chip(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_con = ebu_host->ebu + EBU_CON;
+ u32 cs = ebu_host->cs_num;
+
+ writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW |
+ EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW |
+ EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) |
+ EBU_CON_LAT_EN_CS_P, nand_con);
+}
+
+static void ebu_nand_setup_timing(struct ebu_nand_controller *ctrl,
+ const struct nand_sdr_timings *timings)
+{
+ unsigned int rate = clk_get_rate(ctrl->clk) / 1000000;
+ unsigned int period = DIV_ROUND_UP(1000000, rate);
+ u32 trecov, thold, twrwait, trdwait;
+ u32 reg = 0;
+
+ trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_RECOVC(trecov);
+
+ thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period);
+ reg |= EBU_BUSCON_HOLDC(thold);
+
+ trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_WAITRDC(trdwait);
+
+ twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period);
+ reg |= EBU_BUSCON_WAITWRC(twrwait);
+
+ reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC |
+ EBU_BUSCON_SETUP_EN;
+
+ writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num));
+}
+
+static int ebu_nand_setup_data_interface(struct nand_chip *chip, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ const struct nand_sdr_timings *timings;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ ebu_nand_setup_timing(ctrl, timings);
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = HSNAND_ECC_OFFSET;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = {
+ .ecc = ebu_nand_ooblayout_ecc,
+ .free = ebu_nand_ooblayout_free,
+};
+
+static void ebu_dma_rx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_rx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static void ebu_dma_tx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_tx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
+ const u8 *buf, u32 len)
+{
+ struct dma_async_tx_descriptor *tx;
+ struct completion *dma_completion;
+ dma_async_tx_callback callback;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ dma_addr_t buf_dma;
+ int ret;
+ u32 timeout;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ chan = ebu_host->dma_rx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_rx_callback;
+ } else {
+ chan = ebu_host->dma_tx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_tx_callback;
+ }
+
+ buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
+ if (dma_mapping_error(chan->device->dev, buf_dma)) {
+ dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags);
+ if (!tx)
+ return -ENXIO;
+
+ tx->callback = callback;
+ tx->callback_param = ebu_host;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie);
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ init_completion(dma_completion);
+ dma_async_issue_pending(chan);
+
+ /* Wait DMA to finish the data transfer.*/
+ timeout =
+ wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
+ if (!timeout) {
+ dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n",
+ dmaengine_tx_status(chan, cookie, NULL));
+ dmaengine_terminate_sync(chan);
+ ret = -ETIMEDOUT;
+ goto err_unmap;
+ }
+
+ return 0;
+
+err_unmap:
+ dma_unmap_single(ebu_host->dev, buf_dma, len, dir);
+
+ return ret;
+}
+
+static void ebu_nand_trigger(struct ebu_nand_controller *ebu_host,
+ int page, u32 cmd)
+{
+ unsigned int val;
+
+ val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT;
+ writel(val, ebu_host->hsnand + HSNAND_CTL1);
+ val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5;
+ writel(val, ebu_host->hsnand + HSNAND_CTL2);
+
+ writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0);
+
+ /* clear first, will update later */
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0);
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1);
+
+ writel(HSNAND_INT_MSK_CTL_WR_C,
+ ebu_host->hsnand + HSNAND_INT_MSK_CTL);
+
+ val = cmd == NAND_CMD_READ0 ? HSNAND_CTL_RW_READ : HSNAND_CTL_RW_WRITE;
+
+ writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN |
+ HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) |
+ HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val,
+ ebu_host->hsnand + HSNAND_CTL);
+}
+
+static int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ int ret, x;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0);
+
+ ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ chip->ecc.read_oob(chip, page);
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA;
+ int ret, val, x;
+ u32 reg;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN);
+
+ ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ reg = (chip->oob_poi[3] << 24) | (chip->oob_poi[2] << 16) |
+ (chip->oob_poi[1] << 8) | chip->oob_poi[0];
+
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_0);
+
+ reg = (chip->oob_poi[7] << 24) | (chip->oob_poi[6] << 16) |
+ (chip->oob_poi[5] << 8) | chip->oob_poi[4];
+
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_1);
+ }
+
+ ret = readl_poll_timeout_atomic(int_sta, val,
+ !(val & HSNAND_INT_STA_WR_C), 10, 1000);
+ if (ret)
+ return -EIO;
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, };
+
+static int ebu_nand_attach_chip(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ u32 eccsize, eccsteps, eccbytes, ecctotal, pagesize, pg_per_blk;
+ u32 eccstrength = chip->ecc.strength;
+ u32 writesize = mtd->writesize;
+ u32 blocksize = mtd->erasesize;
+ int start, val, i;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ /* Check whether eccsize is 0x0 or wrong. assign eccsize = 512 if YES */
+ if (!chip->ecc.size)
+ chip->ecc.size = 512;
+ eccsize = chip->ecc.size;
+
+ switch (eccsize) {
+ case 512:
+ start = 1;
+ if (!eccstrength)
+ eccstrength = 4;
+ break;
+ case 1024:
+ start = 4;
+ if (!eccstrength)
+ eccstrength = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i = round_up(start + 1, 4);
+ for (val = start; val < i; val++) {
+ if (eccstrength == ecc_strength[val])
+ break;
+ }
+ if (val == i)
+ return -EINVAL;
+
+ if (eccstrength == 8)
+ eccbytes = 14;
+ else
+ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
+
+ eccsteps = writesize / eccsize;
+ ecctotal = eccsteps * eccbytes;
+ if ((ecctotal + 8) > mtd->oobsize)
+ return -ERANGE;
+
+ chip->ecc.total = ecctotal;
+ pagesize = fls(writesize >> 11);
+ if (pagesize > HSNAND_PARA0_PAGE_V8192)
+ return -ERANGE;
+
+ pg_per_blk = fls((blocksize / writesize) >> 6) << 4;
+ if (pg_per_blk > HSNAND_PARA0_PIB_V256)
+ return -ERANGE;
+
+ ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP |
+ HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN |
+ HSNAND_PARA0_TYPE_ONFI | (val << 29);
+
+ mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops);
+ chip->ecc.read_page = ebu_nand_read_page_hwecc;
+ chip->ecc.write_page = ebu_nand_write_page_hwecc;
+
+ return 0;
+}
+
+static int ebu_nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op, bool check_only)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ const struct nand_op_instr *instr = NULL;
+ unsigned int op_id;
+ int i, time_out, ret = 0;
+ u32 stat;
+
+ ebu_select_chip(chip);
+
+ for (op_id = 0; op_id < op->ninstrs; op_id++) {
+ instr = &op->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.cmd.opcode);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ ebu_nand_writeb(chip,
+ HSNAND_ALE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.addr.addrs[i]);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ebu_read_buf(chip, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ebu_write_buf(chip, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ time_out = instr->ctx.waitrdy.timeout_ms * 1000;
+ ret = readl_poll_timeout(ctrl->ebu + EBU_WAIT,
+ stat, stat & EBU_WAIT_RDBY,
+ 20, time_out);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct nand_controller_ops ebu_nand_controller_ops = {
+ .attach_chip = ebu_nand_attach_chip,
+ .exec_op = ebu_nand_exec_op,
+ .setup_data_interface = ebu_nand_setup_data_interface,
+};
+
+static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
+{
+ if (ebu_host->dma_rx)
+ dma_release_channel(ebu_host->dma_rx);
+
+ if (ebu_host->dma_tx)
+ dma_release_channel(ebu_host->dma_tx);
+}
+
+static int ebu_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ebu_nand_controller *ebu_host;
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ struct resource *res;
+ char *resname;
+ int ret, i;
+ u32 reg;
+
+ ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
+ if (!ebu_host)
+ return -ENOMEM;
+
+ ebu_host->dev = dev;
+ nand_controller_init(&ebu_host->controller);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand");
+ ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->ebu))
+ return PTR_ERR(ebu_host->ebu);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand");
+ ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->hsnand))
+ return PTR_ERR(ebu_host->hsnand);
+
+ ret = device_property_read_u32(dev, "nand,cs", ®);
+ if (ret) {
+ dev_err(dev, "failed to get chip select: %d\n", ret);
+ return ret;
+ }
+ ebu_host->cs_num = reg;
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ if (!res)
+ return -EINVAL;
+ ebu_host->cs[i].chipaddr = devm_ioremap_resource(dev, res);
+ ebu_host->cs[i].nand_pa = res->start;
+ if (IS_ERR(ebu_host->cs[i].chipaddr))
+ return PTR_ERR(ebu_host->cs[i].chipaddr);
+ }
+
+ ebu_host->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ebu_host->clk)) {
+ ret = PTR_ERR(ebu_host->clk);
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ebu_host->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+ ebu_host->clk_rate = clk_get_rate(ebu_host->clk);
+
+ ebu_host->dma_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(ebu_host->dma_tx)) {
+ ret = PTR_ERR(ebu_host->dma_tx);
+ dev_err(dev, "DMA tx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ ebu_host->dma_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(ebu_host->dma_rx)) {
+ ret = PTR_ERR(ebu_host->dma_rx);
+ dev_err(dev, "DMA rx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ if (!res)
+ return -EINVAL;
+ ebu_host->cs[i].addr_sel = res->start;
+ writel(ebu_host->cs[i].addr_sel | EBU_ADDR_MASK(5) |
+ EBU_ADDR_SEL_REGEN, ebu_host->ebu + EBU_ADDR_SEL(i));
+ }
+
+ nand_set_flash_node(&ebu_host->chip, dev->of_node);
+ mtd = nand_to_mtd(&ebu_host->chip);
+ mtd->dev.parent = dev;
+ ebu_host->dev = dev;
+
+ platform_set_drvdata(pdev, ebu_host);
+ nand_set_controller_data(&ebu_host->chip, ebu_host);
+
+ nand = &ebu_host->chip;
+ nand->controller = &ebu_host->controller;
+ nand->controller->ops = &ebu_nand_controller_ops;
+
+ /* Scan to find existence of the device */
+ ret = nand_scan(&ebu_host->chip, 1);
+ if (ret)
+ goto err_cleanup_dma;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_clean_nand;
+
+ return 0;
+
+err_clean_nand:
+ nand_cleanup(&ebu_host->chip);
+err_cleanup_dma:
+ ebu_dma_cleanup(ebu_host);
+ clk_disable_unprepare(ebu_host->clk);
+
+ return ret;
+}
+
+static int ebu_nand_remove(struct platform_device *pdev)
+{
+ struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
+ nand_cleanup(&ebu_host->chip);
+ ebu_nand_disable(&ebu_host->chip);
+ ebu_dma_cleanup(ebu_host);
+ clk_disable_unprepare(ebu_host->clk);
+
+ return 0;
+}
+
+static const struct of_device_id ebu_nand_match[] = {
+ { .compatible = "intel,nand-controller", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ebu_nand_match);
+
+static struct platform_driver ebu_nand_driver = {
+ .probe = ebu_nand_probe,
+ .remove = ebu_nand_remove,
+ .driver = {
+ .name = "intel-nand-controller",
+ .of_match_table = ebu_nand_match,
+ },
+
+};
+module_platform_driver(ebu_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>");
+MODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver");
--
2.11.0
^ permalink raw reply related
* [PATCH v10 1/2] dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:39 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200528153929.46859-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
Add YAML file for dt-bindings to support NAND Flash Controller
on Intel's Lightning Mountain SoC.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 93 ++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
diff --git a/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
new file mode 100644
index 000000000000..afecc9920e04
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/intel,lgm-nand.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel LGM SoC NAND Controller Device Tree Bindings
+
+allOf:
+ - $ref: "nand-controller.yaml"
+
+maintainers:
+ - Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
+
+properties:
+ compatible:
+ const: intel,lgm-nand-controller
+
+ reg:
+ maxItems: 6
+
+ reg-names:
+ items:
+ - const: ebunand
+ - const: hsnand
+ - const: nand_cs0
+ - const: nand_cs1
+ - const: addr_sel0
+ - const: addr_sel1
+
+ clocks:
+ maxItems: 1
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+patternProperties:
+ "^nand@[a-f0-9]+$":
+ type: object
+ properties:
+ reg:
+ minimum: 0
+ maximum: 7
+
+ nand-ecc-mode: true
+
+ nand-ecc-algo:
+ const: hw
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - dmas
+ - dma-names
+
+additionalProperties: false
+
+examples:
+ - |
+ nand-controller@e0f00000 {
+ compatible = "intel,lgm-nand";
+ reg = <0xe0f00000 0x100>,
+ <0xe1000000 0x300>,
+ <0xe1400000 0x8000>,
+ <0xe1c00000 0x1000>,
+ <0x17400000 0x4>,
+ <0x17c00000 0x4>;
+ reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
+ "addr_sel0", "addr_sel1";
+ clocks = <&cgu0 125>;
+ dmas = <&dma0 8>, <&dma0 9>;
+ dma-names = "tx", "rx";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ nand@0 {
+ reg = <0>;
+ nand-on-flash-bbt;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+ };
+
+...
--
2.11.0
^ permalink raw reply related
* [PATCH v10 0/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:39 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar,Vadivel MuruganX
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller also supports in-built HW ECC engine.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Thanks Boris, Andy, Arnd and Rob for the review comments and suggestions.
---
v10:
- No Change
v9:
- No change
v8:
- fix the kbuild bot warnings
- correct the typo's
v7:
- indentation issue is fixed
- add error check for retrieve the resource from dt
v6:
- update EBU_ADDR_SELx register base value build it from DT
- Add tabs in in Kconfig
v5:
- replace by 'HSNAND_CLE_OFFS | HSNAND_CS_OFFS' to NAND_WRITE_CMD and NAND_WRITE_ADDR
- remove the unused macros
- update EBU_ADDR_MASK(x) macro
- update the EBU_ADDR_SELx register values to be written
v4:
- add ebu_nand_cs structure for multiple-CS support
- mask/offset encoding for 0x51 value
- update macro HSNAND_CTL_ENABLE_ECC
- drop the op argument and un-used macros.
- updated the datatype and macros
- add function disable nand module
- remove ebu_host->dma_rx = NULL;
- rename MMIO address range variables to ebu and hsnand
- implement ->setup_data_interface()
- update label err_cleanup_nand and err_cleanup_dma
- add return value check in the nand_remove function
- add/remove tabs and spaces as per coding standard
- encoded CS ids by reg property
v3:
- Add depends on MACRO in Kconfig
- file name update in Makefile
- file name update to intel-nand-controller
- modification of MACRO divided like EBU, HSNAND and NAND
- add NAND_ALE_OFFS, NAND_CLE_OFFS and NAND_CS_OFFS
- rename lgm_ to ebu_ and _va suffix is removed in the whole file
- rename structure and varaibles as per review comments.
- remove lgm_read_byte(), lgm_dev_ready() and cmd_ctrl() un-used function
- update in exec_op() as per review comments
- rename function lgm_dma_exit() by lgm_dma_cleanup()
- hardcoded magic value for base and offset replaced by MACRO defined
- mtd_device_unregister() + nand_cleanup() instead of nand_release()
v2:
- implement the ->exec_op() to replaces the legacy hook-up.
- update the commit message
- add MIPS maintainers and xway_nand driver author in CC
v1:
- initial version
dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
---
v10:
- fix bot errors
v9:
- Rob's review comments address
- dual licensed
- compatible change
- add reg-names
- drop clock-names and clock-cells
- correct typo's
v8:
No change
v7:
- Rob's review comments addressed
- dt-schema build issue fixed with upgraded dt-schema
v6:
- Rob's review comments addressed in YAML file
- add addr_sel0 and addr_sel1 reg-names in YAML example
v5:
- add the example in YAML file
v4:
- No change
v3:
- No change
v2:
YAML compatible string update to intel, lgm-nand-controller
v1:
- initial version
Ramuthevar Vadivel Murugan (2):
dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
mtd: rawnand: Add NAND controller support on Intel LGM SoC
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 93 +++
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++
4 files changed, 849 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
--
2.11.0
^ permalink raw reply
* Re: [PATCH v3 09/10] dmaengine: dw: Introduce max burst length hw config
From: Serge Semin @ 2020-05-28 15:40 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Serge Semin, Vinod Koul, Viresh Kumar, Dan Williams,
Alexey Malahov, Thomas Bogendoerfer, Arnd Bergmann, Rob Herring,
linux-mips, devicetree, dmaengine, linux-kernel
In-Reply-To: <20200528145224.GT1634618@smile.fi.intel.com>
On Thu, May 28, 2020 at 05:52:24PM +0300, Andy Shevchenko wrote:
> On Wed, May 27, 2020 at 01:50:20AM +0300, Serge Semin wrote:
> > IP core of the DW DMA controller may be synthesized with different
> > max burst length of the transfers per each channel. According to Synopsis
> > having the fixed maximum burst transactions length may provide some
> > performance gain. At the same time setting up the source and destination
> > multi size exceeding the max burst length limitation may cause a serious
> > problems. In our case the DMA transaction just hangs up. In order to fix
> > this lets introduce the max burst length platform config of the DW DMA
> > controller device and don't let the DMA channels configuration code
> > exceed the burst length hardware limitation.
> >
> > Note the maximum burst length parameter can be detected either in runtime
> > from the DWC parameter registers or from the dedicated DT property.
> > Depending on the IP core configuration the maximum value can vary from
> > channel to channel so by overriding the channel slave max_burst capability
> > we make sure a DMA consumer will get the channel-specific max burst
> > length.
>
> ...
>
> > static void dwc_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
> > {
> > + struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
> >
>
> Perhaps,
>
> /* DesignWare DMA supports burst value from 0 */
> caps->min_burst = 0;
Regarding min_burst being zero. I don't fully understand what it means.
It means no burst or burst with minimum length or what?
In fact DW DMA burst length starts from 1. Remember the burst-length run-time
parameter we were arguing about? Anyway the driver makes sure that both
0 and 1 requested burst length are setup as burst length of 1 in the
CTLx.SRC_MSIZE, CTLx.DST_MSIZE fields.
I agree with the rest of your comments below.
-Sergey
>
> > + caps->max_burst = dwc->max_burst;
> > }
>
> ...
>
> > + *maxburst = clamp(*maxburst, 0U, dwc->max_burst);
>
> Shouldn't we do the same for iDMA 32-bit? Thus, perhaps do it in the core.c?
>
> > *maxburst = *maxburst > 1 ? fls(*maxburst) - 2 : 0;
>
> > + if (!of_property_read_u32_array(np, "snps,max-burst-len", mb,
> > + nr_channels)) {
> > + for (tmp = 0; tmp < nr_channels; tmp++)
> > + pdata->max_burst[tmp] = mb[tmp];
>
> I think we may read directly to the array. This ugly loops were introduced due
> to type mismatch. (See below)
>
> > + } else {
> > + for (tmp = 0; tmp < nr_channels; tmp++)
> > + pdata->max_burst[tmp] = DW_DMA_MAX_BURST;
> > + }
>
> And this will be effectively memset32().
>
> > unsigned char nr_masters;
> > unsigned char data_width[DW_DMA_MAX_NR_MASTERS];
> > unsigned char multi_block[DW_DMA_MAX_NR_CHANNELS];
> > + unsigned int max_burst[DW_DMA_MAX_NR_CHANNELS];
>
> I think we have to stop with this kind of types and use directly what is in the
> properties, i.e.
>
> u32 max_burst[...];
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply
* [PATCH v9 2/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:34 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200528153419.46775-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++++++++
3 files changed, 756 insertions(+)
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index a80a46bb5b8b..75ab2afb78cf 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -457,6 +457,14 @@ config MTD_NAND_CADENCE
Enable the driver for NAND flash on platforms using a Cadence NAND
controller.
+config MTD_NAND_INTEL_LGM
+ tristate "Support for NAND controller on Intel LGM SoC"
+ depends on OF || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Enables support for NAND Flash chips on Intel's LGM SoC.
+ NAND flash controller interfaced through the External Bus Unit.
+
comment "Misc"
config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2d136b158fb7..bfc8fe4d2cb0 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o
+obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c
new file mode 100644
index 000000000000..564d28978943
--- /dev/null
+++ b/drivers/mtd/nand/raw/intel-nand-controller.c
@@ -0,0 +1,747 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2020 Intel Corporation. */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand.h>
+#include <linux/resource.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+#define EBU_CLC 0x000
+#define EBU_CLC_RST 0x00000000u
+
+#define EBU_ADDR_SEL(n) (0x20 + (n) * 4)
+/* 5 bits 26:22 included for comparison in the ADDR_SELx */
+#define EBU_ADDR_MASK(x) ((x) << 4)
+#define EBU_ADDR_SEL_REGEN 0x1
+
+#define EBU_BUSCON(n) (0x60 + (n) * 4)
+#define EBU_BUSCON_CMULT_V4 0x1
+#define EBU_BUSCON_RECOVC(n) ((n) << 2)
+#define EBU_BUSCON_HOLDC(n) ((n) << 4)
+#define EBU_BUSCON_WAITRDC(n) ((n) << 6)
+#define EBU_BUSCON_WAITWRC(n) ((n) << 8)
+#define EBU_BUSCON_BCGEN_CS 0x0
+#define EBU_BUSCON_SETUP_EN BIT(22)
+#define EBU_BUSCON_ALEC 0xC000
+
+#define EBU_CON 0x0B0
+#define EBU_CON_NANDM_EN BIT(0)
+#define EBU_CON_NANDM_DIS 0x0
+#define EBU_CON_CSMUX_E_EN BIT(1)
+#define EBU_CON_ALE_P_LOW BIT(2)
+#define EBU_CON_CLE_P_LOW BIT(3)
+#define EBU_CON_CS_P_LOW BIT(4)
+#define EBU_CON_SE_P_LOW BIT(5)
+#define EBU_CON_WP_P_LOW BIT(6)
+#define EBU_CON_PRE_P_LOW BIT(7)
+#define EBU_CON_IN_CS_S(n) ((n) << 8)
+#define EBU_CON_OUT_CS_S(n) ((n) << 10)
+#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18)
+
+#define EBU_WAIT 0x0B4
+#define EBU_WAIT_RDBY BIT(0)
+#define EBU_WAIT_WR_C BIT(3)
+
+#define HSNAND_CTL1 0x110
+#define HSNAND_CTL1_ADDR_SHIFT 24
+
+#define HSNAND_CTL2 0x114
+#define HSNAND_CTL2_ADDR_SHIFT 8
+#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16)
+
+#define HSNAND_INT_MSK_CTL 0x124
+#define HSNAND_INT_MSK_CTL_WR_C BIT(4)
+
+#define HSNAND_INT_STA 0x128
+#define HSNAND_INT_STA_WR_C BIT(4)
+
+#define HSNAND_CTL 0x130
+#define HSNAND_CTL_ENABLE_ECC BIT(0)
+#define HSNAND_CTL_GO BIT(2)
+#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n))
+#define HSNAND_CTL_RW_READ 0x0
+#define HSNAND_CTL_RW_WRITE BIT(10)
+#define HSNAND_CTL_ECC_OFF_V8TH BIT(11)
+#define HSNAND_CTL_CKFF_EN 0x0
+#define HSNAND_CTL_MSG_EN BIT(17)
+
+#define HSNAND_PARA0 0x13c
+#define HSNAND_PARA0_PAGE_V8192 0x3
+#define HSNAND_PARA0_PIB_V256 (0x3 << 4)
+#define HSNAND_PARA0_BYP_EN_NP 0x0
+#define HSNAND_PARA0_BYP_DEC_NP 0x0
+#define HSNAND_PARA0_TYPE_ONFI BIT(18)
+#define HSNAND_PARA0_ADEP_EN BIT(21)
+
+#define HSNAND_CMSG_0 0x150
+#define HSNAND_CMSG_1 0x154
+
+#define HSNAND_ALE_OFFS BIT(2)
+#define HSNAND_CLE_OFFS BIT(3)
+#define HSNAND_CS_OFFS BIT(4)
+
+#define HSNAND_ECC_OFFSET 0x008
+
+#define NAND_DATA_IFACE_CHECK_ONLY -1
+
+#define MAX_CS 2
+
+struct ebu_nand_cs {
+ void __iomem *chipaddr;
+ dma_addr_t nand_pa;
+ u32 addr_sel;
+};
+
+struct ebu_nand_controller {
+ struct nand_controller controller;
+ struct nand_chip chip;
+ struct device *dev;
+ void __iomem *ebu;
+ void __iomem *hsnand;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ struct completion dma_access_complete;
+ unsigned long clk_rate;
+ struct clk *clk;
+ u32 nd_para0;
+ u8 cs_num;
+ struct ebu_nand_cs cs[MAX_CS];
+};
+
+static inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip)
+{
+ return container_of(chip, struct ebu_nand_controller, chip);
+}
+
+static u8 ebu_nand_readb(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+ u8 val;
+
+ val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+
+ return val;
+}
+
+static void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_wait = ebu_host->ebu + EBU_WAIT;
+ u8 cs_num = ebu_host->cs_num;
+ u32 stat;
+ int ret;
+
+ writeb(value, ebu_host->cs[cs_num].chipaddr + offset);
+
+ ret = readl_poll_timeout(nand_wait, stat, stat & EBU_WAIT_WR_C,
+ 20, 1000);
+ if (ret)
+ dev_warn(ebu_host->dev,
+ "ebu nand write timeout. nand_wait(0x%p)=0x%x\n",
+ nand_wait, readl(nand_wait));
+}
+
+static void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = ebu_nand_readb(chip);
+}
+
+static void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]);
+}
+
+static void ebu_nand_disable(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+
+ writel(0, ebu_host->ebu + EBU_CON);
+}
+
+static void ebu_select_chip(struct nand_chip *chip)
+{
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *nand_con = ebu_host->ebu + EBU_CON;
+ u32 cs = ebu_host->cs_num;
+
+ writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW |
+ EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW |
+ EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) |
+ EBU_CON_LAT_EN_CS_P, nand_con);
+}
+
+static void ebu_nand_setup_timing(struct ebu_nand_controller *ctrl,
+ const struct nand_sdr_timings *timings)
+{
+ unsigned int rate = clk_get_rate(ctrl->clk) / 1000000;
+ unsigned int period = DIV_ROUND_UP(1000000, rate);
+ u32 trecov, thold, twrwait, trdwait;
+ u32 reg = 0;
+
+ trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_RECOVC(trecov);
+
+ thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period);
+ reg |= EBU_BUSCON_HOLDC(thold);
+
+ trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min),
+ period);
+ reg |= EBU_BUSCON_WAITRDC(trdwait);
+
+ twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period);
+ reg |= EBU_BUSCON_WAITWRC(twrwait);
+
+ reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC |
+ EBU_BUSCON_SETUP_EN;
+
+ writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num));
+}
+
+static int ebu_nand_setup_data_interface(struct nand_chip *chip, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ const struct nand_sdr_timings *timings;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ ebu_nand_setup_timing(ctrl, timings);
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = HSNAND_ECC_OFFSET;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = {
+ .ecc = ebu_nand_ooblayout_ecc,
+ .free = ebu_nand_ooblayout_free,
+};
+
+static void ebu_dma_rx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_rx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static void ebu_dma_tx_callback(void *cookie)
+{
+ struct ebu_nand_controller *ebu_host = cookie;
+
+ dmaengine_terminate_async(ebu_host->dma_tx);
+
+ complete(&ebu_host->dma_access_complete);
+}
+
+static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
+ const u8 *buf, u32 len)
+{
+ struct dma_async_tx_descriptor *tx;
+ struct completion *dma_completion;
+ dma_async_tx_callback callback;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ dma_addr_t buf_dma;
+ int ret;
+ u32 timeout;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ chan = ebu_host->dma_rx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_rx_callback;
+ } else {
+ chan = ebu_host->dma_tx;
+ dma_completion = &ebu_host->dma_access_complete;
+ callback = ebu_dma_tx_callback;
+ }
+
+ buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
+ if (dma_mapping_error(chan->device->dev, buf_dma)) {
+ dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags);
+ if (!tx)
+ return -ENXIO;
+
+ tx->callback = callback;
+ tx->callback_param = ebu_host;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie);
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ init_completion(dma_completion);
+ dma_async_issue_pending(chan);
+
+ /* Wait DMA to finish the data transfer.*/
+ timeout =
+ wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
+ if (!timeout) {
+ dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n",
+ dmaengine_tx_status(chan, cookie, NULL));
+ dmaengine_terminate_sync(chan);
+ ret = -ETIMEDOUT;
+ goto err_unmap;
+ }
+
+ return 0;
+
+err_unmap:
+ dma_unmap_single(ebu_host->dev, buf_dma, len, dir);
+
+ return ret;
+}
+
+static void ebu_nand_trigger(struct ebu_nand_controller *ebu_host,
+ int page, u32 cmd)
+{
+ unsigned int val;
+
+ val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT;
+ writel(val, ebu_host->hsnand + HSNAND_CTL1);
+ val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5;
+ writel(val, ebu_host->hsnand + HSNAND_CTL2);
+
+ writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0);
+
+ /* clear first, will update later */
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0);
+ writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1);
+
+ writel(HSNAND_INT_MSK_CTL_WR_C,
+ ebu_host->hsnand + HSNAND_INT_MSK_CTL);
+
+ val = cmd == NAND_CMD_READ0 ? HSNAND_CTL_RW_READ : HSNAND_CTL_RW_WRITE;
+
+ writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN |
+ HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) |
+ HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val,
+ ebu_host->hsnand + HSNAND_CTL);
+}
+
+static int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ int ret, x;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0);
+
+ ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ chip->ecc.read_oob(chip, page);
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA;
+ int ret, val, x;
+ u32 reg;
+
+ ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN);
+
+ ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ reg = (chip->oob_poi[3] << 24) | (chip->oob_poi[2] << 16) |
+ (chip->oob_poi[1] << 8) | chip->oob_poi[0];
+
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_0);
+
+ reg = (chip->oob_poi[7] << 24) | (chip->oob_poi[6] << 16) |
+ (chip->oob_poi[5] << 8) | chip->oob_poi[4];
+
+ writel(reg, ebu_host->hsnand + HSNAND_CMSG_1);
+ }
+
+ ret = readl_poll_timeout_atomic(int_sta, val,
+ !(val & HSNAND_INT_STA_WR_C), 10, 1000);
+ if (ret)
+ return -EIO;
+
+ x = readl(ebu_host->hsnand + HSNAND_CTL);
+ x &= ~HSNAND_CTL_GO;
+ writel(x, ebu_host->hsnand + HSNAND_CTL);
+
+ return 0;
+}
+
+static const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, };
+
+static int ebu_nand_attach_chip(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
+ u32 eccsize, eccsteps, eccbytes, ecctotal, pagesize, pg_per_blk;
+ u32 eccstrength = chip->ecc.strength;
+ u32 writesize = mtd->writesize;
+ u32 blocksize = mtd->erasesize;
+ int start, val, i;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ /* Check whether eccsize is 0x0 or wrong. assign eccsize = 512 if YES */
+ if (!chip->ecc.size)
+ chip->ecc.size = 512;
+ eccsize = chip->ecc.size;
+
+ switch (eccsize) {
+ case 512:
+ start = 1;
+ if (!eccstrength)
+ eccstrength = 4;
+ break;
+ case 1024:
+ start = 4;
+ if (!eccstrength)
+ eccstrength = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i = round_up(start + 1, 4);
+ for (val = start; val < i; val++) {
+ if (eccstrength == ecc_strength[val])
+ break;
+ }
+ if (val == i)
+ return -EINVAL;
+
+ if (eccstrength == 8)
+ eccbytes = 14;
+ else
+ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
+
+ eccsteps = writesize / eccsize;
+ ecctotal = eccsteps * eccbytes;
+ if ((ecctotal + 8) > mtd->oobsize)
+ return -ERANGE;
+
+ chip->ecc.total = ecctotal;
+ pagesize = fls(writesize >> 11);
+ if (pagesize > HSNAND_PARA0_PAGE_V8192)
+ return -ERANGE;
+
+ pg_per_blk = fls((blocksize / writesize) >> 6) << 4;
+ if (pg_per_blk > HSNAND_PARA0_PIB_V256)
+ return -ERANGE;
+
+ ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP |
+ HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN |
+ HSNAND_PARA0_TYPE_ONFI | (val << 29);
+
+ mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops);
+ chip->ecc.read_page = ebu_nand_read_page_hwecc;
+ chip->ecc.write_page = ebu_nand_write_page_hwecc;
+
+ return 0;
+}
+
+static int ebu_nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op, bool check_only)
+{
+ struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
+ const struct nand_op_instr *instr = NULL;
+ unsigned int op_id;
+ int i, time_out, ret = 0;
+ u32 stat;
+
+ ebu_select_chip(chip);
+
+ for (op_id = 0; op_id < op->ninstrs; op_id++) {
+ instr = &op->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.cmd.opcode);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ ebu_nand_writeb(chip,
+ HSNAND_ALE_OFFS | HSNAND_CS_OFFS,
+ instr->ctx.addr.addrs[i]);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ebu_read_buf(chip, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ebu_write_buf(chip, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ time_out = instr->ctx.waitrdy.timeout_ms * 1000;
+ ret = readl_poll_timeout(ctrl->ebu + EBU_WAIT,
+ stat, stat & EBU_WAIT_RDBY,
+ 20, time_out);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static const struct nand_controller_ops ebu_nand_controller_ops = {
+ .attach_chip = ebu_nand_attach_chip,
+ .exec_op = ebu_nand_exec_op,
+ .setup_data_interface = ebu_nand_setup_data_interface,
+};
+
+static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
+{
+ if (ebu_host->dma_rx)
+ dma_release_channel(ebu_host->dma_rx);
+
+ if (ebu_host->dma_tx)
+ dma_release_channel(ebu_host->dma_tx);
+}
+
+static int ebu_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ebu_nand_controller *ebu_host;
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ struct resource *res;
+ char *resname;
+ int ret, i;
+ u32 reg;
+
+ ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
+ if (!ebu_host)
+ return -ENOMEM;
+
+ ebu_host->dev = dev;
+ nand_controller_init(&ebu_host->controller);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand");
+ ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->ebu))
+ return PTR_ERR(ebu_host->ebu);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand");
+ ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ebu_host->hsnand))
+ return PTR_ERR(ebu_host->hsnand);
+
+ ret = device_property_read_u32(dev, "nand,cs", ®);
+ if (ret) {
+ dev_err(dev, "failed to get chip select: %d\n", ret);
+ return ret;
+ }
+ ebu_host->cs_num = reg;
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ if (!res)
+ return -EINVAL;
+ ebu_host->cs[i].chipaddr = devm_ioremap_resource(dev, res);
+ ebu_host->cs[i].nand_pa = res->start;
+ if (IS_ERR(ebu_host->cs[i].chipaddr))
+ return PTR_ERR(ebu_host->cs[i].chipaddr);
+ }
+
+ ebu_host->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ebu_host->clk)) {
+ ret = PTR_ERR(ebu_host->clk);
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ebu_host->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+ ebu_host->clk_rate = clk_get_rate(ebu_host->clk);
+
+ ebu_host->dma_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(ebu_host->dma_tx)) {
+ ret = PTR_ERR(ebu_host->dma_tx);
+ dev_err(dev, "DMA tx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ ebu_host->dma_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(ebu_host->dma_rx)) {
+ ret = PTR_ERR(ebu_host->dma_rx);
+ dev_err(dev, "DMA rx channel request fail!.\n");
+ goto err_cleanup_dma;
+ }
+
+ for (i = 0; i < MAX_CS; i++) {
+ resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", i);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ resname);
+ if (!res)
+ return -EINVAL;
+ ebu_host->cs[i].addr_sel = res->start;
+ writel(ebu_host->cs[i].addr_sel | EBU_ADDR_MASK(5) |
+ EBU_ADDR_SEL_REGEN, ebu_host->ebu + EBU_ADDR_SEL(i));
+ }
+
+ nand_set_flash_node(&ebu_host->chip, dev->of_node);
+ mtd = nand_to_mtd(&ebu_host->chip);
+ mtd->dev.parent = dev;
+ ebu_host->dev = dev;
+
+ platform_set_drvdata(pdev, ebu_host);
+ nand_set_controller_data(&ebu_host->chip, ebu_host);
+
+ nand = &ebu_host->chip;
+ nand->controller = &ebu_host->controller;
+ nand->controller->ops = &ebu_nand_controller_ops;
+
+ /* Scan to find existence of the device */
+ ret = nand_scan(&ebu_host->chip, 1);
+ if (ret)
+ goto err_cleanup_dma;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_clean_nand;
+
+ return 0;
+
+err_clean_nand:
+ nand_cleanup(&ebu_host->chip);
+err_cleanup_dma:
+ ebu_dma_cleanup(ebu_host);
+ clk_disable_unprepare(ebu_host->clk);
+
+ return ret;
+}
+
+static int ebu_nand_remove(struct platform_device *pdev)
+{
+ struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
+ nand_cleanup(&ebu_host->chip);
+ ebu_nand_disable(&ebu_host->chip);
+ ebu_dma_cleanup(ebu_host);
+ clk_disable_unprepare(ebu_host->clk);
+
+ return 0;
+}
+
+static const struct of_device_id ebu_nand_match[] = {
+ { .compatible = "intel,nand-controller", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ebu_nand_match);
+
+static struct platform_driver ebu_nand_driver = {
+ .probe = ebu_nand_probe,
+ .remove = ebu_nand_remove,
+ .driver = {
+ .name = "intel-nand-controller",
+ .of_match_table = ebu_nand_match,
+ },
+
+};
+module_platform_driver(ebu_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>");
+MODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver");
--
2.11.0
^ permalink raw reply related
* [PATCH v9 1/2] dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:34 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar Vadivel Murugan
In-Reply-To: <20200528153419.46775-1-vadivel.muruganx.ramuthevar@linux.intel.com>
From: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
Add YAML file for dt-bindings to support NAND Flash Controller
on Intel's Lightning Mountain SoC.
Signed-off-by: Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
---
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 93 ++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
diff --git a/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
new file mode 100644
index 000000000000..afecc9920e04
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/intel,lgm-nand.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel LGM SoC NAND Controller Device Tree Bindings
+
+allOf:
+ - $ref: "nand-controller.yaml"
+
+maintainers:
+ - Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
+
+properties:
+ compatible:
+ const: intel,lgm-nand-controller
+
+ reg:
+ maxItems: 6
+
+ reg-names:
+ items:
+ - const: ebunand
+ - const: hsnand
+ - const: nand_cs0
+ - const: nand_cs1
+ - const: addr_sel0
+ - const: addr_sel1
+
+ clocks:
+ maxItems: 1
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+patternProperties:
+ "^nand@[a-f0-9]+$":
+ type: object
+ properties:
+ reg:
+ minimum: 0
+ maximum: 7
+
+ nand-ecc-mode: true
+
+ nand-ecc-algo:
+ const: hw
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - dmas
+ - dma-names
+
+additionalProperties: false
+
+examples:
+ - |
+ nand-controller@e0f00000 {
+ compatible = "intel,lgm-nand";
+ reg = <0xe0f00000 0x100>,
+ <0xe1000000 0x300>,
+ <0xe1400000 0x8000>,
+ <0xe1c00000 0x1000>,
+ <0x17400000 0x4>,
+ <0x17c00000 0x4>;
+ reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
+ "addr_sel0", "addr_sel1";
+ clocks = <&cgu0 125>;
+ dmas = <&dma0 8>, <&dma0 9>;
+ dma-names = "tx", "rx";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ nand@0 {
+ reg = <0>;
+ nand-on-flash-bbt;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+ };
+
+...
--
2.11.0
^ permalink raw reply related
* [PATCH v9 0/2] mtd: rawnand: Add NAND controller support on Intel LGM SoC
From: Ramuthevar,Vadivel MuruganX @ 2020-05-28 15:34 UTC (permalink / raw)
To: linux-kernel, linux-mtd, devicetree, miquel.raynal
Cc: richard, vigneshr, arnd, brendanhiggins, tglx, boris.brezillon,
anders.roxell, masonccyang, robh+dt, linux-mips, hauke.mehrtens,
andriy.shevchenko, qi-ming.wu, cheol.yong.kim,
Ramuthevar,Vadivel MuruganX
This patch adds the new IP of Nand Flash Controller(NFC) support
on Intel's Lightning Mountain(LGM) SoC.
DMA is used for burst data transfer operation, also DMA HW supports
aligned 32bit memory address and aligned data access by default.
DMA burst of 8 supported. Data register used to support the read/write
operation from/to device.
NAND controller also supports in-built HW ECC engine.
NAND controller driver implements ->exec_op() to replace legacy hooks,
these specific call-back method to execute NAND operations.
Thanks Boris, Andy, Arnd and Rob for the review comments and suggestions.
---
v10:
- No Change
v9:
- No change
v8:
- fix the kbuild bot warnings
- correct the typo's
v7:
- indentation issue is fixed
- add error check for retrieve the resource from dt
v6:
- update EBU_ADDR_SELx register base value build it from DT
- Add tabs in in Kconfig
v5:
- replace by 'HSNAND_CLE_OFFS | HSNAND_CS_OFFS' to NAND_WRITE_CMD and NAND_WRITE_ADDR
- remove the unused macros
- update EBU_ADDR_MASK(x) macro
- update the EBU_ADDR_SELx register values to be written
v4:
- add ebu_nand_cs structure for multiple-CS support
- mask/offset encoding for 0x51 value
- update macro HSNAND_CTL_ENABLE_ECC
- drop the op argument and un-used macros.
- updated the datatype and macros
- add function disable nand module
- remove ebu_host->dma_rx = NULL;
- rename MMIO address range variables to ebu and hsnand
- implement ->setup_data_interface()
- update label err_cleanup_nand and err_cleanup_dma
- add return value check in the nand_remove function
- add/remove tabs and spaces as per coding standard
- encoded CS ids by reg property
v3:
- Add depends on MACRO in Kconfig
- file name update in Makefile
- file name update to intel-nand-controller
- modification of MACRO divided like EBU, HSNAND and NAND
- add NAND_ALE_OFFS, NAND_CLE_OFFS and NAND_CS_OFFS
- rename lgm_ to ebu_ and _va suffix is removed in the whole file
- rename structure and varaibles as per review comments.
- remove lgm_read_byte(), lgm_dev_ready() and cmd_ctrl() un-used function
- update in exec_op() as per review comments
- rename function lgm_dma_exit() by lgm_dma_cleanup()
- hardcoded magic value for base and offset replaced by MACRO defined
- mtd_device_unregister() + nand_cleanup() instead of nand_release()
v2:
- implement the ->exec_op() to replaces the legacy hook-up.
- update the commit message
- add MIPS maintainers and xway_nand driver author in CC
v1:
- initial version
dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
---
v10:
- fix bot errors
v9:
- Rob's review comments address
- dual licensed
- compatible change
- add reg-names
- drop clock-names and clock-cells
- correct typo's
v8:
No change
v7:
- Rob's review comments addressed
- dt-schema build issue fixed with upgraded dt-schema
v6:
- Rob's review comments addressed in YAML file
- add addr_sel0 and addr_sel1 reg-names in YAML example
v5:
- add the example in YAML file
v4:
- No change
v3:
- No change
v2:
YAML compatible string update to intel, lgm-nand-controller
v1:
- initial version
Ramuthevar Vadivel Murugan (2):
dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC
mtd: rawnand: Add NAND controller support on Intel LGM SoC
.../devicetree/bindings/mtd/intel,lgm-nand.yaml | 93 +++
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/intel-nand-controller.c | 747 +++++++++++++++++++++
4 files changed, 849 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/intel,lgm-nand.yaml
create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c
--
2.11.0
^ permalink raw reply
* Re: [PATCH 0/8] R8A7742 add support for HSUSB and USB2.0/3.0
From: Geert Uytterhoeven @ 2020-05-28 15:33 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: Lad Prabhakar, Geert Uytterhoeven, Vinod Koul, Rob Herring,
Bjorn Helgaas, Kishon Vijay Abraham I, Greg Kroah-Hartman,
Magnus Damm, dmaengine, linux-pci, USB list, Linux-Renesas,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux Kernel Mailing List, Prabhakar
In-Reply-To: <20200528142139.GA28290@e121166-lin.cambridge.arm.com>
Hi Lorenzo,
On Thu, May 28, 2020 at 4:21 PM Lorenzo Pieralisi
<lorenzo.pieralisi@arm.com> wrote:
> On Sun, May 24, 2020 at 10:37:49PM +0100, Lad Prabhakar wrote:
> > This patch series adds support for HSUSB, USB2.0 and USB3.0 to
> > R8A7742 SoC DT.
> >
> > This patch series applies on-top of [1].
> >
> > [1] https://patchwork.kernel.org/project/linux-renesas-soc/list/?series=288491
>
> I think Geert will pull this series, so I'd drop it from the PCI
> patchwork unless there is a reason I should not, please let me know.
I'll take the DTS patches only.
You may want to take 2/8, or leave it to Rob.
> > Lad Prabhakar (8):
> > dt-bindings: phy: rcar-gen2: Add r8a7742 support
> > dt-bindings: PCI: pci-rcar-gen2: Add device tree support for r8a7742
> > dt-bindings: usb: renesas,usbhs: Add support for r8a7742
> > dt-bindings: dmaengine: renesas,usb-dmac: Add binding for r8a7742
> > dt-bindings: usb: usb-xhci: Document r8a7742 support
> > ARM: dts: r8a7742: Add USB 2.0 host support
> > ARM: dts: r8a7742: Add USB-DMAC and HSUSB device nodes
> > ARM: dts: r8a7742: Add xhci support
> >
> > .../devicetree/bindings/dma/renesas,usb-dmac.yaml | 1 +
> > .../devicetree/bindings/pci/pci-rcar-gen2.txt | 3 +-
> > .../devicetree/bindings/phy/rcar-gen2-phy.txt | 3 +-
> > .../devicetree/bindings/usb/renesas,usbhs.yaml | 1 +
> > Documentation/devicetree/bindings/usb/usb-xhci.txt | 1 +
> > arch/arm/boot/dts/r8a7742.dtsi | 173 +++++++++++++++++++++
> > 6 files changed, 180 insertions(+), 2 deletions(-)
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
* [PATCH v5 3/6] irqchip: Add Loongson PCH PIC controller
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
This controller appears on Loongson LS7A family of PCH to transform
interrupts from devices into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
- Style clean-ups
- Use IRQ_FASTEOI_HIERARCHY_HANDLERS
- Move lock into bitclr & bitset
- Make loongson,pic-base-vec as required property
v4:
- Fix variable declear ordering
- Remove irqsave for spin-locks
---
drivers/irqchip/Kconfig | 9 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-loongson-pch-pic.c | 243 +++++++++++++++++++++++++
3 files changed, 253 insertions(+)
create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index de4564e2ea88..5524a621638c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -540,4 +540,13 @@ config LOONGSON_HTVEC
help
Support for the Loongson3 HyperTransport Interrupt Vector Controller.
+config LOONGSON_PCH_PIC
+ bool "Loongson PCH PIC Controller"
+ depends on MACH_LOONGSON64 || COMPILE_TEST
+ default MACH_LOONGSON64
+ select IRQ_DOMAIN_HIERARCHY
+ select IRQ_FASTEOI_HIERARCHY_HANDLERS
+ help
+ Support for the Loongson PCH PIC Controller.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 74561879f5a7..acc72331cec8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,3 +108,4 @@ obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
+obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o
diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c
new file mode 100644
index 000000000000..2a05b9305012
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-pic.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * Loongson PCH PIC support
+ */
+
+#define pr_fmt(fmt) "pch-pic: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+/* Registers */
+#define PCH_PIC_MASK 0x20
+#define PCH_PIC_HTMSI_EN 0x40
+#define PCH_PIC_EDGE 0x60
+#define PCH_PIC_CLR 0x80
+#define PCH_PIC_AUTO0 0xc0
+#define PCH_PIC_AUTO1 0xe0
+#define PCH_INT_ROUTE(irq) (0x100 + irq)
+#define PCH_INT_HTVEC(irq) (0x200 + irq)
+#define PCH_PIC_POL 0x3e0
+
+#define PIC_COUNT_PER_REG 32
+#define PIC_REG_COUNT 2
+#define PIC_COUNT (PIC_COUNT_PER_REG * PIC_REG_COUNT)
+#define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG)
+#define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG)
+
+struct pch_pic {
+ void __iomem *base;
+ struct irq_domain *pic_domain;
+ u32 ht_vec_base;
+ raw_spinlock_t pic_lock;
+};
+
+static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
+{
+ u32 reg;
+ void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+
+ raw_spin_lock(&priv->pic_lock);
+ reg = readl(addr);
+ reg |= BIT(PIC_REG_BIT(bit));
+ writel(reg, addr);
+ raw_spin_unlock(&priv->pic_lock);
+}
+
+static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
+{
+ u32 reg;
+ void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
+
+ raw_spin_lock(&priv->pic_lock);
+ reg = readl(addr);
+ reg &= ~BIT(PIC_REG_BIT(bit));
+ writel(reg, addr);
+ raw_spin_unlock(&priv->pic_lock);
+}
+
+static void pch_pic_eoi_irq(struct irq_data *d)
+{
+ u32 idx = PIC_REG_IDX(d->hwirq);
+ struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+ writel(BIT(PIC_REG_BIT(d->hwirq)),
+ priv->base + PCH_PIC_CLR + idx * 4);
+}
+
+static void pch_pic_mask_irq(struct irq_data *d)
+{
+ struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+ pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
+ irq_chip_mask_parent(d);
+}
+
+static void pch_pic_unmask_irq(struct irq_data *d)
+{
+ struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+
+ irq_chip_unmask_parent(d);
+ pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
+}
+
+static int pch_pic_set_type(struct irq_data *d, unsigned int type)
+{
+ struct pch_pic *priv = irq_data_get_irq_chip_data(d);
+ int ret = 0;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+ pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
+ pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+ pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
+ pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static struct irq_chip pch_pic_irq_chip = {
+ .name = "PCH PIC",
+ .irq_mask = pch_pic_mask_irq,
+ .irq_unmask = pch_pic_unmask_irq,
+ .irq_ack = irq_chip_ack_parent,
+ .irq_eoi = pch_pic_eoi_irq,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_set_type = pch_pic_set_type,
+};
+
+static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ int err;
+ unsigned int type;
+ unsigned long hwirq;
+ struct irq_fwspec fwspec;
+ struct pch_pic *priv = domain->host_data;
+
+ irq_domain_translate_twocell(domain, arg, &hwirq, &type);
+
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 1;
+ fwspec.param[0] = hwirq + priv->ht_vec_base;
+
+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+ if (err)
+ return err;
+
+ irq_domain_set_info(domain, virq, hwirq,
+ &pch_pic_irq_chip, priv,
+ handle_fasteoi_ack_irq, NULL, NULL);
+ irq_set_probe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pch_pic_domain_ops = {
+ .translate = irq_domain_translate_twocell,
+ .alloc = pch_pic_alloc,
+ .free = irq_domain_free_irqs_parent,
+};
+
+static void pch_pic_reset(struct pch_pic *priv)
+{
+ int i;
+
+ for (i = 0; i < PIC_COUNT; i++) {
+ /* Write vectore ID */
+ writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
+ /* Hardcode route to HT0 Lo */
+ writeb(1, priv->base + PCH_INT_ROUTE(i));
+ }
+
+ for (i = 0; i < PIC_REG_COUNT; i++) {
+ /* Clear IRQ cause registers, mask all interrupts */
+ writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
+ writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
+ /* Clear auto bounce, we don't need that */
+ writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
+ writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
+ /* Enable HTMSI transformer */
+ writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
+ }
+}
+
+static int pch_pic_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct pch_pic *priv;
+ struct irq_domain *parent_domain;
+ int err;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&priv->pic_lock);
+ priv->base = of_iomap(node, 0);
+ if (!priv->base) {
+ err = -ENOMEM;
+ goto free_priv;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("Failed to find the parent domain\n");
+ err = -ENXIO;
+ goto iounmap_base;
+ }
+
+ if (of_property_read_u32(node, "loongson,pic-base-vec",
+ &priv->ht_vec_base)) {
+ pr_err("Failed to determine pic-base-vec\n");
+ err = -EINVAL;
+ goto iounmap_base;
+ }
+
+ priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
+ PIC_COUNT,
+ of_node_to_fwnode(node),
+ &pch_pic_domain_ops,
+ priv);
+ if (!priv->pic_domain) {
+ pr_err("Failed to create IRQ domain\n");
+ err = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ pch_pic_reset(priv);
+
+ return 0;
+
+iounmap_base:
+ iounmap(priv->base);
+free_priv:
+ kfree(priv);
+
+ return err;
+}
+
+IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 5/6] irqchip: Add Loongson PCH MSI controller
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
This controller appears on Loongson LS7A family of PCH to transform
interrupts from PCI MSI into HyperTransport vectorized interrrupts
and send them to procrssor's HT vector controller.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
- Style clean-ups
- Add ack callback
- Use bitmap_find_free_region
v3:
- Style clean-ups
- mutex lock instead of spin lock
- correct bitmap usage
v4:
- Fix table layout
- Make mask parent symetric
---
drivers/irqchip/Kconfig | 10 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-loongson-pch-msi.c | 255 +++++++++++++++++++++++++
3 files changed, 266 insertions(+)
create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 5524a621638c..0b6b826dd843 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -549,4 +549,14 @@ config LOONGSON_PCH_PIC
help
Support for the Loongson PCH PIC Controller.
+config LOONGSON_PCH_MSI
+ bool "Loongson PCH PIC Controller"
+ depends on MACH_LOONGSON64 || COMPILE_TEST
+ depends on PCI
+ default MACH_LOONGSON64
+ select IRQ_DOMAIN_HIERARCHY
+ select PCI_MSI
+ help
+ Support for the Loongson PCH MSI Controller.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index acc72331cec8..3a4ce283189a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -109,3 +109,4 @@ obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o
+obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
new file mode 100644
index 000000000000..50becd21008c
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * Loongson PCH MSI support
+ */
+
+#define pr_fmt(fmt) "pch-msi: " fmt
+
+#include <linux/irqchip.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+struct pch_msi_data {
+ struct mutex msi_map_lock;
+ phys_addr_t doorbell;
+ u32 irq_first; /* The vector number that MSIs starts */
+ u32 num_irqs; /* The number of vectors for MSIs */
+ unsigned long *msi_map;
+};
+
+static void pch_msi_mask_msi_irq(struct irq_data *d)
+{
+ pci_msi_mask_irq(d);
+ irq_chip_mask_parent(d);
+}
+
+static void pch_msi_unmask_msi_irq(struct irq_data *d)
+{
+ irq_chip_unmask_parent(d);
+ pci_msi_unmask_irq(d);
+}
+
+static struct irq_chip pch_msi_irq_chip = {
+ .name = "PCH PCI MSI",
+ .irq_mask = pch_msi_mask_msi_irq,
+ .irq_unmask = pch_msi_unmask_msi_irq,
+ .irq_ack = irq_chip_ack_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
+{
+ int first;
+
+ mutex_lock(&priv->msi_map_lock);
+
+ first = bitmap_find_free_region(priv->msi_map, priv->num_irqs,
+ get_count_order(num_req));
+ if (first < 0) {
+ mutex_unlock(&priv->msi_map_lock);
+ return -ENOSPC;
+ }
+
+ mutex_unlock(&priv->msi_map_lock);
+
+ return priv->irq_first + first;
+}
+
+static void pch_msi_free_hwirq(struct pch_msi_data *priv,
+ int hwirq, int num_req)
+{
+ int first = hwirq - priv->irq_first;
+
+ mutex_lock(&priv->msi_map_lock);
+ bitmap_release_region(priv->msi_map, first, get_count_order(num_req));
+ mutex_unlock(&priv->msi_map_lock);
+}
+
+static void pch_msi_compose_msi_msg(struct irq_data *data,
+ struct msi_msg *msg)
+{
+ struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
+
+ msg->address_hi = upper_32_bits(priv->doorbell);
+ msg->address_lo = lower_32_bits(priv->doorbell);
+ msg->data = data->hwirq;
+}
+
+static struct msi_domain_info pch_msi_domain_info = {
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+ .chip = &pch_msi_irq_chip,
+};
+
+static struct irq_chip middle_irq_chip = {
+ .name = "PCH MSI",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_ack = irq_chip_ack_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_compose_msi_msg = pch_msi_compose_msi_msg,
+};
+
+static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
+ unsigned int virq, int hwirq)
+{
+ struct irq_fwspec fwspec;
+ int ret;
+
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 1;
+ fwspec.param[0] = hwirq;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *args)
+{
+ struct pch_msi_data *priv = domain->host_data;
+ int hwirq, err, i;
+
+ hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
+ if (hwirq < 0)
+ return hwirq;
+
+ for (i = 0; i < nr_irqs; i++) {
+ err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
+ if (err)
+ goto err_hwirq;
+
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &middle_irq_chip, priv);
+ }
+
+ return 0;
+
+err_hwirq:
+ pch_msi_free_hwirq(priv, hwirq, nr_irqs);
+ irq_domain_free_irqs_parent(domain, virq, i - 1);
+
+ return err;
+}
+
+static void pch_msi_middle_domain_free(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
+
+ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+ pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
+}
+
+static const struct irq_domain_ops pch_msi_middle_domain_ops = {
+ .alloc = pch_msi_middle_domain_alloc,
+ .free = pch_msi_middle_domain_free,
+};
+
+static int pch_msi_init_domains(struct pch_msi_data *priv,
+ struct device_node *node,
+ struct irq_domain *parent)
+{
+ struct irq_domain *middle_domain, *msi_domain;
+
+ middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+ priv->num_irqs,
+ &pch_msi_middle_domain_ops,
+ priv);
+ if (!middle_domain) {
+ pr_err("Failed to create the MSI middle domain\n");
+ return -ENOMEM;
+ }
+
+ middle_domain->parent = parent;
+ irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
+
+ msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+ &pch_msi_domain_info,
+ middle_domain);
+ if (!msi_domain) {
+ pr_err("Failed to create PCI MSI domain\n");
+ irq_domain_remove(middle_domain);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int pch_msi_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct pch_msi_data *priv;
+ struct irq_domain *parent_domain;
+ struct resource res;
+ int ret;
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("Failed to find the parent domain\n");
+ return -ENXIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->msi_map_lock);
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ pr_err("Failed to allocate resource\n");
+ goto err_priv;
+ }
+
+ priv->doorbell = res.start;
+
+ if (of_property_read_u32(node, "loongson,msi-base-vec",
+ &priv->irq_first)) {
+ pr_err("Unable to parse MSI vec base\n");
+ ret = -EINVAL;
+ goto err_priv;
+ }
+
+ if (of_property_read_u32(node, "loongson,msi-num-vecs",
+ &priv->num_irqs)) {
+ pr_err("Unable to parse MSI vec number\n");
+ ret = -EINVAL;
+ goto err_priv;
+ }
+
+ priv->msi_map = bitmap_alloc(priv->num_irqs, GFP_KERNEL);
+ if (!priv->msi_map) {
+ ret = -ENOMEM;
+ goto err_priv;
+ }
+
+ pr_debug("Registering %d MSIs, starting at %d\n",
+ priv->num_irqs, priv->irq_first);
+
+ ret = pch_msi_init_domains(priv, node, parent_domain);
+ if (ret)
+ goto err_map;
+
+ return 0;
+
+err_map:
+ kfree(priv->msi_map);
+err_priv:
+ kfree(priv);
+ return ret;
+}
+
+IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 6/6] dt-bindings: interrupt-controller: Add Loongson PCH MSI
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Rob Herring, Thomas Gleixner, Jason Cooper,
Rob Herring, Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
Add binding for Loongson PCH MSI controller.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Reviewed-by: Rob Herring <robh@kernel.org>
--
v5: Add range check for msi-base-vec & msi-num-vecs
---
.../loongson,pch-msi.yaml | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
new file mode 100644
index 000000000000..1a5ebbdd219a
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-msi.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH MSI Controller
+
+maintainers:
+ - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description:
+ This interrupt controller is found in the Loongson LS7A family of PCH for
+ transforming interrupts from PCIe MSI into HyperTransport vectorized
+ interrupts.
+
+properties:
+ compatible:
+ const: loongson,pch-msi-1.0
+
+ reg:
+ maxItems: 1
+
+ loongson,msi-base-vec:
+ description:
+ u32 value of the base of parent HyperTransport vector allocated
+ to PCH MSI.
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 255
+
+ loongson,msi-num-vecs:
+ description:
+ u32 value of the number of parent HyperTransport vectors allocated
+ to PCH MSI.
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 1
+ maximum: 256
+
+ msi-controller: true
+
+required:
+ - compatible
+ - reg
+ - msi-controller
+ - loongson,msi-base-vec
+ - loongson,msi-num-vecs
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ msi: msi-controller@2ff00000 {
+ compatible = "loongson,pch-msi-1.0";
+ reg = <0x2ff00000 0x4>;
+ msi-controller;
+ loongson,msi-base-vec = <64>;
+ loongson,msi-num-vecs = <64>;
+ interrupt-parent = <&htvec>;
+ };
+...
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 4/6] dt-bindings: interrupt-controller: Add Loongson PCH PIC
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
Add binding for Loongson PCH PIC Controller.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v2:
- Fix naming
- Mark loongson,pic-base-vec as required
v5:
- Add range check for loongson,pic-base-vec
---
.../loongson,pch-pic.yaml | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
new file mode 100644
index 000000000000..274adea13f33
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,pch-pic.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson PCH PIC Controller
+
+maintainers:
+ - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description:
+ This interrupt controller is found in the Loongson LS7A family of PCH for
+ transforming interrupts from on-chip devices into HyperTransport vectorized
+ interrupts.
+
+properties:
+ compatible:
+ const: loongson,pch-pic-1.0
+
+ reg:
+ maxItems: 1
+
+ loongson,pic-base-vec:
+ description:
+ u32 value of the base of parent HyperTransport vector allocated
+ to PCH PIC.
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - minimum: 0
+ maximum: 192
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 2
+
+required:
+ - compatible
+ - reg
+ - loongson,pic-base-vec
+ - interrupt-controller
+ - '#interrupt-cells'
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ pic: interrupt-controller@10000000 {
+ compatible = "loongson,pch-pic-1.0";
+ reg = <0x10000000 0x400>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ loongson,pic-base-vec = <64>;
+ interrupt-parent = <&htvec>;
+ };
+...
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 2/6] dt-bindings: interrupt-controller: Add Loongson HTVEC
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
Add binding for Loongson-3 HyperTransport Interrupt Vector Controller.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
--
v4: Drop ref, '|', add additionalProperties, fix example
---
.../interrupt-controller/loongson,htvec.yaml | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
new file mode 100644
index 000000000000..e865cd8f96a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/interrupt-controller/loongson,htvec.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Loongson-3 HyperTransport Interrupt Vector Controller
+
+maintainers:
+ - Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+description:
+ This interrupt controller is found in the Loongson-3 family of chips for
+ receiving vectorized interrupts from PCH's interrupt controller.
+
+properties:
+ compatible:
+ const: loongson,htvec-1.0
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ maxItems: 4
+ description: Four parent interrupts that receive chained interrupts.
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-controller
+ - '#interrupt-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ htvec: interrupt-controller@fb000080 {
+ compatible = "loongson,htvec-1.0";
+ reg = <0xfb000080 0x40>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ interrupt-parent = <&liointc>;
+ interrupts = <24 IRQ_TYPE_LEVEL_HIGH>,
+ <25 IRQ_TYPE_LEVEL_HIGH>,
+ <26 IRQ_TYPE_LEVEL_HIGH>,
+ <27 IRQ_TYPE_LEVEL_HIGH>;
+ };
+...
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 1/6] irqchip: Add Loongson HyperTransport Vector support
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
In-Reply-To: <20200528152757.1028711-1-jiaxun.yang@flygoat.com>
This controller appears on Loongson-3 chips for receiving interrupt
vectors from PCH's PIC and PCH's PCIe MSI interrupts.
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
v2:
- Style cleanup
- Set ack callback and set correct edge_irq handler
v3:
- Correct bitops in ACK callback
v4:
- Drop irqsave for spinlocks
- Fix brace align and ordering issue thanks to tglx
---
drivers/irqchip/Kconfig | 8 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-loongson-htvec.c | 214 +++++++++++++++++++++++++++
3 files changed, 223 insertions(+)
create mode 100644 drivers/irqchip/irq-loongson-htvec.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a85aada04a64..de4564e2ea88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -532,4 +532,12 @@ config LOONGSON_HTPIC
help
Support for the Loongson-3 HyperTransport PIC Controller.
+config LOONGSON_HTVEC
+ bool "Loongson3 HyperTransport Interrupt Vector Controller"
+ depends on MACH_LOONGSON64 || COMPILE_TEST
+ default MACH_LOONGSON64
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ Support for the Loongson3 HyperTransport Interrupt Vector Controller.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 37bbe39bf909..74561879f5a7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o
obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
+obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c
new file mode 100644
index 000000000000..1ece9337c78d
--- /dev/null
+++ b/drivers/irqchip/irq-loongson-htvec.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * Loongson HyperTransport Interrupt Vector support
+ */
+
+#define pr_fmt(fmt) "htvec: " fmt
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+/* Registers */
+#define HTVEC_EN_OFF 0x20
+#define HTVEC_MAX_PARENT_IRQ 4
+
+#define VEC_COUNT_PER_REG 32
+#define VEC_REG_COUNT 4
+#define VEC_COUNT (VEC_COUNT_PER_REG * VEC_REG_COUNT)
+#define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG)
+#define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG)
+
+struct htvec {
+ void __iomem *base;
+ struct irq_domain *htvec_domain;
+ raw_spinlock_t htvec_lock;
+};
+
+static void htvec_irq_dispatch(struct irq_desc *desc)
+{
+ int i;
+ u32 pending;
+ bool handled = false;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct htvec *priv = irq_desc_get_handler_data(desc);
+
+ chained_irq_enter(chip, desc);
+
+ for (i = 0; i < VEC_REG_COUNT; i++) {
+ pending = readl(priv->base + 4 * i);
+ while (pending) {
+ int bit = __ffs(pending);
+
+ generic_handle_irq(irq_linear_revmap(priv->htvec_domain, bit +
+ VEC_COUNT_PER_REG * i));
+ pending &= ~BIT(bit);
+ handled = true;
+ }
+ }
+
+ if (!handled)
+ spurious_interrupt();
+
+ chained_irq_exit(chip, desc);
+}
+
+static void htvec_ack_irq(struct irq_data *d)
+{
+ struct htvec *priv = irq_data_get_irq_chip_data(d);
+
+ writel(BIT(VEC_REG_BIT(d->hwirq)),
+ priv->base + VEC_REG_IDX(d->hwirq) * 4);
+}
+
+static void htvec_mask_irq(struct irq_data *d)
+{
+ u32 reg;
+ void __iomem *addr;
+ struct htvec *priv = irq_data_get_irq_chip_data(d);
+
+ raw_spin_lock(&priv->htvec_lock);
+ addr = priv->base + HTVEC_EN_OFF;
+ addr += VEC_REG_IDX(d->hwirq) * 4;
+ reg = readl(addr);
+ reg &= ~BIT(VEC_REG_BIT(d->hwirq));
+ writel(reg, addr);
+ raw_spin_unlock(&priv->htvec_lock);
+}
+
+static void htvec_unmask_irq(struct irq_data *d)
+{
+ u32 reg;
+ void __iomem *addr;
+ struct htvec *priv = irq_data_get_irq_chip_data(d);
+
+ raw_spin_lock(&priv->htvec_lock);
+ addr = priv->base + HTVEC_EN_OFF;
+ addr += VEC_REG_IDX(d->hwirq) * 4;
+ reg = readl(addr);
+ reg |= BIT(VEC_REG_BIT(d->hwirq));
+ writel(reg, addr);
+ raw_spin_unlock(&priv->htvec_lock);
+}
+
+static struct irq_chip htvec_irq_chip = {
+ .name = "LOONGSON_HTVEC",
+ .irq_mask = htvec_mask_irq,
+ .irq_unmask = htvec_unmask_irq,
+ .irq_ack = htvec_ack_irq,
+};
+
+static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ unsigned long hwirq;
+ unsigned int type, i;
+ struct htvec *priv = domain->host_data;
+
+ irq_domain_translate_onecell(domain, arg, &hwirq, &type);
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
+ priv, handle_edge_irq, NULL, NULL);
+ }
+
+ return 0;
+}
+
+static void htvec_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ int i;
+
+ for (i = 0; i < nr_irqs; i++) {
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+
+ irq_set_handler(virq + i, NULL);
+ irq_domain_reset_irq_data(d);
+ }
+}
+
+static const struct irq_domain_ops htvec_domain_ops = {
+ .translate = irq_domain_translate_onecell,
+ .alloc = htvec_domain_alloc,
+ .free = htvec_domain_free,
+};
+
+static void htvec_reset(struct htvec *priv)
+{
+ u32 idx;
+
+ /* Clear IRQ cause registers, mask all interrupts */
+ for (idx = 0; idx < VEC_REG_COUNT; idx++) {
+ writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
+ writel_relaxed(0xFFFFFFFF, priv->base);
+ }
+}
+
+static int htvec_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct htvec *priv;
+ int err, parent_irq[4], num_parents = 0, i;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&priv->htvec_lock);
+ priv->base = of_iomap(node, 0);
+ if (!priv->base) {
+ err = -ENOMEM;
+ goto free_priv;
+ }
+
+ /* Interrupt may come from any of the 4 interrupt line */
+ for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
+ parent_irq[i] = irq_of_parse_and_map(node, i);
+ if (parent_irq[i] <= 0)
+ break;
+
+ num_parents++;
+ }
+
+ if (!num_parents) {
+ pr_err("Failed to get parent irqs\n");
+ err = -ENODEV;
+ goto iounmap_base;
+ }
+
+ priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+ VEC_COUNT,
+ &htvec_domain_ops,
+ priv);
+ if (!priv->htvec_domain) {
+ pr_err("Failed to create IRQ domain\n");
+ err = -ENOMEM;
+ goto iounmap_base;
+ }
+
+ htvec_reset(priv);
+
+ for (i = 0; i < num_parents; i++)
+ irq_set_chained_handler_and_data(parent_irq[i],
+ htvec_irq_dispatch, priv);
+
+ return 0;
+
+iounmap_base:
+ iounmap(priv->base);
+free_priv:
+ kfree(priv);
+
+ return err;
+}
+
+IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);
--
2.27.0.rc0
^ permalink raw reply related
* [PATCH v5 0/6] Three Loongson irqchip support
From: Jiaxun Yang @ 2020-05-28 15:27 UTC (permalink / raw)
To: maz
Cc: Jiaxun Yang, Thomas Gleixner, Jason Cooper, Rob Herring,
Huacai Chen, linux-kernel, devicetree, linux-mips
v5:
- Add some range checks in dt-schema
Jiaxun Yang (6):
irqchip: Add Loongson HyperTransport Vector support
dt-bindings: interrupt-controller: Add Loongson HTVEC
irqchip: Add Loongson PCH PIC controller
dt-bindings: interrupt-controller: Add Loongson PCH PIC
irqchip: Add Loongson PCH MSI controller
dt-bindings: interrupt-controller: Add Loongson PCH MSI
.../interrupt-controller/loongson,htvec.yaml | 57 ++++
.../loongson,pch-msi.yaml | 62 +++++
.../loongson,pch-pic.yaml | 56 ++++
drivers/irqchip/Kconfig | 27 ++
drivers/irqchip/Makefile | 3 +
drivers/irqchip/irq-loongson-htvec.c | 214 +++++++++++++++
drivers/irqchip/irq-loongson-pch-msi.c | 255 ++++++++++++++++++
drivers/irqchip/irq-loongson-pch-pic.c | 243 +++++++++++++++++
8 files changed, 917 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,htvec.yaml
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-msi.yaml
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-pic.yaml
create mode 100644 drivers/irqchip/irq-loongson-htvec.c
create mode 100644 drivers/irqchip/irq-loongson-pch-msi.c
create mode 100644 drivers/irqchip/irq-loongson-pch-pic.c
--
2.27.0.rc0
^ permalink raw reply
* Re: [PATCH 1/1] dt-bindings: rng: Convert OMAP RNG to schema
From: Rob Herring @ 2020-05-28 15:27 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-crypto, devicetree, mpm, herbert, robh+dt
In-Reply-To: <20200514131947.28094-1-t-kristo@ti.com>
On Thu, 14 May 2020 16:19:47 +0300, Tero Kristo wrote:
> Convert TI OMAP Random number generator bindings to DT schema.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> .../devicetree/bindings/rng/omap_rng.txt | 38 ---------
> .../devicetree/bindings/rng/ti,omap-rng.yaml | 77 +++++++++++++++++++
> 2 files changed, 77 insertions(+), 38 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/rng/omap_rng.txt
> create mode 100644 Documentation/devicetree/bindings/rng/ti,omap-rng.yaml
>
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
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