diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2017-02-10 17:44:44 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2017-04-12 09:51:47 +0200 |
commit | cead8f9dfd8395577c6274e34fd592bcf76afe1c (patch) | |
tree | 77b9758510e0323d3f1758b9f9887ac022d60cbb /target/linux/apm821xx/patches-4.4 | |
parent | 51397d7d95d9f5e210a5557f65de1fa21e6f5921 (diff) | |
download | upstream-cead8f9dfd8395577c6274e34fd592bcf76afe1c.tar.gz upstream-cead8f9dfd8395577c6274e34fd592bcf76afe1c.tar.bz2 upstream-cead8f9dfd8395577c6274e34fd592bcf76afe1c.zip |
apm821xx: remove 4.4 kernel support
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Diffstat (limited to 'target/linux/apm821xx/patches-4.4')
22 files changed, 0 insertions, 6788 deletions
diff --git a/target/linux/apm821xx/patches-4.4/001-crypto4xx-integrate-ppc4xx-rng-into-crypto4xx.patch b/target/linux/apm821xx/patches-4.4/001-crypto4xx-integrate-ppc4xx-rng-into-crypto4xx.patch deleted file mode 100644 index 324c695d05..0000000000 --- a/target/linux/apm821xx/patches-4.4/001-crypto4xx-integrate-ppc4xx-rng-into-crypto4xx.patch +++ /dev/null @@ -1,481 +0,0 @@ -From 5343e674f32fb82b7a80a24b5a84eee62d3fe624 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@googlemail.com> -Date: Mon, 18 Apr 2016 12:57:41 +0200 -Subject: [PATCH] crypto4xx: integrate ppc4xx-rng into crypto4xx - -This patch integrates the ppc4xx-rng driver into the existing -crypto4xx. This is because the true random number generator -is controlled and part of the security core. - -Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> -Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> ---- - drivers/char/hw_random/Kconfig | 13 --- - drivers/char/hw_random/Makefile | 1 - - drivers/char/hw_random/ppc4xx-rng.c | 147 -------------------------------- - drivers/crypto/Kconfig | 8 ++ - drivers/crypto/amcc/Makefile | 1 + - drivers/crypto/amcc/crypto4xx_core.c | 7 +- - drivers/crypto/amcc/crypto4xx_core.h | 4 + - drivers/crypto/amcc/crypto4xx_reg_def.h | 1 + - drivers/crypto/amcc/crypto4xx_trng.c | 131 ++++++++++++++++++++++++++++ - drivers/crypto/amcc/crypto4xx_trng.h | 34 ++++++++ - 10 files changed, 184 insertions(+), 163 deletions(-) - delete mode 100644 drivers/char/hw_random/ppc4xx-rng.c - create mode 100644 drivers/crypto/amcc/crypto4xx_trng.c - create mode 100644 drivers/crypto/amcc/crypto4xx_trng.h - ---- a/drivers/char/hw_random/Kconfig -+++ b/drivers/char/hw_random/Kconfig -@@ -268,19 +268,6 @@ config HW_RANDOM_NOMADIK - - If unsure, say Y. - --config HW_RANDOM_PPC4XX -- tristate "PowerPC 4xx generic true random number generator support" -- depends on PPC && 4xx -- default HW_RANDOM -- ---help--- -- This driver provides the kernel-side support for the TRNG hardware -- found in the security function of some PowerPC 4xx SoCs. -- -- To compile this driver as a module, choose M here: the -- module will be called ppc4xx-rng. -- -- If unsure, say N. -- - config HW_RANDOM_PSERIES - tristate "pSeries HW Random Number Generator support" - depends on PPC64 && IBMVIO ---- a/drivers/char/hw_random/Makefile -+++ b/drivers/char/hw_random/Makefile -@@ -22,7 +22,6 @@ obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939 - obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o - obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o - obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o --obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o - obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o - obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o - obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o ---- a/drivers/char/hw_random/ppc4xx-rng.c -+++ /dev/null -@@ -1,147 +0,0 @@ --/* -- * Generic PowerPC 44x RNG driver -- * -- * Copyright 2011 IBM Corporation -- * -- * This program is free software; you can redistribute it and/or modify it -- * under the terms of the GNU General Public License as published by the -- * Free Software Foundation; version 2 of the License. -- */ -- --#include <linux/module.h> --#include <linux/kernel.h> --#include <linux/platform_device.h> --#include <linux/hw_random.h> --#include <linux/delay.h> --#include <linux/of_address.h> --#include <linux/of_platform.h> --#include <asm/io.h> -- --#define PPC4XX_TRNG_DEV_CTRL 0x60080 -- --#define PPC4XX_TRNGE 0x00020000 --#define PPC4XX_TRNG_CTRL 0x0008 --#define PPC4XX_TRNG_CTRL_DALM 0x20 --#define PPC4XX_TRNG_STAT 0x0004 --#define PPC4XX_TRNG_STAT_B 0x1 --#define PPC4XX_TRNG_DATA 0x0000 -- --#define MODULE_NAME "ppc4xx_rng" -- --static int ppc4xx_rng_data_present(struct hwrng *rng, int wait) --{ -- void __iomem *rng_regs = (void __iomem *) rng->priv; -- int busy, i, present = 0; -- -- for (i = 0; i < 20; i++) { -- busy = (in_le32(rng_regs + PPC4XX_TRNG_STAT) & PPC4XX_TRNG_STAT_B); -- if (!busy || !wait) { -- present = 1; -- break; -- } -- udelay(10); -- } -- return present; --} -- --static int ppc4xx_rng_data_read(struct hwrng *rng, u32 *data) --{ -- void __iomem *rng_regs = (void __iomem *) rng->priv; -- *data = in_le32(rng_regs + PPC4XX_TRNG_DATA); -- return 4; --} -- --static int ppc4xx_rng_enable(int enable) --{ -- struct device_node *ctrl; -- void __iomem *ctrl_reg; -- int err = 0; -- u32 val; -- -- /* Find the main crypto device node and map it to turn the TRNG on */ -- ctrl = of_find_compatible_node(NULL, NULL, "amcc,ppc4xx-crypto"); -- if (!ctrl) -- return -ENODEV; -- -- ctrl_reg = of_iomap(ctrl, 0); -- if (!ctrl_reg) { -- err = -ENODEV; -- goto out; -- } -- -- val = in_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL); -- -- if (enable) -- val |= PPC4XX_TRNGE; -- else -- val = val & ~PPC4XX_TRNGE; -- -- out_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL, val); -- iounmap(ctrl_reg); -- --out: -- of_node_put(ctrl); -- -- return err; --} -- --static struct hwrng ppc4xx_rng = { -- .name = MODULE_NAME, -- .data_present = ppc4xx_rng_data_present, -- .data_read = ppc4xx_rng_data_read, --}; -- --static int ppc4xx_rng_probe(struct platform_device *dev) --{ -- void __iomem *rng_regs; -- int err = 0; -- -- rng_regs = of_iomap(dev->dev.of_node, 0); -- if (!rng_regs) -- return -ENODEV; -- -- err = ppc4xx_rng_enable(1); -- if (err) -- return err; -- -- out_le32(rng_regs + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); -- ppc4xx_rng.priv = (unsigned long) rng_regs; -- -- err = hwrng_register(&ppc4xx_rng); -- -- return err; --} -- --static int ppc4xx_rng_remove(struct platform_device *dev) --{ -- void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv; -- -- hwrng_unregister(&ppc4xx_rng); -- ppc4xx_rng_enable(0); -- iounmap(rng_regs); -- -- return 0; --} -- --static const struct of_device_id ppc4xx_rng_match[] = { -- { .compatible = "ppc4xx-rng", }, -- { .compatible = "amcc,ppc460ex-rng", }, -- { .compatible = "amcc,ppc440epx-rng", }, -- {}, --}; --MODULE_DEVICE_TABLE(of, ppc4xx_rng_match); -- --static struct platform_driver ppc4xx_rng_driver = { -- .driver = { -- .name = MODULE_NAME, -- .of_match_table = ppc4xx_rng_match, -- }, -- .probe = ppc4xx_rng_probe, -- .remove = ppc4xx_rng_remove, --}; -- --module_platform_driver(ppc4xx_rng_driver); -- --MODULE_LICENSE("GPL"); --MODULE_AUTHOR("Josh Boyer <jwboyer@linux.vnet.ibm.com>"); --MODULE_DESCRIPTION("HW RNG driver for PPC 4xx processors"); ---- a/drivers/crypto/Kconfig -+++ b/drivers/crypto/Kconfig -@@ -277,6 +277,14 @@ config CRYPTO_DEV_PPC4XX - help - This option allows you to have support for AMCC crypto acceleration. - -+config HW_RANDOM_PPC4XX -+ bool "PowerPC 4xx generic true random number generator support" -+ depends on CRYPTO_DEV_PPC4XX && HW_RANDOM -+ default y -+ ---help--- -+ This option provides the kernel-side support for the TRNG hardware -+ found in the security function of some PowerPC 4xx SoCs. -+ - config CRYPTO_DEV_OMAP_SHAM - tristate "Support for OMAP MD5/SHA1/SHA2 hw accelerator" - depends on ARCH_OMAP2PLUS ---- a/drivers/crypto/amcc/Makefile -+++ b/drivers/crypto/amcc/Makefile -@@ -1,2 +1,3 @@ - obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o - crypto4xx-y := crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o -+crypto4xx-$(CONFIG_HW_RANDOM_PPC4XX) += crypto4xx_trng.o ---- a/drivers/crypto/amcc/crypto4xx_core.c -+++ b/drivers/crypto/amcc/crypto4xx_core.c -@@ -40,6 +40,7 @@ - #include "crypto4xx_reg_def.h" - #include "crypto4xx_core.h" - #include "crypto4xx_sa.h" -+#include "crypto4xx_trng.h" - - #define PPC4XX_SEC_VERSION_STR "0.5" - -@@ -1221,6 +1222,7 @@ static int crypto4xx_probe(struct platfo - if (rc) - goto err_start_dev; - -+ ppc4xx_trng_probe(core_dev); - return 0; - - err_start_dev: -@@ -1248,6 +1250,8 @@ static int crypto4xx_remove(struct platf - struct device *dev = &ofdev->dev; - struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev); - -+ ppc4xx_trng_remove(core_dev); -+ - free_irq(core_dev->irq, dev); - irq_dispose_mapping(core_dev->irq); - -@@ -1268,7 +1272,7 @@ MODULE_DEVICE_TABLE(of, crypto4xx_match) - - static struct platform_driver crypto4xx_driver = { - .driver = { -- .name = "crypto4xx", -+ .name = MODULE_NAME, - .of_match_table = crypto4xx_match, - }, - .probe = crypto4xx_probe, -@@ -1280,4 +1284,3 @@ module_platform_driver(crypto4xx_driver) - MODULE_LICENSE("GPL"); - MODULE_AUTHOR("James Hsiao <jhsiao@amcc.com>"); - MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator"); -- ---- a/drivers/crypto/amcc/crypto4xx_core.h -+++ b/drivers/crypto/amcc/crypto4xx_core.h -@@ -24,6 +24,8 @@ - - #include <crypto/internal/hash.h> - -+#define MODULE_NAME "crypto4xx" -+ - #define PPC460SX_SDR0_SRST 0x201 - #define PPC405EX_SDR0_SRST 0x200 - #define PPC460EX_SDR0_SRST 0x201 -@@ -72,6 +74,7 @@ struct crypto4xx_device { - char *name; - u64 ce_phy_address; - void __iomem *ce_base; -+ void __iomem *trng_base; - - void *pdr; /* base address of packet - descriptor ring */ -@@ -106,6 +109,7 @@ struct crypto4xx_core_device { - struct device *device; - struct platform_device *ofdev; - struct crypto4xx_device *dev; -+ struct hwrng *trng; - u32 int_status; - u32 irq; - struct tasklet_struct tasklet; ---- a/drivers/crypto/amcc/crypto4xx_reg_def.h -+++ b/drivers/crypto/amcc/crypto4xx_reg_def.h -@@ -125,6 +125,7 @@ - #define PPC4XX_INTERRUPT_CLR 0x3ffff - #define PPC4XX_PRNG_CTRL_AUTO_EN 0x3 - #define PPC4XX_DC_3DES_EN 1 -+#define PPC4XX_TRNG_EN 0x00020000 - #define PPC4XX_INT_DESCR_CNT 4 - #define PPC4XX_INT_TIMEOUT_CNT 0 - #define PPC4XX_INT_CFG 1 ---- /dev/null -+++ b/drivers/crypto/amcc/crypto4xx_trng.c -@@ -0,0 +1,131 @@ -+/* -+ * Generic PowerPC 44x RNG driver -+ * -+ * Copyright 2011 IBM Corporation -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; version 2 of the License. -+ */ -+ -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/interrupt.h> -+#include <linux/platform_device.h> -+#include <linux/hw_random.h> -+#include <linux/delay.h> -+#include <linux/of_address.h> -+#include <linux/of_platform.h> -+#include <linux/io.h> -+ -+#include "crypto4xx_core.h" -+#include "crypto4xx_trng.h" -+#include "crypto4xx_reg_def.h" -+ -+#define PPC4XX_TRNG_CTRL 0x0008 -+#define PPC4XX_TRNG_CTRL_DALM 0x20 -+#define PPC4XX_TRNG_STAT 0x0004 -+#define PPC4XX_TRNG_STAT_B 0x1 -+#define PPC4XX_TRNG_DATA 0x0000 -+ -+static int ppc4xx_trng_data_present(struct hwrng *rng, int wait) -+{ -+ struct crypto4xx_device *dev = (void *)rng->priv; -+ int busy, i, present = 0; -+ -+ for (i = 0; i < 20; i++) { -+ busy = (in_le32(dev->trng_base + PPC4XX_TRNG_STAT) & -+ PPC4XX_TRNG_STAT_B); -+ if (!busy || !wait) { -+ present = 1; -+ break; -+ } -+ udelay(10); -+ } -+ return present; -+} -+ -+static int ppc4xx_trng_data_read(struct hwrng *rng, u32 *data) -+{ -+ struct crypto4xx_device *dev = (void *)rng->priv; -+ *data = in_le32(dev->trng_base + PPC4XX_TRNG_DATA); -+ return 4; -+} -+ -+static void ppc4xx_trng_enable(struct crypto4xx_device *dev, bool enable) -+{ -+ u32 device_ctrl; -+ -+ device_ctrl = readl(dev->ce_base + CRYPTO4XX_DEVICE_CTRL); -+ if (enable) -+ device_ctrl |= PPC4XX_TRNG_EN; -+ else -+ device_ctrl &= ~PPC4XX_TRNG_EN; -+ writel(device_ctrl, dev->ce_base + CRYPTO4XX_DEVICE_CTRL); -+} -+ -+static const struct of_device_id ppc4xx_trng_match[] = { -+ { .compatible = "ppc4xx-rng", }, -+ { .compatible = "amcc,ppc460ex-rng", }, -+ { .compatible = "amcc,ppc440epx-rng", }, -+ {}, -+}; -+ -+void ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev) -+{ -+ struct crypto4xx_device *dev = core_dev->dev; -+ struct device_node *trng = NULL; -+ struct hwrng *rng = NULL; -+ int err; -+ -+ /* Find the TRNG device node and map it */ -+ trng = of_find_matching_node(NULL, ppc4xx_trng_match); -+ if (!trng || !of_device_is_available(trng)) -+ return; -+ -+ dev->trng_base = of_iomap(trng, 0); -+ of_node_put(trng); -+ if (!dev->trng_base) -+ goto err_out; -+ -+ rng = kzalloc(sizeof(*rng), GFP_KERNEL); -+ if (!rng) -+ goto err_out; -+ -+ rng->name = MODULE_NAME; -+ rng->data_present = ppc4xx_trng_data_present; -+ rng->data_read = ppc4xx_trng_data_read; -+ rng->priv = (unsigned long) dev; -+ core_dev->trng = rng; -+ ppc4xx_trng_enable(dev, true); -+ out_le32(dev->trng_base + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); -+ err = devm_hwrng_register(core_dev->device, core_dev->trng); -+ if (err) { -+ ppc4xx_trng_enable(dev, false); -+ dev_err(core_dev->device, "failed to register hwrng (%d).\n", -+ err); -+ goto err_out; -+ } -+ return; -+ -+err_out: -+ of_node_put(trng); -+ iounmap(dev->trng_base); -+ kfree(rng); -+ dev->trng_base = NULL; -+ core_dev->trng = NULL; -+} -+ -+void ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev) -+{ -+ if (core_dev && core_dev->trng) { -+ struct crypto4xx_device *dev = core_dev->dev; -+ -+ devm_hwrng_unregister(core_dev->device, core_dev->trng); -+ ppc4xx_trng_enable(dev, false); -+ iounmap(dev->trng_base); -+ kfree(core_dev->trng); -+ } -+} -+ -+MODULE_ALIAS("ppc4xx_rng"); ---- /dev/null -+++ b/drivers/crypto/amcc/crypto4xx_trng.h -@@ -0,0 +1,34 @@ -+/** -+ * AMCC SoC PPC4xx Crypto Driver -+ * -+ * Copyright (c) 2008 Applied Micro Circuits Corporation. -+ * All rights reserved. James Hsiao <jhsiao@amcc.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * This file defines the security context -+ * associate format. -+ */ -+ -+#ifndef __CRYPTO4XX_TRNG_H__ -+#define __CRYPTO4XX_TRNG_H__ -+ -+#ifdef CONFIG_HW_RANDOM_PPC4XX -+void ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev); -+void ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev); -+#else -+static inline void ppc4xx_trng_probe( -+ struct crypto4xx_device *dev __maybe_unused) { } -+static inline void ppc4xx_trng_remove( -+ struct crypto4xx_device *dev __maybe_unused) { } -+#endif -+ -+#endif diff --git a/target/linux/apm821xx/patches-4.4/002-powerpc_ibm_phy_add_ar8035.patch b/target/linux/apm821xx/patches-4.4/002-powerpc_ibm_phy_add_ar8035.patch deleted file mode 100644 index f885167e04..0000000000 --- a/target/linux/apm821xx/patches-4.4/002-powerpc_ibm_phy_add_ar8035.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0c13957a43a90b1522eb616f3c9967ec44e4da1d Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@googlemail.com> -Date: Tue, 3 May 2016 13:58:24 +0200 -Subject: [PATCH] drivers: net: emac: add Atheros AR8035 phy initialization - code -To: netdev@vger.kernel.org - -This patch adds the phy initialization code for Qualcomm -Atheros AR8035 phy. This configuration is found in the -Cisco Meraki MR24. - -Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> ---- - drivers/net/ethernet/ibm/emac/phy.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - ---- a/drivers/net/ethernet/ibm/emac/phy.c -+++ b/drivers/net/ethernet/ibm/emac/phy.c -@@ -470,12 +470,38 @@ static struct mii_phy_def m88e1112_phy_d - .ops = &m88e1112_phy_ops, - }; - -+static int ar8035_init(struct mii_phy *phy) -+{ -+ phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */ -+ phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */ -+ phy_write(phy, 0x1d, 0xb); /* Address hib ctrl */ -+ phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */ -+ -+ return 0; -+} -+ -+static struct mii_phy_ops ar8035_phy_ops = { -+ .init = ar8035_init, -+ .setup_aneg = genmii_setup_aneg, -+ .setup_forced = genmii_setup_forced, -+ .poll_link = genmii_poll_link, -+ .read_link = genmii_read_link, -+}; -+ -+static struct mii_phy_def ar8035_phy_def = { -+ .phy_id = 0x004dd070, -+ .phy_id_mask = 0xfffffff0, -+ .name = "Atheros 8035 Gigabit Ethernet", -+ .ops = &ar8035_phy_ops, -+}; -+ - static struct mii_phy_def *mii_phy_table[] = { - &et1011c_phy_def, - &cis8201_phy_def, - &bcm5248_phy_def, - &m88e1111_phy_def, - &m88e1112_phy_def, -+ &ar8035_phy_def, - &genmii_phy_def, - NULL - }; diff --git a/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch b/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch deleted file mode 100644 index 89bdd49d2b..0000000000 --- a/target/linux/apm821xx/patches-4.4/010-dmaengine-Add-transfer-termination-synchronization-s.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 7bd903c5ca47fde5ad52370a47776491813c772e Mon Sep 17 00:00:00 2001 -From: Peter Ujfalusi <peter.ujfalusi@ti.com> -Date: Mon, 14 Dec 2015 22:47:39 +0200 -Subject: [PATCH 1/3] dmaengine: core: Move and merge the code paths using - private_candidate - -Channel matching with private_candidate() is used in two paths, the error -checking is slightly different in them and they are duplicating code also. -Move the code under find_candidate() to provide consistent execution and -going to allow us to reuse this mode of channel lookup later. - -Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> -Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> -Reviewed-by: Arnd Bergmann <arnd@arndb.de> -Signed-off-by: Vinod Koul <vinod.koul@intel.com> ---- - drivers/dma/dmaengine.c | 81 +++++++++++++++++++++++++------------------------ - 1 file changed, 42 insertions(+), 39 deletions(-) - ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -542,6 +542,42 @@ static struct dma_chan *private_candidat - return NULL; - } - -+static struct dma_chan *find_candidate(struct dma_device *device, -+ const dma_cap_mask_t *mask, -+ dma_filter_fn fn, void *fn_param) -+{ -+ struct dma_chan *chan = private_candidate(mask, device, fn, fn_param); -+ int err; -+ -+ if (chan) { -+ /* Found a suitable channel, try to grab, prep, and return it. -+ * We first set DMA_PRIVATE to disable balance_ref_count as this -+ * channel will not be published in the general-purpose -+ * allocator -+ */ -+ dma_cap_set(DMA_PRIVATE, device->cap_mask); -+ device->privatecnt++; -+ err = dma_chan_get(chan); -+ -+ if (err) { -+ if (err == -ENODEV) { -+ pr_debug("%s: %s module removed\n", __func__, -+ dma_chan_name(chan)); -+ list_del_rcu(&device->global_node); -+ } else -+ pr_debug("%s: failed to get %s: (%d)\n", -+ __func__, dma_chan_name(chan), err); -+ -+ if (--device->privatecnt == 0) -+ dma_cap_clear(DMA_PRIVATE, device->cap_mask); -+ -+ chan = ERR_PTR(err); -+ } -+ } -+ -+ return chan ? chan : ERR_PTR(-EPROBE_DEFER); -+} -+ - /** - * dma_get_slave_channel - try to get specific channel exclusively - * @chan: target channel -@@ -580,7 +616,6 @@ struct dma_chan *dma_get_any_slave_chann - { - dma_cap_mask_t mask; - struct dma_chan *chan; -- int err; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); -@@ -588,23 +623,11 @@ struct dma_chan *dma_get_any_slave_chann - /* lock against __dma_request_channel */ - mutex_lock(&dma_list_mutex); - -- chan = private_candidate(&mask, device, NULL, NULL); -- if (chan) { -- dma_cap_set(DMA_PRIVATE, device->cap_mask); -- device->privatecnt++; -- err = dma_chan_get(chan); -- if (err) { -- pr_debug("%s: failed to get %s: (%d)\n", -- __func__, dma_chan_name(chan), err); -- chan = NULL; -- if (--device->privatecnt == 0) -- dma_cap_clear(DMA_PRIVATE, device->cap_mask); -- } -- } -+ chan = find_candidate(device, &mask, NULL, NULL); - - mutex_unlock(&dma_list_mutex); - -- return chan; -+ return IS_ERR(chan) ? NULL : chan; - } - EXPORT_SYMBOL_GPL(dma_get_any_slave_channel); - -@@ -621,35 +644,15 @@ struct dma_chan *__dma_request_channel(c - { - struct dma_device *device, *_d; - struct dma_chan *chan = NULL; -- int err; - - /* Find a channel */ - mutex_lock(&dma_list_mutex); - list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { -- chan = private_candidate(mask, device, fn, fn_param); -- if (chan) { -- /* Found a suitable channel, try to grab, prep, and -- * return it. We first set DMA_PRIVATE to disable -- * balance_ref_count as this channel will not be -- * published in the general-purpose allocator -- */ -- dma_cap_set(DMA_PRIVATE, device->cap_mask); -- device->privatecnt++; -- err = dma_chan_get(chan); -+ chan = find_candidate(device, mask, fn, fn_param); -+ if (!IS_ERR(chan)) -+ break; - -- if (err == -ENODEV) { -- pr_debug("%s: %s module removed\n", -- __func__, dma_chan_name(chan)); -- list_del_rcu(&device->global_node); -- } else if (err) -- pr_debug("%s: failed to get %s: (%d)\n", -- __func__, dma_chan_name(chan), err); -- else -- break; -- if (--device->privatecnt == 0) -- dma_cap_clear(DMA_PRIVATE, device->cap_mask); -- chan = NULL; -- } -+ chan = NULL; - } - mutex_unlock(&dma_list_mutex); - diff --git a/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch b/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch deleted file mode 100644 index 7a0e26f6a3..0000000000 --- a/target/linux/apm821xx/patches-4.4/011-dmaengine-core-Introduce-new-universal-API-to-reques.patch +++ /dev/null @@ -1,335 +0,0 @@ -From a8135d0d79e9d0ad3a4ff494fceeaae838becf38 Mon Sep 17 00:00:00 2001 -From: Peter Ujfalusi <peter.ujfalusi@ti.com> -Date: Mon, 14 Dec 2015 22:47:40 +0200 -Subject: [PATCH 2/3] dmaengine: core: Introduce new, universal API to request - a channel - -The two API function can cover most, if not all current APIs used to -request a channel. With minimal effort dmaengine drivers, platforms and -dmaengine user drivers can be converted to use the two function. - -struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); - -To request any channel matching with the requested capabilities, can be -used to request channel for memcpy, memset, xor, etc where no hardware -synchronization is needed. - -struct dma_chan *dma_request_chan(struct device *dev, const char *name); -To request a slave channel. The dma_request_chan() will try to find the -channel via DT, ACPI or in case if the kernel booted in non DT/ACPI mode -it will use a filter lookup table and retrieves the needed information from -the dma_slave_map provided by the DMA drivers. -This legacy mode needs changes in platform code, in dmaengine drivers and -finally the dmaengine user drivers can be converted: - -For each dmaengine driver an array of DMA device, slave and the parameter -for the filter function needs to be added: - -static const struct dma_slave_map da830_edma_map[] = { - { "davinci-mcasp.0", "rx", EDMA_FILTER_PARAM(0, 0) }, - { "davinci-mcasp.0", "tx", EDMA_FILTER_PARAM(0, 1) }, - { "davinci-mcasp.1", "rx", EDMA_FILTER_PARAM(0, 2) }, - { "davinci-mcasp.1", "tx", EDMA_FILTER_PARAM(0, 3) }, - { "davinci-mcasp.2", "rx", EDMA_FILTER_PARAM(0, 4) }, - { "davinci-mcasp.2", "tx", EDMA_FILTER_PARAM(0, 5) }, - { "spi_davinci.0", "rx", EDMA_FILTER_PARAM(0, 14) }, - { "spi_davinci.0", "tx", EDMA_FILTER_PARAM(0, 15) }, - { "da830-mmc.0", "rx", EDMA_FILTER_PARAM(0, 16) }, - { "da830-mmc.0", "tx", EDMA_FILTER_PARAM(0, 17) }, - { "spi_davinci.1", "rx", EDMA_FILTER_PARAM(0, 18) }, - { "spi_davinci.1", "tx", EDMA_FILTER_PARAM(0, 19) }, -}; - -This information is going to be needed by the dmaengine driver, so -modification to the platform_data is needed, and the driver map should be -added to the pdata of the DMA driver: - -da8xx_edma0_pdata.slave_map = da830_edma_map; -da8xx_edma0_pdata.slavecnt = ARRAY_SIZE(da830_edma_map); - -The DMA driver then needs to configure the needed device -> filter_fn -mapping before it registers with dma_async_device_register() : - -ecc->dma_slave.filter_map.map = info->slave_map; -ecc->dma_slave.filter_map.mapcnt = info->slavecnt; -ecc->dma_slave.filter_map.fn = edma_filter_fn; - -When neither DT or ACPI lookup is available the dma_request_chan() will -try to match the requester's device name with the filter_map's list of -device names, when a match found it will use the information from the -dma_slave_map to get the channel with the dma_get_channel() internal -function. - -Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> -Reviewed-by: Arnd Bergmann <arnd@arndb.de> -Signed-off-by: Vinod Koul <vinod.koul@intel.com> ---- - Documentation/dmaengine/client.txt | 23 +++------- - drivers/dma/dmaengine.c | 89 +++++++++++++++++++++++++++++++++----- - include/linux/dmaengine.h | 51 +++++++++++++++++++--- - 3 files changed, 127 insertions(+), 36 deletions(-) - ---- a/Documentation/dmaengine/client.txt -+++ b/Documentation/dmaengine/client.txt -@@ -22,25 +22,14 @@ The slave DMA usage consists of followin - Channel allocation is slightly different in the slave DMA context, - client drivers typically need a channel from a particular DMA - controller only and even in some cases a specific channel is desired. -- To request a channel dma_request_channel() API is used. -+ To request a channel dma_request_chan() API is used. - - Interface: -- struct dma_chan *dma_request_channel(dma_cap_mask_t mask, -- dma_filter_fn filter_fn, -- void *filter_param); -- where dma_filter_fn is defined as: -- typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param); -+ struct dma_chan *dma_request_chan(struct device *dev, const char *name); - -- The 'filter_fn' parameter is optional, but highly recommended for -- slave and cyclic channels as they typically need to obtain a specific -- DMA channel. -- -- When the optional 'filter_fn' parameter is NULL, dma_request_channel() -- simply returns the first channel that satisfies the capability mask. -- -- Otherwise, the 'filter_fn' routine will be called once for each free -- channel which has a capability in 'mask'. 'filter_fn' is expected to -- return 'true' when the desired DMA channel is found. -+ Which will find and return the 'name' DMA channel associated with the 'dev' -+ device. The association is done via DT, ACPI or board file based -+ dma_slave_map matching table. - - A channel allocated via this interface is exclusive to the caller, - until dma_release_channel() is called. ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -43,6 +43,7 @@ - - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -+#include <linux/platform_device.h> - #include <linux/dma-mapping.h> - #include <linux/init.h> - #include <linux/module.h> -@@ -665,27 +666,73 @@ struct dma_chan *__dma_request_channel(c - } - EXPORT_SYMBOL_GPL(__dma_request_channel); - -+static const struct dma_slave_map *dma_filter_match(struct dma_device *device, -+ const char *name, -+ struct device *dev) -+{ -+ int i; -+ -+ if (!device->filter.mapcnt) -+ return NULL; -+ -+ for (i = 0; i < device->filter.mapcnt; i++) { -+ const struct dma_slave_map *map = &device->filter.map[i]; -+ -+ if (!strcmp(map->devname, dev_name(dev)) && -+ !strcmp(map->slave, name)) -+ return map; -+ } -+ -+ return NULL; -+} -+ - /** -- * dma_request_slave_channel_reason - try to allocate an exclusive slave channel -+ * dma_request_chan - try to allocate an exclusive slave channel - * @dev: pointer to client device structure - * @name: slave channel name - * - * Returns pointer to appropriate DMA channel on success or an error pointer. - */ --struct dma_chan *dma_request_slave_channel_reason(struct device *dev, -- const char *name) -+struct dma_chan *dma_request_chan(struct device *dev, const char *name) - { -+ struct dma_device *d, *_d; -+ struct dma_chan *chan = NULL; -+ - /* If device-tree is present get slave info from here */ - if (dev->of_node) -- return of_dma_request_slave_channel(dev->of_node, name); -+ chan = of_dma_request_slave_channel(dev->of_node, name); - - /* If device was enumerated by ACPI get slave info from here */ -- if (ACPI_HANDLE(dev)) -- return acpi_dma_request_slave_chan_by_name(dev, name); -+ if (has_acpi_companion(dev) && !chan) -+ chan = acpi_dma_request_slave_chan_by_name(dev, name); -+ -+ if (chan) { -+ /* Valid channel found or requester need to be deferred */ -+ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) -+ return chan; -+ } -+ -+ /* Try to find the channel via the DMA filter map(s) */ -+ mutex_lock(&dma_list_mutex); -+ list_for_each_entry_safe(d, _d, &dma_device_list, global_node) { -+ dma_cap_mask_t mask; -+ const struct dma_slave_map *map = dma_filter_match(d, name, dev); -+ -+ if (!map) -+ continue; -+ -+ dma_cap_zero(mask); -+ dma_cap_set(DMA_SLAVE, mask); - -- return ERR_PTR(-ENODEV); -+ chan = find_candidate(d, &mask, d->filter.fn, map->param); -+ if (!IS_ERR(chan)) -+ break; -+ } -+ mutex_unlock(&dma_list_mutex); -+ -+ return chan ? chan : ERR_PTR(-EPROBE_DEFER); - } --EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason); -+EXPORT_SYMBOL_GPL(dma_request_chan); - - /** - * dma_request_slave_channel - try to allocate an exclusive slave channel -@@ -697,17 +744,35 @@ EXPORT_SYMBOL_GPL(dma_request_slave_chan - struct dma_chan *dma_request_slave_channel(struct device *dev, - const char *name) - { -- struct dma_chan *ch = dma_request_slave_channel_reason(dev, name); -+ struct dma_chan *ch = dma_request_chan(dev, name); - if (IS_ERR(ch)) - return NULL; - -- dma_cap_set(DMA_PRIVATE, ch->device->cap_mask); -- ch->device->privatecnt++; -- - return ch; - } - EXPORT_SYMBOL_GPL(dma_request_slave_channel); - -+/** -+ * dma_request_chan_by_mask - allocate a channel satisfying certain capabilities -+ * @mask: capabilities that the channel must satisfy -+ * -+ * Returns pointer to appropriate DMA channel on success or an error pointer. -+ */ -+struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask) -+{ -+ struct dma_chan *chan; -+ -+ if (!mask) -+ return ERR_PTR(-ENODEV); -+ -+ chan = __dma_request_channel(mask, NULL, NULL); -+ if (!chan) -+ chan = ERR_PTR(-ENODEV); -+ -+ return chan; -+} -+EXPORT_SYMBOL_GPL(dma_request_chan_by_mask); -+ - void dma_release_channel(struct dma_chan *chan) - { - mutex_lock(&dma_list_mutex); ---- a/include/linux/dmaengine.h -+++ b/include/linux/dmaengine.h -@@ -607,11 +607,38 @@ enum dmaengine_alignment { - }; - - /** -+ * struct dma_slave_map - associates slave device and it's slave channel with -+ * parameter to be used by a filter function -+ * @devname: name of the device -+ * @slave: slave channel name -+ * @param: opaque parameter to pass to struct dma_filter.fn -+ */ -+struct dma_slave_map { -+ const char *devname; -+ const char *slave; -+ void *param; -+}; -+ -+/** -+ * struct dma_filter - information for slave device/channel to filter_fn/param -+ * mapping -+ * @fn: filter function callback -+ * @mapcnt: number of slave device/channel in the map -+ * @map: array of channel to filter mapping data -+ */ -+struct dma_filter { -+ dma_filter_fn fn; -+ int mapcnt; -+ const struct dma_slave_map *map; -+}; -+ -+/** - * struct dma_device - info on the entity supplying DMA services - * @chancnt: how many DMA channels are supported - * @privatecnt: how many DMA channels are requested by dma_request_channel - * @channels: the list of struct dma_chan - * @global_node: list_head for global dma_device_list -+ * @filter: information for device/slave to filter function/param mapping - * @cap_mask: one or more dma_capability flags - * @max_xor: maximum number of xor sources, 0 if no capability - * @max_pq: maximum number of PQ sources and PQ-continue capability -@@ -666,6 +693,7 @@ struct dma_device { - unsigned int privatecnt; - struct list_head channels; - struct list_head global_node; -+ struct dma_filter filter; - dma_cap_mask_t cap_mask; - unsigned short max_xor; - unsigned short max_pq; -@@ -1140,9 +1168,11 @@ enum dma_status dma_wait_for_async_tx(st - void dma_issue_pending_all(void); - struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, - dma_filter_fn fn, void *fn_param); --struct dma_chan *dma_request_slave_channel_reason(struct device *dev, -- const char *name); - struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); -+ -+struct dma_chan *dma_request_chan(struct device *dev, const char *name); -+struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); -+ - void dma_release_channel(struct dma_chan *chan); - int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); - #else -@@ -1166,16 +1196,21 @@ static inline struct dma_chan *__dma_req - { - return NULL; - } --static inline struct dma_chan *dma_request_slave_channel_reason( -- struct device *dev, const char *name) --{ -- return ERR_PTR(-ENODEV); --} - static inline struct dma_chan *dma_request_slave_channel(struct device *dev, - const char *name) - { - return NULL; - } -+static inline struct dma_chan *dma_request_chan(struct device *dev, -+ const char *name) -+{ -+ return ERR_PTR(-ENODEV); -+} -+static inline struct dma_chan *dma_request_chan_by_mask( -+ const dma_cap_mask_t *mask) -+{ -+ return ERR_PTR(-ENODEV); -+} - static inline void dma_release_channel(struct dma_chan *chan) - { - } -@@ -1186,6 +1221,8 @@ static inline int dma_get_slave_caps(str - } - #endif - -+#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name) -+ - static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx) - { - struct dma_slave_caps caps; diff --git a/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch b/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch deleted file mode 100644 index 21efb046c2..0000000000 --- a/target/linux/apm821xx/patches-4.4/012-dmaengine-Add-transfer-termination-synchronization-s.patch +++ /dev/null @@ -1,282 +0,0 @@ -From b36f09c3c441a6e59eab9315032e7d546571de3f Mon Sep 17 00:00:00 2001 -From: Lars-Peter Clausen <lars@metafoo.de> -Date: Tue, 20 Oct 2015 11:46:28 +0200 -Subject: [PATCH] dmaengine: Add transfer termination synchronization support - -The DMAengine API has a long standing race condition that is inherent to -the API itself. Calling dmaengine_terminate_all() is supposed to stop and -abort any pending or active transfers that have previously been submitted. -Unfortunately it is possible that this operation races against a currently -running (or with some drivers also scheduled) completion callback. - -Since the API allows dmaengine_terminate_all() to be called from atomic -context as well as from within a completion callback it is not possible to -synchronize to the execution of the completion callback from within -dmaengine_terminate_all() itself. - -This means that a user of the DMAengine API does not know when it is safe -to free resources used in the completion callback, which can result in a -use-after-free race condition. - -This patch addresses the issue by introducing an explicit synchronization -primitive to the DMAengine API called dmaengine_synchronize(). - -The existing dmaengine_terminate_all() is deprecated in favor of -dmaengine_terminate_sync() and dmaengine_terminate_async(). The former -aborts all pending and active transfers and synchronizes to the current -context, meaning it will wait until all running completion callbacks have -finished. This means it is only possible to call this function from -non-atomic context. The later function does not synchronize, but can still -be used in atomic context or from within a complete callback. It has to be -followed up by dmaengine_synchronize() before a client can free the -resources used in a completion callback. - -In addition to this the semantics of the device_terminate_all() callback -are slightly relaxed by this patch. It is now OK for a driver to only -schedule the termination of the active transfer, but does not necessarily -have to wait until the DMA controller has completely stopped. The driver -must ensure though that the controller has stopped and no longer accesses -any memory when the device_synchronize() callback returns. - -This was in part done since most drivers do not pay attention to this -anyway at the moment and to emphasize that this needs to be done when the -device_synchronize() callback is implemented. But it also helps with -implementing support for devices where stopping the controller can require -operations that may sleep. - -Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> -Signed-off-by: Vinod Koul <vinod.koul@intel.com> ---- - Documentation/dmaengine/client.txt | 38 ++++++++++++++- - Documentation/dmaengine/provider.txt | 20 +++++++- - drivers/dma/dmaengine.c | 5 +- - include/linux/dmaengine.h | 90 ++++++++++++++++++++++++++++++++++++ - 4 files changed, 148 insertions(+), 5 deletions(-) - ---- a/Documentation/dmaengine/client.txt -+++ b/Documentation/dmaengine/client.txt -@@ -117,7 +117,7 @@ The slave DMA usage consists of followin - transaction. - - For cyclic DMA, a callback function may wish to terminate the -- DMA via dmaengine_terminate_all(). -+ DMA via dmaengine_terminate_async(). - - Therefore, it is important that DMA engine drivers drop any - locks before calling the callback function which may cause a -@@ -155,12 +155,29 @@ The slave DMA usage consists of followin - - Further APIs: - --1. int dmaengine_terminate_all(struct dma_chan *chan) -+1. int dmaengine_terminate_sync(struct dma_chan *chan) -+ int dmaengine_terminate_async(struct dma_chan *chan) -+ int dmaengine_terminate_all(struct dma_chan *chan) /* DEPRECATED */ - - This causes all activity for the DMA channel to be stopped, and may - discard data in the DMA FIFO which hasn't been fully transferred. - No callback functions will be called for any incomplete transfers. - -+ Two variants of this function are available. -+ -+ dmaengine_terminate_async() might not wait until the DMA has been fully -+ stopped or until any running complete callbacks have finished. But it is -+ possible to call dmaengine_terminate_async() from atomic context or from -+ within a complete callback. dmaengine_synchronize() must be called before it -+ is safe to free the memory accessed by the DMA transfer or free resources -+ accessed from within the complete callback. -+ -+ dmaengine_terminate_sync() will wait for the transfer and any running -+ complete callbacks to finish before it returns. But the function must not be -+ called from atomic context or from within a complete callback. -+ -+ dmaengine_terminate_all() is deprecated and should not be used in new code. -+ - 2. int dmaengine_pause(struct dma_chan *chan) - - This pauses activity on the DMA channel without data loss. -@@ -186,3 +203,20 @@ Further APIs: - a running DMA channel. It is recommended that DMA engine users - pause or stop (via dmaengine_terminate_all()) the channel before - using this API. -+ -+5. void dmaengine_synchronize(struct dma_chan *chan) -+ -+ Synchronize the termination of the DMA channel to the current context. -+ -+ This function should be used after dmaengine_terminate_async() to synchronize -+ the termination of the DMA channel to the current context. The function will -+ wait for the transfer and any running complete callbacks to finish before it -+ returns. -+ -+ If dmaengine_terminate_async() is used to stop the DMA channel this function -+ must be called before it is safe to free memory accessed by previously -+ submitted descriptors or to free any resources accessed within the complete -+ callback of previously submitted descriptors. -+ -+ The behavior of this function is undefined if dma_async_issue_pending() has -+ been called between dmaengine_terminate_async() and this function. ---- a/Documentation/dmaengine/provider.txt -+++ b/Documentation/dmaengine/provider.txt -@@ -327,8 +327,24 @@ supported. - - * device_terminate_all - - Aborts all the pending and ongoing transfers on the channel -- - This command should operate synchronously on the channel, -- terminating right away all the channels -+ - For aborted transfers the complete callback should not be called -+ - Can be called from atomic context or from within a complete -+ callback of a descriptor. Must not sleep. Drivers must be able -+ to handle this correctly. -+ - Termination may be asynchronous. The driver does not have to -+ wait until the currently active transfer has completely stopped. -+ See device_synchronize. -+ -+ * device_synchronize -+ - Must synchronize the termination of a channel to the current -+ context. -+ - Must make sure that memory for previously submitted -+ descriptors is no longer accessed by the DMA controller. -+ - Must make sure that all complete callbacks for previously -+ submitted descriptors have finished running and none are -+ scheduled to run. -+ - May sleep. -+ - - Misc notes (stuff that should be documented, but don't really know - where to put them) ---- a/drivers/dma/dmaengine.c -+++ b/drivers/dma/dmaengine.c -@@ -266,8 +266,11 @@ static void dma_chan_put(struct dma_chan - module_put(dma_chan_to_owner(chan)); - - /* This channel is not in use anymore, free it */ -- if (!chan->client_count && chan->device->device_free_chan_resources) -+ if (!chan->client_count && chan->device->device_free_chan_resources) { -+ /* Make sure all operations have completed */ -+ dmaengine_synchronize(chan); - chan->device->device_free_chan_resources(chan); -+ } - - /* If the channel is used via a DMA request router, free the mapping */ - if (chan->router && chan->router->route_free) { ---- a/include/linux/dmaengine.h -+++ b/include/linux/dmaengine.h -@@ -681,6 +681,8 @@ struct dma_filter { - * paused. Returns 0 or an error code - * @device_terminate_all: Aborts all transfers on a channel. Returns 0 - * or an error code -+ * @device_synchronize: Synchronizes the termination of a transfers to the -+ * current context. - * @device_tx_status: poll for transaction completion, the optional - * txstate parameter can be supplied with a pointer to get a - * struct with auxiliary transfer status information, otherwise the call -@@ -765,6 +767,7 @@ struct dma_device { - int (*device_pause)(struct dma_chan *chan); - int (*device_resume)(struct dma_chan *chan); - int (*device_terminate_all)(struct dma_chan *chan); -+ void (*device_synchronize)(struct dma_chan *chan); - - enum dma_status (*device_tx_status)(struct dma_chan *chan, - dma_cookie_t cookie, -@@ -856,6 +859,13 @@ static inline struct dma_async_tx_descri - src_sg, src_nents, flags); - } - -+/** -+ * dmaengine_terminate_all() - Terminate all active DMA transfers -+ * @chan: The channel for which to terminate the transfers -+ * -+ * This function is DEPRECATED use either dmaengine_terminate_sync() or -+ * dmaengine_terminate_async() instead. -+ */ - static inline int dmaengine_terminate_all(struct dma_chan *chan) - { - if (chan->device->device_terminate_all) -@@ -864,6 +874,86 @@ static inline int dmaengine_terminate_al - return -ENOSYS; - } - -+/** -+ * dmaengine_terminate_async() - Terminate all active DMA transfers -+ * @chan: The channel for which to terminate the transfers -+ * -+ * Calling this function will terminate all active and pending descriptors -+ * that have previously been submitted to the channel. It is not guaranteed -+ * though that the transfer for the active descriptor has stopped when the -+ * function returns. Furthermore it is possible the complete callback of a -+ * submitted transfer is still running when this function returns. -+ * -+ * dmaengine_synchronize() needs to be called before it is safe to free -+ * any memory that is accessed by previously submitted descriptors or before -+ * freeing any resources accessed from within the completion callback of any -+ * perviously submitted descriptors. -+ * -+ * This function can be called from atomic context as well as from within a -+ * complete callback of a descriptor submitted on the same channel. -+ * -+ * If none of the two conditions above apply consider using -+ * dmaengine_terminate_sync() instead. -+ */ -+static inline int dmaengine_terminate_async(struct dma_chan *chan) -+{ -+ if (chan->device->device_terminate_all) -+ return chan->device->device_terminate_all(chan); -+ -+ return -EINVAL; -+} -+ -+/** -+ * dmaengine_synchronize() - Synchronize DMA channel termination -+ * @chan: The channel to synchronize -+ * -+ * Synchronizes to the DMA channel termination to the current context. When this -+ * function returns it is guaranteed that all transfers for previously issued -+ * descriptors have stopped and and it is safe to free the memory assoicated -+ * with them. Furthermore it is guaranteed that all complete callback functions -+ * for a previously submitted descriptor have finished running and it is safe to -+ * free resources accessed from within the complete callbacks. -+ * -+ * The behavior of this function is undefined if dma_async_issue_pending() has -+ * been called between dmaengine_terminate_async() and this function. -+ * -+ * This function must only be called from non-atomic context and must not be -+ * called from within a complete callback of a descriptor submitted on the same -+ * channel. -+ */ -+static inline void dmaengine_synchronize(struct dma_chan *chan) -+{ -+ if (chan->device->device_synchronize) -+ chan->device->device_synchronize(chan); -+} -+ -+/** -+ * dmaengine_terminate_sync() - Terminate all active DMA transfers -+ * @chan: The channel for which to terminate the transfers -+ * -+ * Calling this function will terminate all active and pending transfers -+ * that have previously been submitted to the channel. It is similar to -+ * dmaengine_terminate_async() but guarantees that the DMA transfer has actually -+ * stopped and that all complete callbacks have finished running when the -+ * function returns. -+ * -+ * This function must only be called from non-atomic context and must not be -+ * called from within a complete callback of a descriptor submitted on the same -+ * channel. -+ */ -+static inline int dmaengine_terminate_sync(struct dma_chan *chan) -+{ -+ int ret; -+ -+ ret = dmaengine_terminate_async(chan); -+ if (ret) -+ return ret; -+ -+ dmaengine_synchronize(chan); -+ -+ return 0; -+} -+ - static inline int dmaengine_pause(struct dma_chan *chan) - { - if (chan->device->device_pause) diff --git a/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch b/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch deleted file mode 100644 index 0380088547..0000000000 --- a/target/linux/apm821xx/patches-4.4/015-dmaengine-dw-fixed.patch +++ /dev/null @@ -1,1517 +0,0 @@ -From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> -Subject: [PATCH v6 0/4] Fixes / cleanups in dw_dmac (affects on few subsystems) -Date: Mon, 25 Apr 2016 15:35:05 +0300 - -This patch series (v3: http://www.spinics.net/lists/kernel/msg2215303.html) -contains a number of mostly minor fixes and cleanups for the DW DMA driver. A -couple of them affect the DT binding so these may need to be updated to -maintain compatibility (old format is still supported though). The rest should -be relatively straight-forward. - -This version has been tested on the following bare metal platforms: -- ATNGW100 (avr32 based platform) with dmatest -- Sam460ex (powerpc 44x based platform) with SATA -- Intel Braswell with UART -- Intel Galileo (Intel Quark based platform) with UART - -(SATA driver and Intel Galileo UART support are based on this series and just - published recently for a review) - -Vinod, there are few patch sets developed on top of this one, so, the idea is -to keep this in an immuutable branch / tag. - -Changes since v5: -- fixed an issue found by kbuildbot - -Changes since v4: -- send proper set of patches -- add changelog - -Changes since v3: -- add patch 1 to check value of dma-masters property -- drop the upstreamed patches -- update patch 2 to keep an array for data-width property as well - -Changes since v2: -- add patch 1 to fix master selection which was broken for long time -- remove "use field-by-field initialization" patch since like Mans metioned in - has mostly no value and even might increase error prone -- rebase on top of recent linux-next -- wide testing on several platforms - -Changes since v1: -- zeroing struct dw_dma_slave before use -- fall back to old data_width property if data-width is not found -- append tags for few patches -- correct title of cover letter -- rebase on top of recent linux-next - -Andy Shevchenko (4): - dmaengine: dw: platform: check nr_masters to be non-zero - dmaengine: dw: revisit data_width property - dmaengine: dw: keep entire platform data in struct dw_dma - dmaengine: dw: pass platform data via struct dw_dma_chip - - Documentation/devicetree/bindings/dma/snps-dma.txt | 6 +- - arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +- - arch/arm/boot/dts/spear13xx.dtsi | 4 +- - drivers/ata/sata_dwc_460ex.c | 2 +- - drivers/dma/dw/core.c | 75 ++++++++-------------- - drivers/dma/dw/pci.c | 5 +- - drivers/dma/dw/platform.c | 32 +++++---- - drivers/dma/dw/regs.h | 5 +- - include/linux/dma/dw.h | 5 +- - include/linux/platform_data/dma-dw.h | 4 +- - sound/soc/intel/common/sst-firmware.c | 2 +- - 11 files changed, 64 insertions(+), 78 deletions(-) - ---- a/drivers/dma/dw/core.c -+++ b/drivers/dma/dw/core.c -@@ -45,22 +45,19 @@ - DW_DMA_MSIZE_16; \ - u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ - DW_DMA_MSIZE_16; \ -+ u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \ -+ _dwc->p_master : _dwc->m_master; \ -+ u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \ -+ _dwc->p_master : _dwc->m_master; \ - \ - (DWC_CTLL_DST_MSIZE(_dmsize) \ - | DWC_CTLL_SRC_MSIZE(_smsize) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN \ -- | DWC_CTLL_DMS(_dwc->dst_master) \ -- | DWC_CTLL_SMS(_dwc->src_master)); \ -+ | DWC_CTLL_DMS(_dms) \ -+ | DWC_CTLL_SMS(_sms)); \ - }) - --/* -- * Number of descriptors to allocate for each channel. This should be -- * made configurable somehow; preferably, the clients (at least the -- * ones using slave transfers) should be able to give us a hint. -- */ --#define NR_DESCS_PER_CHANNEL 64 -- - /* The set of bus widths supported by the DMA controller */ - #define DW_DMA_BUSWIDTHS \ - BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ -@@ -80,51 +77,65 @@ static struct dw_desc *dwc_first_active( - return to_dw_desc(dwc->active_list.next); - } - --static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) -+static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) - { -- struct dw_desc *desc, *_desc; -- struct dw_desc *ret = NULL; -- unsigned int i = 0; -- unsigned long flags; -+ struct dw_desc *desc = txd_to_dw_desc(tx); -+ struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); -+ dma_cookie_t cookie; -+ unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); -- list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { -- i++; -- if (async_tx_test_ack(&desc->txd)) { -- list_del(&desc->desc_node); -- ret = desc; -- break; -- } -- dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); -- } -+ cookie = dma_cookie_assign(tx); -+ -+ /* -+ * REVISIT: We should attempt to chain as many descriptors as -+ * possible, perhaps even appending to those already submitted -+ * for DMA. But this is hard to do in a race-free manner. -+ */ -+ -+ list_add_tail(&desc->desc_node, &dwc->queue); - spin_unlock_irqrestore(&dwc->lock, flags); -+ dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", -+ __func__, desc->txd.cookie); - -- dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); -+ return cookie; -+} - -- return ret; -+static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) -+{ -+ struct dw_dma *dw = to_dw_dma(dwc->chan.device); -+ struct dw_desc *desc; -+ dma_addr_t phys; -+ -+ desc = dma_pool_zalloc(dw->desc_pool, GFP_ATOMIC, &phys); -+ if (!desc) -+ return NULL; -+ -+ dwc->descs_allocated++; -+ INIT_LIST_HEAD(&desc->tx_list); -+ dma_async_tx_descriptor_init(&desc->txd, &dwc->chan); -+ desc->txd.tx_submit = dwc_tx_submit; -+ desc->txd.flags = DMA_CTRL_ACK; -+ desc->txd.phys = phys; -+ return desc; - } - --/* -- * Move a descriptor, including any children, to the free list. -- * `desc' must not be on any lists. -- */ - static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) - { -- unsigned long flags; -+ struct dw_dma *dw = to_dw_dma(dwc->chan.device); -+ struct dw_desc *child, *_next; - -- if (desc) { -- struct dw_desc *child; -+ if (unlikely(!desc)) -+ return; - -- spin_lock_irqsave(&dwc->lock, flags); -- list_for_each_entry(child, &desc->tx_list, desc_node) -- dev_vdbg(chan2dev(&dwc->chan), -- "moving child desc %p to freelist\n", -- child); -- list_splice_init(&desc->tx_list, &dwc->free_list); -- dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); -- list_add(&desc->desc_node, &dwc->free_list); -- spin_unlock_irqrestore(&dwc->lock, flags); -+ list_for_each_entry_safe(child, _next, &desc->tx_list, desc_node) { -+ list_del(&child->desc_node); -+ dma_pool_free(dw->desc_pool, child, child->txd.phys); -+ dwc->descs_allocated--; - } -+ -+ dma_pool_free(dw->desc_pool, desc, desc->txd.phys); -+ dwc->descs_allocated--; - } - - static void dwc_initialize(struct dw_dma_chan *dwc) -@@ -133,7 +144,7 @@ static void dwc_initialize(struct dw_dma - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - -- if (dwc->initialized == true) -+ if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) - return; - - cfghi |= DWC_CFGH_DST_PER(dwc->dst_id); -@@ -146,26 +157,11 @@ static void dwc_initialize(struct dw_dma - channel_set_bit(dw, MASK.XFER, dwc->mask); - channel_set_bit(dw, MASK.ERROR, dwc->mask); - -- dwc->initialized = true; -+ set_bit(DW_DMA_IS_INITIALIZED, &dwc->flags); - } - - /*----------------------------------------------------------------------*/ - --static inline unsigned int dwc_fast_ffs(unsigned long long v) --{ -- /* -- * We can be a lot more clever here, but this should take care -- * of the most common optimization. -- */ -- if (!(v & 7)) -- return 3; -- else if (!(v & 3)) -- return 2; -- else if (!(v & 1)) -- return 1; -- return 0; --} -- - static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) - { - dev_err(chan2dev(&dwc->chan), -@@ -197,12 +193,12 @@ static inline void dwc_do_single_block(s - * Software emulation of LLP mode relies on interrupts to continue - * multi block transfer. - */ -- ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; -+ ctllo = lli_read(desc, ctllo) | DWC_CTLL_INT_EN; - -- channel_writel(dwc, SAR, desc->lli.sar); -- channel_writel(dwc, DAR, desc->lli.dar); -+ channel_writel(dwc, SAR, lli_read(desc, sar)); -+ channel_writel(dwc, DAR, lli_read(desc, dar)); - channel_writel(dwc, CTL_LO, ctllo); -- channel_writel(dwc, CTL_HI, desc->lli.ctlhi); -+ channel_writel(dwc, CTL_HI, lli_read(desc, ctlhi)); - channel_set_bit(dw, CH_EN, dwc->mask); - - /* Move pointer to next descriptor */ -@@ -213,6 +209,7 @@ static inline void dwc_do_single_block(s - static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) - { - struct dw_dma *dw = to_dw_dma(dwc->chan.device); -+ u8 lms = DWC_LLP_LMS(dwc->m_master); - unsigned long was_soft_llp; - - /* ASSERT: channel is idle */ -@@ -237,7 +234,7 @@ static void dwc_dostart(struct dw_dma_ch - - dwc_initialize(dwc); - -- dwc->residue = first->total_len; -+ first->residue = first->total_len; - dwc->tx_node_active = &first->tx_list; - - /* Submit first block */ -@@ -248,9 +245,8 @@ static void dwc_dostart(struct dw_dma_ch - - dwc_initialize(dwc); - -- channel_writel(dwc, LLP, first->txd.phys); -- channel_writel(dwc, CTL_LO, -- DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); -+ channel_writel(dwc, LLP, first->txd.phys | lms); -+ channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - channel_writel(dwc, CTL_HI, 0); - channel_set_bit(dw, CH_EN, dwc->mask); - } -@@ -293,11 +289,7 @@ dwc_descriptor_complete(struct dw_dma_ch - list_for_each_entry(child, &desc->tx_list, desc_node) - async_tx_ack(&child->txd); - async_tx_ack(&desc->txd); -- -- list_splice_init(&desc->tx_list, &dwc->free_list); -- list_move(&desc->desc_node, &dwc->free_list); -- -- dma_descriptor_unmap(txd); -+ dwc_desc_put(dwc, desc); - spin_unlock_irqrestore(&dwc->lock, flags); - - if (callback) -@@ -368,11 +360,11 @@ static void dwc_scan_descriptors(struct - - head = &desc->tx_list; - if (active != head) { -- /* Update desc to reflect last sent one */ -- if (active != head->next) -- desc = to_dw_desc(active->prev); -- -- dwc->residue -= desc->len; -+ /* Update residue to reflect last sent descriptor */ -+ if (active == head->next) -+ desc->residue -= desc->len; -+ else -+ desc->residue -= to_dw_desc(active->prev)->len; - - child = to_dw_desc(active); - -@@ -387,8 +379,6 @@ static void dwc_scan_descriptors(struct - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - -- dwc->residue = 0; -- - spin_unlock_irqrestore(&dwc->lock, flags); - - dwc_complete_all(dw, dwc); -@@ -396,7 +386,6 @@ static void dwc_scan_descriptors(struct - } - - if (list_empty(&dwc->active_list)) { -- dwc->residue = 0; - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } -@@ -411,31 +400,31 @@ static void dwc_scan_descriptors(struct - - list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { - /* Initial residue value */ -- dwc->residue = desc->total_len; -+ desc->residue = desc->total_len; - - /* Check first descriptors addr */ -- if (desc->txd.phys == llp) { -+ if (desc->txd.phys == DWC_LLP_LOC(llp)) { - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - /* Check first descriptors llp */ -- if (desc->lli.llp == llp) { -+ if (lli_read(desc, llp) == llp) { - /* This one is currently in progress */ -- dwc->residue -= dwc_get_sent(dwc); -+ desc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - -- dwc->residue -= desc->len; -+ desc->residue -= desc->len; - list_for_each_entry(child, &desc->tx_list, desc_node) { -- if (child->lli.llp == llp) { -+ if (lli_read(child, llp) == llp) { - /* Currently in progress */ -- dwc->residue -= dwc_get_sent(dwc); -+ desc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } -- dwc->residue -= child->len; -+ desc->residue -= child->len; - } - - /* -@@ -457,10 +446,14 @@ static void dwc_scan_descriptors(struct - spin_unlock_irqrestore(&dwc->lock, flags); - } - --static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) -+static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_desc *desc) - { - dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", -- lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); -+ lli_read(desc, sar), -+ lli_read(desc, dar), -+ lli_read(desc, llp), -+ lli_read(desc, ctlhi), -+ lli_read(desc, ctllo)); - } - - static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) -@@ -496,9 +489,9 @@ static void dwc_handle_error(struct dw_d - */ - dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" - " cookie: %d\n", bad_desc->txd.cookie); -- dwc_dump_lli(dwc, &bad_desc->lli); -+ dwc_dump_lli(dwc, bad_desc); - list_for_each_entry(child, &bad_desc->tx_list, desc_node) -- dwc_dump_lli(dwc, &child->lli); -+ dwc_dump_lli(dwc, child); - - spin_unlock_irqrestore(&dwc->lock, flags); - -@@ -549,7 +542,7 @@ static void dwc_handle_cyclic(struct dw_ - */ - if (unlikely(status_err & dwc->mask) || - unlikely(status_xfer & dwc->mask)) { -- int i; -+ unsigned int i; - - dev_err(chan2dev(&dwc->chan), - "cyclic DMA unexpected %s interrupt, stopping DMA transfer\n", -@@ -571,7 +564,7 @@ static void dwc_handle_cyclic(struct dw_ - dma_writel(dw, CLEAR.XFER, dwc->mask); - - for (i = 0; i < dwc->cdesc->periods; i++) -- dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); -+ dwc_dump_lli(dwc, dwc->cdesc->desc[i]); - - spin_unlock_irqrestore(&dwc->lock, flags); - } -@@ -589,7 +582,7 @@ static void dw_dma_tasklet(unsigned long - u32 status_block; - u32 status_xfer; - u32 status_err; -- int i; -+ unsigned int i; - - status_block = dma_readl(dw, RAW.BLOCK); - status_xfer = dma_readl(dw, RAW.XFER); -@@ -616,12 +609,17 @@ static void dw_dma_tasklet(unsigned long - static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) - { - struct dw_dma *dw = dev_id; -- u32 status = dma_readl(dw, STATUS_INT); -+ u32 status; -+ -+ /* Check if we have any interrupt from the DMAC which is not in use */ -+ if (!dw->in_use) -+ return IRQ_NONE; - -+ status = dma_readl(dw, STATUS_INT); - dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, status); - - /* Check if we have any interrupt from the DMAC */ -- if (!status || !dw->in_use) -+ if (!status) - return IRQ_NONE; - - /* -@@ -653,30 +651,6 @@ static irqreturn_t dw_dma_interrupt(int - - /*----------------------------------------------------------------------*/ - --static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) --{ -- struct dw_desc *desc = txd_to_dw_desc(tx); -- struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); -- dma_cookie_t cookie; -- unsigned long flags; -- -- spin_lock_irqsave(&dwc->lock, flags); -- cookie = dma_cookie_assign(tx); -- -- /* -- * REVISIT: We should attempt to chain as many descriptors as -- * possible, perhaps even appending to those already submitted -- * for DMA. But this is hard to do in a race-free manner. -- */ -- -- dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, desc->txd.cookie); -- list_add_tail(&desc->desc_node, &dwc->queue); -- -- spin_unlock_irqrestore(&dwc->lock, flags); -- -- return cookie; --} -- - static struct dma_async_tx_descriptor * - dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, - size_t len, unsigned long flags) -@@ -688,10 +662,12 @@ dwc_prep_dma_memcpy(struct dma_chan *cha - struct dw_desc *prev; - size_t xfer_count; - size_t offset; -+ u8 m_master = dwc->m_master; - unsigned int src_width; - unsigned int dst_width; -- unsigned int data_width; -+ unsigned int data_width = dw->pdata->data_width[m_master]; - u32 ctllo; -+ u8 lms = DWC_LLP_LMS(m_master); - - dev_vdbg(chan2dev(chan), - "%s: d%pad s%pad l0x%zx f0x%lx\n", __func__, -@@ -704,11 +680,7 @@ dwc_prep_dma_memcpy(struct dma_chan *cha - - dwc->direction = DMA_MEM_TO_MEM; - -- data_width = min_t(unsigned int, dw->data_width[dwc->src_master], -- dw->data_width[dwc->dst_master]); -- -- src_width = dst_width = min_t(unsigned int, data_width, -- dwc_fast_ffs(src | dest | len)); -+ src_width = dst_width = __ffs(data_width | src | dest | len); - - ctllo = DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(dst_width) -@@ -726,27 +698,27 @@ dwc_prep_dma_memcpy(struct dma_chan *cha - if (!desc) - goto err_desc_get; - -- desc->lli.sar = src + offset; -- desc->lli.dar = dest + offset; -- desc->lli.ctllo = ctllo; -- desc->lli.ctlhi = xfer_count; -+ lli_write(desc, sar, src + offset); -+ lli_write(desc, dar, dest + offset); -+ lli_write(desc, ctllo, ctllo); -+ lli_write(desc, ctlhi, xfer_count); - desc->len = xfer_count << src_width; - - if (!first) { - first = desc; - } else { -- prev->lli.llp = desc->txd.phys; -- list_add_tail(&desc->desc_node, -- &first->tx_list); -+ lli_write(prev, llp, desc->txd.phys | lms); -+ list_add_tail(&desc->desc_node, &first->tx_list); - } - prev = desc; - } - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ -- prev->lli.ctllo |= DWC_CTLL_INT_EN; -+ lli_set(prev, ctllo, DWC_CTLL_INT_EN); - - prev->lli.llp = 0; -+ lli_clear(prev, ctllo, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - first->txd.flags = flags; - first->total_len = len; - -@@ -768,10 +740,12 @@ dwc_prep_slave_sg(struct dma_chan *chan, - struct dw_desc *prev; - struct dw_desc *first; - u32 ctllo; -+ u8 m_master = dwc->m_master; -+ u8 lms = DWC_LLP_LMS(m_master); - dma_addr_t reg; - unsigned int reg_width; - unsigned int mem_width; -- unsigned int data_width; -+ unsigned int data_width = dw->pdata->data_width[m_master]; - unsigned int i; - struct scatterlist *sg; - size_t total_len = 0; -@@ -797,8 +771,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P); - -- data_width = dw->data_width[dwc->src_master]; -- - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; -@@ -806,17 +778,16 @@ dwc_prep_slave_sg(struct dma_chan *chan, - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - -- mem_width = min_t(unsigned int, -- data_width, dwc_fast_ffs(mem | len)); -+ mem_width = __ffs(data_width | mem | len); - - slave_sg_todev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) - goto err_desc_get; - -- desc->lli.sar = mem; -- desc->lli.dar = reg; -- desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); -+ lli_write(desc, sar, mem); -+ lli_write(desc, dar, reg); -+ lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width)); - if ((len >> mem_width) > dwc->block_size) { - dlen = dwc->block_size << mem_width; - mem += dlen; -@@ -826,15 +797,14 @@ slave_sg_todev_fill_desc: - len = 0; - } - -- desc->lli.ctlhi = dlen >> mem_width; -+ lli_write(desc, ctlhi, dlen >> mem_width); - desc->len = dlen; - - if (!first) { - first = desc; - } else { -- prev->lli.llp = desc->txd.phys; -- list_add_tail(&desc->desc_node, -- &first->tx_list); -+ lli_write(prev, llp, desc->txd.phys | lms); -+ list_add_tail(&desc->desc_node, &first->tx_list); - } - prev = desc; - total_len += dlen; -@@ -854,8 +824,6 @@ slave_sg_todev_fill_desc: - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M); - -- data_width = dw->data_width[dwc->dst_master]; -- - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; -@@ -863,17 +831,16 @@ slave_sg_todev_fill_desc: - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - -- mem_width = min_t(unsigned int, -- data_width, dwc_fast_ffs(mem | len)); -+ mem_width = __ffs(data_width | mem | len); - - slave_sg_fromdev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) - goto err_desc_get; - -- desc->lli.sar = reg; -- desc->lli.dar = mem; -- desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); -+ lli_write(desc, sar, reg); -+ lli_write(desc, dar, mem); -+ lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width)); - if ((len >> reg_width) > dwc->block_size) { - dlen = dwc->block_size << reg_width; - mem += dlen; -@@ -882,15 +849,14 @@ slave_sg_fromdev_fill_desc: - dlen = len; - len = 0; - } -- desc->lli.ctlhi = dlen >> reg_width; -+ lli_write(desc, ctlhi, dlen >> reg_width); - desc->len = dlen; - - if (!first) { - first = desc; - } else { -- prev->lli.llp = desc->txd.phys; -- list_add_tail(&desc->desc_node, -- &first->tx_list); -+ lli_write(prev, llp, desc->txd.phys | lms); -+ list_add_tail(&desc->desc_node, &first->tx_list); - } - prev = desc; - total_len += dlen; -@@ -905,9 +871,10 @@ slave_sg_fromdev_fill_desc: - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ -- prev->lli.ctllo |= DWC_CTLL_INT_EN; -+ lli_set(prev, ctllo, DWC_CTLL_INT_EN); - - prev->lli.llp = 0; -+ lli_clear(prev, ctllo, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - first->total_len = total_len; - - return &first->txd; -@@ -932,8 +899,8 @@ bool dw_dma_filter(struct dma_chan *chan - dwc->src_id = dws->src_id; - dwc->dst_id = dws->dst_id; - -- dwc->src_master = dws->src_master; -- dwc->dst_master = dws->dst_master; -+ dwc->m_master = dws->m_master; -+ dwc->p_master = dws->p_master; - - return true; - } -@@ -986,7 +953,7 @@ static int dwc_pause(struct dma_chan *ch - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) - udelay(2); - -- dwc->paused = true; -+ set_bit(DW_DMA_IS_PAUSED, &dwc->flags); - - spin_unlock_irqrestore(&dwc->lock, flags); - -@@ -999,7 +966,7 @@ static inline void dwc_chan_resume(struc - - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - -- dwc->paused = false; -+ clear_bit(DW_DMA_IS_PAUSED, &dwc->flags); - } - - static int dwc_resume(struct dma_chan *chan) -@@ -1007,12 +974,10 @@ static int dwc_resume(struct dma_chan *c - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - unsigned long flags; - -- if (!dwc->paused) -- return 0; -- - spin_lock_irqsave(&dwc->lock, flags); - -- dwc_chan_resume(dwc); -+ if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags)) -+ dwc_chan_resume(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - -@@ -1048,16 +1013,37 @@ static int dwc_terminate_all(struct dma_ - return 0; - } - --static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) -+static struct dw_desc *dwc_find_desc(struct dw_dma_chan *dwc, dma_cookie_t c) -+{ -+ struct dw_desc *desc; -+ -+ list_for_each_entry(desc, &dwc->active_list, desc_node) -+ if (desc->txd.cookie == c) -+ return desc; -+ -+ return NULL; -+} -+ -+static u32 dwc_get_residue(struct dw_dma_chan *dwc, dma_cookie_t cookie) - { -+ struct dw_desc *desc; - unsigned long flags; - u32 residue; - - spin_lock_irqsave(&dwc->lock, flags); - -- residue = dwc->residue; -- if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) -- residue -= dwc_get_sent(dwc); -+ desc = dwc_find_desc(dwc, cookie); -+ if (desc) { -+ if (desc == dwc_first_active(dwc)) { -+ residue = desc->residue; -+ if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) -+ residue -= dwc_get_sent(dwc); -+ } else { -+ residue = desc->total_len; -+ } -+ } else { -+ residue = 0; -+ } - - spin_unlock_irqrestore(&dwc->lock, flags); - return residue; -@@ -1078,10 +1064,12 @@ dwc_tx_status(struct dma_chan *chan, - dwc_scan_descriptors(to_dw_dma(chan->device), dwc); - - ret = dma_cookie_status(chan, cookie, txstate); -- if (ret != DMA_COMPLETE) -- dma_set_residue(txstate, dwc_get_residue(dwc)); -+ if (ret == DMA_COMPLETE) -+ return ret; -+ -+ dma_set_residue(txstate, dwc_get_residue(dwc, cookie)); - -- if (dwc->paused && ret == DMA_IN_PROGRESS) -+ if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags) && ret == DMA_IN_PROGRESS) - return DMA_PAUSED; - - return ret; -@@ -1102,7 +1090,7 @@ static void dwc_issue_pending(struct dma - - static void dw_dma_off(struct dw_dma *dw) - { -- int i; -+ unsigned int i; - - dma_writel(dw, CFG, 0); - -@@ -1116,7 +1104,7 @@ static void dw_dma_off(struct dw_dma *dw - cpu_relax(); - - for (i = 0; i < dw->dma.chancnt; i++) -- dw->chan[i].initialized = false; -+ clear_bit(DW_DMA_IS_INITIALIZED, &dw->chan[i].flags); - } - - static void dw_dma_on(struct dw_dma *dw) -@@ -1128,9 +1116,6 @@ static int dwc_alloc_chan_resources(stru - { - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); -- struct dw_desc *desc; -- int i; -- unsigned long flags; - - dev_vdbg(chan2dev(chan), "%s\n", __func__); - -@@ -1161,48 +1146,13 @@ static int dwc_alloc_chan_resources(stru - dw_dma_on(dw); - dw->in_use |= dwc->mask; - -- spin_lock_irqsave(&dwc->lock, flags); -- i = dwc->descs_allocated; -- while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { -- dma_addr_t phys; -- -- spin_unlock_irqrestore(&dwc->lock, flags); -- -- desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); -- if (!desc) -- goto err_desc_alloc; -- -- memset(desc, 0, sizeof(struct dw_desc)); -- -- INIT_LIST_HEAD(&desc->tx_list); -- dma_async_tx_descriptor_init(&desc->txd, chan); -- desc->txd.tx_submit = dwc_tx_submit; -- desc->txd.flags = DMA_CTRL_ACK; -- desc->txd.phys = phys; -- -- dwc_desc_put(dwc, desc); -- -- spin_lock_irqsave(&dwc->lock, flags); -- i = ++dwc->descs_allocated; -- } -- -- spin_unlock_irqrestore(&dwc->lock, flags); -- -- dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); -- -- return i; -- --err_desc_alloc: -- dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); -- -- return i; -+ return 0; - } - - static void dwc_free_chan_resources(struct dma_chan *chan) - { - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); -- struct dw_desc *desc, *_desc; - unsigned long flags; - LIST_HEAD(list); - -@@ -1215,17 +1165,15 @@ static void dwc_free_chan_resources(stru - BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); - - spin_lock_irqsave(&dwc->lock, flags); -- list_splice_init(&dwc->free_list, &list); -- dwc->descs_allocated = 0; - - /* Clear custom channel configuration */ - dwc->src_id = 0; - dwc->dst_id = 0; - -- dwc->src_master = 0; -- dwc->dst_master = 0; -+ dwc->m_master = 0; -+ dwc->p_master = 0; - -- dwc->initialized = false; -+ clear_bit(DW_DMA_IS_INITIALIZED, &dwc->flags); - - /* Disable interrupts */ - channel_clear_bit(dw, MASK.XFER, dwc->mask); -@@ -1239,11 +1187,6 @@ static void dwc_free_chan_resources(stru - if (!dw->in_use) - dw_dma_off(dw); - -- list_for_each_entry_safe(desc, _desc, &list, desc_node) { -- dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); -- dma_pool_free(dw->desc_pool, desc, desc->txd.phys); -- } -- - dev_vdbg(chan2dev(chan), "%s: done\n", __func__); - } - -@@ -1321,6 +1264,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre - struct dw_cyclic_desc *retval = NULL; - struct dw_desc *desc; - struct dw_desc *last = NULL; -+ u8 lms = DWC_LLP_LMS(dwc->m_master); - unsigned long was_cyclic; - unsigned int reg_width; - unsigned int periods; -@@ -1374,9 +1318,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre - - retval = ERR_PTR(-ENOMEM); - -- if (periods > NR_DESCS_PER_CHANNEL) -- goto out_err; -- - cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); - if (!cdesc) - goto out_err; -@@ -1392,50 +1333,50 @@ struct dw_cyclic_desc *dw_dma_cyclic_pre - - switch (direction) { - case DMA_MEM_TO_DEV: -- desc->lli.dar = sconfig->dst_addr; -- desc->lli.sar = buf_addr + (period_len * i); -- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) -- | DWC_CTLL_DST_WIDTH(reg_width) -- | DWC_CTLL_SRC_WIDTH(reg_width) -- | DWC_CTLL_DST_FIX -- | DWC_CTLL_SRC_INC -- | DWC_CTLL_INT_EN); -- -- desc->lli.ctllo |= sconfig->device_fc ? -- DWC_CTLL_FC(DW_DMA_FC_P_M2P) : -- DWC_CTLL_FC(DW_DMA_FC_D_M2P); -+ lli_write(desc, dar, sconfig->dst_addr); -+ lli_write(desc, sar, buf_addr + period_len * i); -+ lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan) -+ | DWC_CTLL_DST_WIDTH(reg_width) -+ | DWC_CTLL_SRC_WIDTH(reg_width) -+ | DWC_CTLL_DST_FIX -+ | DWC_CTLL_SRC_INC -+ | DWC_CTLL_INT_EN)); -+ -+ lli_set(desc, ctllo, sconfig->device_fc ? -+ DWC_CTLL_FC(DW_DMA_FC_P_M2P) : -+ DWC_CTLL_FC(DW_DMA_FC_D_M2P)); - - break; - case DMA_DEV_TO_MEM: -- desc->lli.dar = buf_addr + (period_len * i); -- desc->lli.sar = sconfig->src_addr; -- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) -- | DWC_CTLL_SRC_WIDTH(reg_width) -- | DWC_CTLL_DST_WIDTH(reg_width) -- | DWC_CTLL_DST_INC -- | DWC_CTLL_SRC_FIX -- | DWC_CTLL_INT_EN); -- -- desc->lli.ctllo |= sconfig->device_fc ? -- DWC_CTLL_FC(DW_DMA_FC_P_P2M) : -- DWC_CTLL_FC(DW_DMA_FC_D_P2M); -+ lli_write(desc, dar, buf_addr + period_len * i); -+ lli_write(desc, sar, sconfig->src_addr); -+ lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan) -+ | DWC_CTLL_SRC_WIDTH(reg_width) -+ | DWC_CTLL_DST_WIDTH(reg_width) -+ | DWC_CTLL_DST_INC -+ | DWC_CTLL_SRC_FIX -+ | DWC_CTLL_INT_EN)); -+ -+ lli_set(desc, ctllo, sconfig->device_fc ? -+ DWC_CTLL_FC(DW_DMA_FC_P_P2M) : -+ DWC_CTLL_FC(DW_DMA_FC_D_P2M)); - - break; - default: - break; - } - -- desc->lli.ctlhi = (period_len >> reg_width); -+ lli_write(desc, ctlhi, period_len >> reg_width); - cdesc->desc[i] = desc; - - if (last) -- last->lli.llp = desc->txd.phys; -+ lli_write(last, llp, desc->txd.phys | lms); - - last = desc; - } - - /* Let's make a cyclic list */ -- last->lli.llp = cdesc->desc[0]->txd.phys; -+ lli_write(last, llp, cdesc->desc[0]->txd.phys | lms); - - dev_dbg(chan2dev(&dwc->chan), - "cyclic prepared buf %pad len %zu period %zu periods %d\n", -@@ -1466,7 +1407,7 @@ void dw_dma_cyclic_free(struct dma_chan - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_cyclic_desc *cdesc = dwc->cdesc; -- int i; -+ unsigned int i; - unsigned long flags; - - dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); -@@ -1490,32 +1431,38 @@ void dw_dma_cyclic_free(struct dma_chan - kfree(cdesc->desc); - kfree(cdesc); - -+ dwc->cdesc = NULL; -+ - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - } - EXPORT_SYMBOL(dw_dma_cyclic_free); - - /*----------------------------------------------------------------------*/ - --int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) -+int dw_dma_probe(struct dw_dma_chip *chip) - { -+ struct dw_dma_platform_data *pdata; - struct dw_dma *dw; - bool autocfg = false; - unsigned int dw_params; -- unsigned int max_blk_size = 0; -+ unsigned int i; - int err; -- int i; - - dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); - if (!dw) - return -ENOMEM; - -+ dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL); -+ if (!dw->pdata) -+ return -ENOMEM; -+ - dw->regs = chip->regs; - chip->dw = dw; - - pm_runtime_get_sync(chip->dev); - -- if (!pdata) { -- dw_params = dma_read_byaddr(chip->regs, DW_PARAMS); -+ if (!chip->pdata) { -+ dw_params = dma_readl(dw, DW_PARAMS); - dev_dbg(chip->dev, "DW_PARAMS: 0x%08x\n", dw_params); - - autocfg = dw_params >> DW_PARAMS_EN & 1; -@@ -1524,29 +1471,31 @@ int dw_dma_probe(struct dw_dma_chip *chi - goto err_pdata; - } - -- pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); -- if (!pdata) { -- err = -ENOMEM; -- goto err_pdata; -- } -+ /* Reassign the platform data pointer */ -+ pdata = dw->pdata; - - /* Get hardware configuration parameters */ - pdata->nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 7) + 1; - pdata->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; - for (i = 0; i < pdata->nr_masters; i++) { - pdata->data_width[i] = -- (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; -+ 4 << (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3); - } -- max_blk_size = dma_readl(dw, MAX_BLK_SIZE); -+ pdata->block_size = dma_readl(dw, MAX_BLK_SIZE); - - /* Fill platform data with the default values */ - pdata->is_private = true; - pdata->is_memcpy = true; - pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; - pdata->chan_priority = CHAN_PRIORITY_ASCENDING; -- } else if (pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) { -+ } else if (chip->pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) { - err = -EINVAL; - goto err_pdata; -+ } else { -+ memcpy(dw->pdata, chip->pdata, sizeof(*dw->pdata)); -+ -+ /* Reassign the platform data pointer */ -+ pdata = dw->pdata; - } - - dw->chan = devm_kcalloc(chip->dev, pdata->nr_channels, sizeof(*dw->chan), -@@ -1556,11 +1505,6 @@ int dw_dma_probe(struct dw_dma_chip *chi - goto err_pdata; - } - -- /* Get hardware configuration parameters */ -- dw->nr_masters = pdata->nr_masters; -- for (i = 0; i < dw->nr_masters; i++) -- dw->data_width[i] = pdata->data_width[i]; -- - /* Calculate all channel mask before DMA setup */ - dw->all_chan_mask = (1 << pdata->nr_channels) - 1; - -@@ -1607,7 +1551,6 @@ int dw_dma_probe(struct dw_dma_chip *chi - - INIT_LIST_HEAD(&dwc->active_list); - INIT_LIST_HEAD(&dwc->queue); -- INIT_LIST_HEAD(&dwc->free_list); - - channel_clear_bit(dw, CH_EN, dwc->mask); - -@@ -1615,11 +1558,9 @@ int dw_dma_probe(struct dw_dma_chip *chi - - /* Hardware configuration */ - if (autocfg) { -- unsigned int dwc_params; - unsigned int r = DW_DMA_MAX_NR_CHANNELS - i - 1; -- void __iomem *addr = chip->regs + r * sizeof(u32); -- -- dwc_params = dma_read_byaddr(addr, DWC_PARAMS); -+ void __iomem *addr = &__dw_regs(dw)->DWC_PARAMS[r]; -+ unsigned int dwc_params = dma_readl_native(addr); - - dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, - dwc_params); -@@ -1630,16 +1571,15 @@ int dw_dma_probe(struct dw_dma_chip *chi - * up to 0x0a for 4095. - */ - dwc->block_size = -- (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; -+ (4 << ((pdata->block_size >> 4 * i) & 0xf)) - 1; - dwc->nollp = - (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; - } else { - dwc->block_size = pdata->block_size; - - /* Check if channel supports multi block transfer */ -- channel_writel(dwc, LLP, 0xfffffffc); -- dwc->nollp = -- (channel_readl(dwc, LLP) & 0xfffffffc) == 0; -+ channel_writel(dwc, LLP, DWC_LLP_LOC(0xffffffff)); -+ dwc->nollp = DWC_LLP_LOC(channel_readl(dwc, LLP)) == 0; - channel_writel(dwc, LLP, 0); - } - } ---- a/drivers/dma/dw/pci.c -+++ b/drivers/dma/dw/pci.c -@@ -17,8 +17,8 @@ - - static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) - { -+ const struct dw_dma_platform_data *pdata = (void *)pid->driver_data; - struct dw_dma_chip *chip; -- struct dw_dma_platform_data *pdata = (void *)pid->driver_data; - int ret; - - ret = pcim_enable_device(pdev); -@@ -49,8 +49,9 @@ static int dw_pci_probe(struct pci_dev * - chip->dev = &pdev->dev; - chip->regs = pcim_iomap_table(pdev)[0]; - chip->irq = pdev->irq; -+ chip->pdata = pdata; - -- ret = dw_dma_probe(chip, pdata); -+ ret = dw_dma_probe(chip); - if (ret) - return ret; - -@@ -108,6 +109,10 @@ static const struct pci_device_id dw_pci - - /* Haswell */ - { PCI_VDEVICE(INTEL, 0x9c60) }, -+ -+ /* Broadwell */ -+ { PCI_VDEVICE(INTEL, 0x9ce0) }, -+ - { } - }; - MODULE_DEVICE_TABLE(pci, dw_pci_id_table); ---- a/drivers/dma/dw/platform.c -+++ b/drivers/dma/dw/platform.c -@@ -42,13 +42,13 @@ static struct dma_chan *dw_dma_of_xlate( - - slave.src_id = dma_spec->args[0]; - slave.dst_id = dma_spec->args[0]; -- slave.src_master = dma_spec->args[1]; -- slave.dst_master = dma_spec->args[2]; -+ slave.m_master = dma_spec->args[1]; -+ slave.p_master = dma_spec->args[2]; - - if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS || - slave.dst_id >= DW_DMA_MAX_NR_REQUESTS || -- slave.src_master >= dw->nr_masters || -- slave.dst_master >= dw->nr_masters)) -+ slave.m_master >= dw->pdata->nr_masters || -+ slave.p_master >= dw->pdata->nr_masters)) - return NULL; - - dma_cap_zero(cap); -@@ -66,8 +66,8 @@ static bool dw_dma_acpi_filter(struct dm - .dma_dev = dma_spec->dev, - .src_id = dma_spec->slave_id, - .dst_id = dma_spec->slave_id, -- .src_master = 1, -- .dst_master = 0, -+ .m_master = 0, -+ .p_master = 1, - }; - - return dw_dma_filter(chan, &slave); -@@ -103,18 +103,28 @@ dw_dma_parse_dt(struct platform_device * - struct device_node *np = pdev->dev.of_node; - struct dw_dma_platform_data *pdata; - u32 tmp, arr[DW_DMA_MAX_NR_MASTERS]; -+ u32 nr_masters; -+ u32 nr_channels; - - if (!np) { - dev_err(&pdev->dev, "Missing DT data\n"); - return NULL; - } - -+ if (of_property_read_u32(np, "dma-masters", &nr_masters)) -+ return NULL; -+ if (nr_masters < 1 || nr_masters > DW_DMA_MAX_NR_MASTERS) -+ return NULL; -+ -+ if (of_property_read_u32(np, "dma-channels", &nr_channels)) -+ return NULL; -+ - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; - -- if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) -- return NULL; -+ pdata->nr_masters = nr_masters; -+ pdata->nr_channels = nr_channels; - - if (of_property_read_bool(np, "is_private")) - pdata->is_private = true; -@@ -128,17 +138,13 @@ dw_dma_parse_dt(struct platform_device * - if (!of_property_read_u32(np, "block_size", &tmp)) - pdata->block_size = tmp; - -- if (!of_property_read_u32(np, "dma-masters", &tmp)) { -- if (tmp > DW_DMA_MAX_NR_MASTERS) -- return NULL; -- -- pdata->nr_masters = tmp; -- } -- -- if (!of_property_read_u32_array(np, "data_width", arr, -- pdata->nr_masters)) -- for (tmp = 0; tmp < pdata->nr_masters; tmp++) -+ if (!of_property_read_u32_array(np, "data-width", arr, nr_masters)) { -+ for (tmp = 0; tmp < nr_masters; tmp++) - pdata->data_width[tmp] = arr[tmp]; -+ } else if (!of_property_read_u32_array(np, "data_width", arr, nr_masters)) { -+ for (tmp = 0; tmp < nr_masters; tmp++) -+ pdata->data_width[tmp] = BIT(arr[tmp] & 0x07); -+ } - - return pdata; - } -@@ -155,8 +161,7 @@ static int dw_probe(struct platform_devi - struct dw_dma_chip *chip; - struct device *dev = &pdev->dev; - struct resource *mem; -- const struct acpi_device_id *id; -- struct dw_dma_platform_data *pdata; -+ const struct dw_dma_platform_data *pdata; - int err; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); -@@ -179,13 +184,9 @@ static int dw_probe(struct platform_devi - pdata = dev_get_platdata(dev); - if (!pdata) - pdata = dw_dma_parse_dt(pdev); -- if (!pdata && has_acpi_companion(dev)) { -- id = acpi_match_device(dev->driver->acpi_match_table, dev); -- if (id) -- pdata = (struct dw_dma_platform_data *)id->driver_data; -- } - - chip->dev = dev; -+ chip->pdata = pdata; - - chip->clk = devm_clk_get(chip->dev, "hclk"); - if (IS_ERR(chip->clk)) -@@ -196,7 +197,7 @@ static int dw_probe(struct platform_devi - - pm_runtime_enable(&pdev->dev); - -- err = dw_dma_probe(chip, pdata); -+ err = dw_dma_probe(chip); - if (err) - goto err_dw_dma_probe; - -@@ -239,7 +240,19 @@ static void dw_shutdown(struct platform_ - { - struct dw_dma_chip *chip = platform_get_drvdata(pdev); - -+ /* -+ * We have to call dw_dma_disable() to stop any ongoing transfer. On -+ * some platforms we can't do that since DMA device is powered off. -+ * Moreover we have no possibility to check if the platform is affected -+ * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put() -+ * unconditionally. On the other hand we can't use -+ * pm_runtime_suspended() because runtime PM framework is not fully -+ * used by the driver. -+ */ -+ pm_runtime_get_sync(chip->dev); - dw_dma_disable(chip); -+ pm_runtime_put_sync_suspend(chip->dev); -+ - clk_disable_unprepare(chip->clk); - } - -@@ -252,17 +265,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_tab - #endif - - #ifdef CONFIG_ACPI --static struct dw_dma_platform_data dw_dma_acpi_pdata = { -- .nr_channels = 8, -- .is_private = true, -- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, -- .chan_priority = CHAN_PRIORITY_ASCENDING, -- .block_size = 4095, -- .nr_masters = 2, --}; -- - static const struct acpi_device_id dw_dma_acpi_id_table[] = { -- { "INTL9C60", (kernel_ulong_t)&dw_dma_acpi_pdata }, -+ { "INTL9C60", 0 }, - { } - }; - MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table); ---- a/drivers/dma/dw/regs.h -+++ b/drivers/dma/dw/regs.h -@@ -114,10 +114,6 @@ struct dw_dma_regs { - #define dma_writel_native writel - #endif - --/* To access the registers in early stage of probe */ --#define dma_read_byaddr(addr, name) \ -- dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) -- - /* Bitfields in DW_PARAMS */ - #define DW_PARAMS_NR_CHAN 8 /* number of channels */ - #define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ -@@ -143,6 +139,10 @@ enum dw_dma_msize { - DW_DMA_MSIZE_256, - }; - -+/* Bitfields in LLP */ -+#define DWC_LLP_LMS(x) ((x) & 3) /* list master select */ -+#define DWC_LLP_LOC(x) ((x) & ~3) /* next lli */ -+ - /* Bitfields in CTL_LO */ - #define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ - #define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ -@@ -150,7 +150,7 @@ enum dw_dma_msize { - #define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ - #define DWC_CTLL_DST_DEC (1<<7) - #define DWC_CTLL_DST_FIX (2<<7) --#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ -+#define DWC_CTLL_SRC_INC (0<<9) /* SAR update/not */ - #define DWC_CTLL_SRC_DEC (1<<9) - #define DWC_CTLL_SRC_FIX (2<<9) - #define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ -@@ -216,6 +216,8 @@ enum dw_dma_msize { - enum dw_dmac_flags { - DW_DMA_IS_CYCLIC = 0, - DW_DMA_IS_SOFT_LLP = 1, -+ DW_DMA_IS_PAUSED = 2, -+ DW_DMA_IS_INITIALIZED = 3, - }; - - struct dw_dma_chan { -@@ -224,8 +226,6 @@ struct dw_dma_chan { - u8 mask; - u8 priority; - enum dma_transfer_direction direction; -- bool paused; -- bool initialized; - - /* software emulation of the LLP transfers */ - struct list_head *tx_node_active; -@@ -236,8 +236,6 @@ struct dw_dma_chan { - unsigned long flags; - struct list_head active_list; - struct list_head queue; -- struct list_head free_list; -- u32 residue; - struct dw_cyclic_desc *cdesc; - - unsigned int descs_allocated; -@@ -249,8 +247,8 @@ struct dw_dma_chan { - /* custom slave configuration */ - u8 src_id; - u8 dst_id; -- u8 src_master; -- u8 dst_master; -+ u8 m_master; -+ u8 p_master; - - /* configuration passed via .device_config */ - struct dma_slave_config dma_sconfig; -@@ -283,9 +281,8 @@ struct dw_dma { - u8 all_chan_mask; - u8 in_use; - -- /* hardware configuration */ -- unsigned char nr_masters; -- unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; -+ /* platform data */ -+ struct dw_dma_platform_data *pdata; - }; - - static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) -@@ -308,32 +305,51 @@ static inline struct dw_dma *to_dw_dma(s - return container_of(ddev, struct dw_dma, dma); - } - -+#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -+typedef __be32 __dw32; -+#else -+typedef __le32 __dw32; -+#endif -+ - /* LLI == Linked List Item; a.k.a. DMA block descriptor */ - struct dw_lli { - /* values that are not changed by hardware */ -- u32 sar; -- u32 dar; -- u32 llp; /* chain to next lli */ -- u32 ctllo; -+ __dw32 sar; -+ __dw32 dar; -+ __dw32 llp; /* chain to next lli */ -+ __dw32 ctllo; - /* values that may get written back: */ -- u32 ctlhi; -+ __dw32 ctlhi; - /* sstat and dstat can snapshot peripheral register state. - * silicon config may discard either or both... - */ -- u32 sstat; -- u32 dstat; -+ __dw32 sstat; -+ __dw32 dstat; - }; - - struct dw_desc { - /* FIRST values the hardware uses */ - struct dw_lli lli; - -+#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -+#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_be32(v)) -+#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_be32(v)) -+#define lli_read(d, reg) be32_to_cpu((d)->lli.reg) -+#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_be32(v)) -+#else -+#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_le32(v)) -+#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_le32(v)) -+#define lli_read(d, reg) le32_to_cpu((d)->lli.reg) -+#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_le32(v)) -+#endif -+ - /* THEN values for driver housekeeping */ - struct list_head desc_node; - struct list_head tx_list; - struct dma_async_tx_descriptor txd; - size_t len; - size_t total_len; -+ u32 residue; - }; - - #define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) ---- a/include/linux/dma/dw.h -+++ b/include/linux/dma/dw.h -@@ -27,6 +27,7 @@ struct dw_dma; - * @regs: memory mapped I/O space - * @clk: hclk clock - * @dw: struct dw_dma that is filed by dw_dma_probe() -+ * @pdata: pointer to platform data - */ - struct dw_dma_chip { - struct device *dev; -@@ -34,10 +35,12 @@ struct dw_dma_chip { - void __iomem *regs; - struct clk *clk; - struct dw_dma *dw; -+ -+ const struct dw_dma_platform_data *pdata; - }; - - /* Export to the platform drivers */ --int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata); -+int dw_dma_probe(struct dw_dma_chip *chip); - int dw_dma_remove(struct dw_dma_chip *chip); - - /* DMA API extensions */ ---- a/include/linux/platform_data/dma-dw.h -+++ b/include/linux/platform_data/dma-dw.h -@@ -21,15 +21,15 @@ - * @dma_dev: required DMA master device - * @src_id: src request line - * @dst_id: dst request line -- * @src_master: src master for transfers on allocated channel. -- * @dst_master: dest master for transfers on allocated channel. -+ * @m_master: memory master for transfers on allocated channel -+ * @p_master: peripheral master for transfers on allocated channel - */ - struct dw_dma_slave { - struct device *dma_dev; - u8 src_id; - u8 dst_id; -- u8 src_master; -- u8 dst_master; -+ u8 m_master; -+ u8 p_master; - }; - - /** -@@ -43,7 +43,7 @@ struct dw_dma_slave { - * @block_size: Maximum block size supported by the controller - * @nr_masters: Number of AHB masters supported by the controller - * @data_width: Maximum data width supported by hardware per AHB master -- * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) -+ * (in bytes, power of 2) - */ - struct dw_dma_platform_data { - unsigned int nr_channels; -@@ -55,7 +55,7 @@ struct dw_dma_platform_data { - #define CHAN_PRIORITY_ASCENDING 0 /* chan0 highest */ - #define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */ - unsigned char chan_priority; -- unsigned short block_size; -+ unsigned int block_size; - unsigned char nr_masters; - unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; - }; diff --git a/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch b/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch deleted file mode 100644 index 1a6ce05624..0000000000 --- a/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch +++ /dev/null @@ -1,1083 +0,0 @@ -From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> -Date: Sat, 21 May 2016 22:46:32 +0200 -Subject: [PATCH v2 00/23] ata: sata_dwc_460ex: make it working again - -The last approach in the commit 8b3444852a2b ("sata_dwc_460ex: move to generic -DMA driver") to switch to generic DMA engine API wasn't tested on bare metal. -Besides that we expecting new board support coming with the same SATA IP but -with different DMA. - -This series is targetting the following things: -- a few bug fixes to the original driver -- a part to fix the DMA engine usage and in particularly dw_dmac driver -- move driver to use generic PHY and "dmas" property which leads to update in DTS - -The driver has been tested myself on Sam460ex and WD MyBookLive (apollo3g) -boards. In any case I ask Christian, MÃ¥ns, and Julian to independently test and -provide Tested-by tag or an error report. - -Series depends on previously published but not yet fully applied series [1]. - -The patches are also available via public branch [2]. - -[1] http://www.spinics.net/lists/dmaengine/msg09250.html -[2] https://bitbucket.org/andy-shev/linux/branch/topic%2Fdw%2Fsata - -Since v1: -- simplify patch 8 (David Laight) -- add Tested-by and Acked-by tags - -Andy Shevchenko (11): - ata: sata_dwc_460ex: set dma_boundary to 0x1fff - ata: sata_dwc_460ex: burst size must be in items not bytes - ata: sata_dwc_460ex: DMA is always a flow controller - ata: sata_dwc_460ex: select only core part of DMA driver - ata: sata_dwc_460ex: don't call ata_sff_qc_issue() on DMA commands - ata: sata_dwc_460ex: correct HOSTDEV{P}_FROM_*() macros - ata: sata_dwc_460ex: supply physical address of FIFO to DMA - ata: sata_dwc_460ex: switch to new dmaengine_terminate_* API - ata: sata_dwc_460ex: use devm_ioremap - ata: sata_dwc_460ex: make debug messages neat - powerpc/4xx: Device tree update for the 460ex DWC SATA - -Christian Lamparter (1): - ata: sata_dwc_460ex: fix crash on offline links without an attached - drive - -Mans Rullgard (11): - ata: sata_dwc_460ex: remove incorrect locking - ata: sata_dwc_460ex: skip dma setup for non-dma commands - ata: sata_dwc_460ex: use "dmas" DT property to find dma channel - ata: sata_dwc_460ex: add phy support - ata: sata_dwc_460ex: get rid of global data - ata: sata_dwc_460ex: remove empty libata callback - ata: sata_dwc_460ex: get rid of some pointless casts - ata: sata_dwc_460ex: get rid of incorrect cast - ata: sata_dwc_460ex: add __iomem to register base pointer - ata: sata_dwc_460ex: use readl/writel_relaxed() - ata: sata_dwc_460ex: tidy up sata_dwc_clear_dmacr() - - arch/powerpc/boot/dts/canyonlands.dts | 15 +- - drivers/ata/Kconfig | 12 +- - drivers/ata/sata_dwc_460ex.c | 552 +++++++++++++++++----------------- - 3 files changed, 305 insertions(+), 274 deletions(-) - ---- - drivers/ata/sata_dwc_460ex.c | 552 ++++++++++++++++++++++--------------------- - 1 file changed, 283 insertions(+), 269 deletions(-) - ---- a/drivers/ata/sata_dwc_460ex.c -+++ b/drivers/ata/sata_dwc_460ex.c -@@ -30,10 +30,12 @@ - #include <linux/kernel.h> - #include <linux/module.h> - #include <linux/device.h> -+#include <linux/dmaengine.h> - #include <linux/of_address.h> - #include <linux/of_irq.h> - #include <linux/of_platform.h> - #include <linux/platform_device.h> -+#include <linux/phy/phy.h> - #include <linux/libata.h> - #include <linux/slab.h> - -@@ -42,10 +44,6 @@ - #include <scsi/scsi_host.h> - #include <scsi/scsi_cmnd.h> - --/* Supported DMA engine drivers */ --#include <linux/platform_data/dma-dw.h> --#include <linux/dma/dw.h> -- - /* These two are defined in "libata.h" */ - #undef DRV_NAME - #undef DRV_VERSION -@@ -53,19 +51,14 @@ - #define DRV_NAME "sata-dwc" - #define DRV_VERSION "1.3" - --#ifndef out_le32 --#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (void __iomem *)(a)) --#endif -- --#ifndef in_le32 --#define in_le32(a) __le32_to_cpu(__raw_readl((void __iomem *)(a))) --#endif -+#define sata_dwc_writel(a, v) writel_relaxed(v, a) -+#define sata_dwc_readl(a) readl_relaxed(a) - - #ifndef NO_IRQ - #define NO_IRQ 0 - #endif - --#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length*/ -+#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length */ - - enum { - SATA_DWC_MAX_PORTS = 1, -@@ -102,7 +95,7 @@ struct sata_dwc_regs { - u32 versionr; /* Version Register */ - u32 idr; /* ID Register */ - u32 unimpl[192]; /* Unimplemented */ -- u32 dmadr[256]; /* FIFO Locations in DMA Mode */ -+ u32 dmadr[256]; /* FIFO Locations in DMA Mode */ - }; - - enum { -@@ -146,9 +139,14 @@ struct sata_dwc_device { - struct device *dev; /* generic device struct */ - struct ata_probe_ent *pe; /* ptr to probe-ent */ - struct ata_host *host; -- u8 __iomem *reg_base; -- struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ -+ struct sata_dwc_regs __iomem *sata_dwc_regs; /* DW SATA specific */ -+ u32 sactive_issued; -+ u32 sactive_queued; -+ struct phy *phy; -+ phys_addr_t dmadr; -+#ifdef CONFIG_SATA_DWC_OLD_DMA - struct dw_dma_chip *dma; -+#endif - }; - - #define SATA_DWC_QCMD_MAX 32 -@@ -159,25 +157,19 @@ struct sata_dwc_device_port { - int dma_pending[SATA_DWC_QCMD_MAX]; - - /* DMA info */ -- struct dw_dma_slave *dws; - struct dma_chan *chan; - struct dma_async_tx_descriptor *desc[SATA_DWC_QCMD_MAX]; - u32 dma_interrupt_count; - }; - - /* -- * Commonly used DWC SATA driver Macros -+ * Commonly used DWC SATA driver macros - */ --#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)\ -- (host)->private_data) --#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)\ -- (ap)->host->private_data) --#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)\ -- (ap)->private_data) --#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)\ -- (qc)->ap->host->private_data) --#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)\ -- (hsdevp)->hsdev) -+#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)(host)->private_data) -+#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)(ap)->host->private_data) -+#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)(ap)->private_data) -+#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)(qc)->ap->host->private_data) -+#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)(p)->hsdev) - - enum { - SATA_DWC_CMD_ISSUED_NOT = 0, -@@ -190,21 +182,6 @@ enum { - SATA_DWC_DMA_PENDING_RX = 2, - }; - --struct sata_dwc_host_priv { -- void __iomem *scr_addr_sstatus; -- u32 sata_dwc_sactive_issued ; -- u32 sata_dwc_sactive_queued ; --}; -- --static struct sata_dwc_host_priv host_pvt; -- --static struct dw_dma_slave sata_dwc_dma_dws = { -- .src_id = 0, -- .dst_id = 0, -- .src_master = 0, -- .dst_master = 1, --}; -- - /* - * Prototypes - */ -@@ -215,6 +192,93 @@ static void sata_dwc_dma_xfer_complete(s - static void sata_dwc_port_stop(struct ata_port *ap); - static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag); - -+#ifdef CONFIG_SATA_DWC_OLD_DMA -+ -+#include <linux/platform_data/dma-dw.h> -+#include <linux/dma/dw.h> -+ -+static struct dw_dma_slave sata_dwc_dma_dws = { -+ .src_id = 0, -+ .dst_id = 0, -+ .m_master = 1, -+ .p_master = 0, -+}; -+ -+static bool sata_dwc_dma_filter(struct dma_chan *chan, void *param) -+{ -+ struct dw_dma_slave *dws = &sata_dwc_dma_dws; -+ -+ if (dws->dma_dev != chan->device->dev) -+ return false; -+ -+ chan->private = dws; -+ return true; -+} -+ -+static int sata_dwc_dma_get_channel_old(struct sata_dwc_device_port *hsdevp) -+{ -+ struct sata_dwc_device *hsdev = hsdevp->hsdev; -+ struct dw_dma_slave *dws = &sata_dwc_dma_dws; -+ dma_cap_mask_t mask; -+ -+ dws->dma_dev = hsdev->dev; -+ -+ dma_cap_zero(mask); -+ dma_cap_set(DMA_SLAVE, mask); -+ -+ /* Acquire DMA channel */ -+ hsdevp->chan = dma_request_channel(mask, sata_dwc_dma_filter, hsdevp); -+ if (!hsdevp->chan) { -+ dev_err(hsdev->dev, "%s: dma channel unavailable\n", -+ __func__); -+ return -EAGAIN; -+ } -+ -+ return 0; -+} -+ -+static int sata_dwc_dma_init_old(struct platform_device *pdev, -+ struct sata_dwc_device *hsdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct resource *res; -+ -+ hsdev->dma = devm_kzalloc(&pdev->dev, sizeof(*hsdev->dma), GFP_KERNEL); -+ if (!hsdev->dma) -+ return -ENOMEM; -+ -+ hsdev->dma->dev = &pdev->dev; -+ -+ /* Get SATA DMA interrupt number */ -+ hsdev->dma->irq = irq_of_parse_and_map(np, 1); -+ if (hsdev->dma->irq == NO_IRQ) { -+ dev_err(&pdev->dev, "no SATA DMA irq\n"); -+ return -ENODEV; -+ } -+ -+ /* Get physical SATA DMA register base address */ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); -+ hsdev->dma->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(hsdev->dma->regs)) { -+ dev_err(&pdev->dev, -+ "ioremap failed for AHBDMA register address\n"); -+ return PTR_ERR(hsdev->dma->regs); -+ } -+ -+ /* Initialize AHB DMAC */ -+ return dw_dma_probe(hsdev->dma); -+} -+ -+static void sata_dwc_dma_exit_old(struct sata_dwc_device *hsdev) -+{ -+ if (!hsdev->dma) -+ return; -+ -+ dw_dma_remove(hsdev->dma); -+} -+ -+#endif -+ - static const char *get_prot_descript(u8 protocol) - { - switch ((enum ata_tf_protocols)protocol) { -@@ -305,21 +369,20 @@ static struct dma_async_tx_descriptor *d - struct ata_port *ap = qc->ap; - struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); - struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); -- dma_addr_t addr = (dma_addr_t)&hsdev->sata_dwc_regs->dmadr; - struct dma_slave_config sconf; - struct dma_async_tx_descriptor *desc; - - if (qc->dma_dir == DMA_DEV_TO_MEM) { -- sconf.src_addr = addr; -- sconf.device_fc = true; -+ sconf.src_addr = hsdev->dmadr; -+ sconf.device_fc = false; - } else { /* DMA_MEM_TO_DEV */ -- sconf.dst_addr = addr; -+ sconf.dst_addr = hsdev->dmadr; - sconf.device_fc = false; - } - - sconf.direction = qc->dma_dir; -- sconf.src_maxburst = AHB_DMA_BRST_DFLT; -- sconf.dst_maxburst = AHB_DMA_BRST_DFLT; -+ sconf.src_maxburst = AHB_DMA_BRST_DFLT / 4; /* in items */ -+ sconf.dst_maxburst = AHB_DMA_BRST_DFLT / 4; /* in items */ - sconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - sconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - -@@ -336,8 +399,8 @@ static struct dma_async_tx_descriptor *d - desc->callback = dma_dwc_xfer_done; - desc->callback_param = hsdev; - -- dev_dbg(hsdev->dev, "%s sg: 0x%p, count: %d addr: %pad\n", -- __func__, qc->sg, qc->n_elem, &addr); -+ dev_dbg(hsdev->dev, "%s sg: 0x%p, count: %d addr: %pa\n", __func__, -+ qc->sg, qc->n_elem, &hsdev->dmadr); - - return desc; - } -@@ -350,48 +413,38 @@ static int sata_dwc_scr_read(struct ata_ - return -EINVAL; - } - -- *val = in_le32(link->ap->ioaddr.scr_addr + (scr * 4)); -- dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", -- __func__, link->ap->print_id, scr, *val); -+ *val = sata_dwc_readl(link->ap->ioaddr.scr_addr + (scr * 4)); -+ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=0x%08x\n", __func__, -+ link->ap->print_id, scr, *val); - - return 0; - } - - static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) - { -- dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", -- __func__, link->ap->print_id, scr, val); -+ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=0x%08x\n", __func__, -+ link->ap->print_id, scr, val); - if (scr > SCR_NOTIFICATION) { - dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", - __func__, scr); - return -EINVAL; - } -- out_le32(link->ap->ioaddr.scr_addr + (scr * 4), val); -+ sata_dwc_writel(link->ap->ioaddr.scr_addr + (scr * 4), val); - - return 0; - } - --static u32 core_scr_read(unsigned int scr) --{ -- return in_le32(host_pvt.scr_addr_sstatus + (scr * 4)); --} -- --static void core_scr_write(unsigned int scr, u32 val) --{ -- out_le32(host_pvt.scr_addr_sstatus + (scr * 4), val); --} -- --static void clear_serror(void) -+static void clear_serror(struct ata_port *ap) - { - u32 val; -- val = core_scr_read(SCR_ERROR); -- core_scr_write(SCR_ERROR, val); -+ sata_dwc_scr_read(&ap->link, SCR_ERROR, &val); -+ sata_dwc_scr_write(&ap->link, SCR_ERROR, val); - } - - static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) - { -- out_le32(&hsdev->sata_dwc_regs->intpr, -- in_le32(&hsdev->sata_dwc_regs->intpr)); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, -+ sata_dwc_readl(&hsdev->sata_dwc_regs->intpr)); - } - - static u32 qcmd_tag_to_mask(u8 tag) -@@ -412,7 +465,7 @@ static void sata_dwc_error_intr(struct a - - ata_ehi_clear_desc(ehi); - -- serror = core_scr_read(SCR_ERROR); -+ sata_dwc_scr_read(&ap->link, SCR_ERROR, &serror); - status = ap->ops->sff_check_status(ap); - - tag = ap->link.active_tag; -@@ -423,7 +476,7 @@ static void sata_dwc_error_intr(struct a - hsdevp->dma_pending[tag], hsdevp->cmd_issued[tag]); - - /* Clear error register and interrupt bit */ -- clear_serror(); -+ clear_serror(ap); - clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR); - - /* This is the only error happening now. TODO check for exact error */ -@@ -462,12 +515,12 @@ static irqreturn_t sata_dwc_isr(int irq, - int handled, num_processed, port = 0; - uint intpr, sactive, sactive2, tag_mask; - struct sata_dwc_device_port *hsdevp; -- host_pvt.sata_dwc_sactive_issued = 0; -+ hsdev->sactive_issued = 0; - - spin_lock_irqsave(&host->lock, flags); - - /* Read the interrupt register */ -- intpr = in_le32(&hsdev->sata_dwc_regs->intpr); -+ intpr = sata_dwc_readl(&hsdev->sata_dwc_regs->intpr); - - ap = host->ports[port]; - hsdevp = HSDEVP_FROM_AP(ap); -@@ -486,12 +539,12 @@ static irqreturn_t sata_dwc_isr(int irq, - if (intpr & SATA_DWC_INTPR_NEWFP) { - clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP); - -- tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr)); -+ tag = (u8)(sata_dwc_readl(&hsdev->sata_dwc_regs->fptagr)); - dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag); - if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND) - dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag); - -- host_pvt.sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag); -+ hsdev->sactive_issued |= qcmd_tag_to_mask(tag); - - qc = ata_qc_from_tag(ap, tag); - /* -@@ -505,11 +558,11 @@ static irqreturn_t sata_dwc_isr(int irq, - handled = 1; - goto DONE; - } -- sactive = core_scr_read(SCR_ACTIVE); -- tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; -+ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); -+ tag_mask = (hsdev->sactive_issued | sactive) ^ sactive; - - /* If no sactive issued and tag_mask is zero then this is not NCQ */ -- if (host_pvt.sata_dwc_sactive_issued == 0 && tag_mask == 0) { -+ if (hsdev->sactive_issued == 0 && tag_mask == 0) { - if (ap->link.active_tag == ATA_TAG_POISON) - tag = 0; - else -@@ -579,22 +632,19 @@ DRVSTILLBUSY: - */ - - /* process completed commands */ -- sactive = core_scr_read(SCR_ACTIVE); -- tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; -+ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); -+ tag_mask = (hsdev->sactive_issued | sactive) ^ sactive; - -- if (sactive != 0 || (host_pvt.sata_dwc_sactive_issued) > 1 || \ -- tag_mask > 1) { -+ if (sactive != 0 || hsdev->sactive_issued > 1 || tag_mask > 1) { - dev_dbg(ap->dev, - "%s NCQ:sactive=0x%08x sactive_issued=0x%08x tag_mask=0x%08x\n", -- __func__, sactive, host_pvt.sata_dwc_sactive_issued, -- tag_mask); -+ __func__, sactive, hsdev->sactive_issued, tag_mask); - } - -- if ((tag_mask | (host_pvt.sata_dwc_sactive_issued)) != \ -- (host_pvt.sata_dwc_sactive_issued)) { -+ if ((tag_mask | hsdev->sactive_issued) != hsdev->sactive_issued) { - dev_warn(ap->dev, -- "Bad tag mask? sactive=0x%08x (host_pvt.sata_dwc_sactive_issued)=0x%08x tag_mask=0x%08x\n", -- sactive, host_pvt.sata_dwc_sactive_issued, tag_mask); -+ "Bad tag mask? sactive=0x%08x sactive_issued=0x%08x tag_mask=0x%08x\n", -+ sactive, hsdev->sactive_issued, tag_mask); - } - - /* read just to clear ... not bad if currently still busy */ -@@ -656,7 +706,7 @@ STILLBUSY: - * we were processing --we read status as part of processing a completed - * command). - */ -- sactive2 = core_scr_read(SCR_ACTIVE); -+ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive2); - if (sactive2 != sactive) { - dev_dbg(ap->dev, - "More completed - sactive=0x%x sactive2=0x%x\n", -@@ -672,15 +722,14 @@ DONE: - static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag) - { - struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp); -+ u32 dmacr = sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr); - - if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) { -- out_le32(&(hsdev->sata_dwc_regs->dmacr), -- SATA_DWC_DMACR_RX_CLEAR( -- in_le32(&(hsdev->sata_dwc_regs->dmacr)))); -+ dmacr = SATA_DWC_DMACR_RX_CLEAR(dmacr); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, dmacr); - } else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) { -- out_le32(&(hsdev->sata_dwc_regs->dmacr), -- SATA_DWC_DMACR_TX_CLEAR( -- in_le32(&(hsdev->sata_dwc_regs->dmacr)))); -+ dmacr = SATA_DWC_DMACR_TX_CLEAR(dmacr); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, dmacr); - } else { - /* - * This should not happen, it indicates the driver is out of -@@ -688,10 +737,9 @@ static void sata_dwc_clear_dmacr(struct - */ - dev_err(hsdev->dev, - "%s DMA protocol RX and TX DMA not pending tag=0x%02x pending=%d dmacr: 0x%08x\n", -- __func__, tag, hsdevp->dma_pending[tag], -- in_le32(&hsdev->sata_dwc_regs->dmacr)); -- out_le32(&(hsdev->sata_dwc_regs->dmacr), -- SATA_DWC_DMACR_TXRXCH_CLEAR); -+ __func__, tag, hsdevp->dma_pending[tag], dmacr); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, -+ SATA_DWC_DMACR_TXRXCH_CLEAR); - } - } - -@@ -716,7 +764,7 @@ static void sata_dwc_dma_xfer_complete(s - __func__, qc->tag, qc->tf.command, - get_dma_dir_descript(qc->dma_dir), - get_prot_descript(qc->tf.protocol), -- in_le32(&(hsdev->sata_dwc_regs->dmacr))); -+ sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr)); - } - #endif - -@@ -725,7 +773,7 @@ static void sata_dwc_dma_xfer_complete(s - dev_err(ap->dev, - "%s DMA protocol RX and TX DMA not pending dmacr: 0x%08x\n", - __func__, -- in_le32(&(hsdev->sata_dwc_regs->dmacr))); -+ sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr)); - } - - hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE; -@@ -742,8 +790,9 @@ static int sata_dwc_qc_complete(struct a - u8 status = 0; - u32 mask = 0x0; - u8 tag = qc->tag; -+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); - struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); -- host_pvt.sata_dwc_sactive_queued = 0; -+ hsdev->sactive_queued = 0; - dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status); - - if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) -@@ -756,10 +805,8 @@ static int sata_dwc_qc_complete(struct a - - /* clear active bit */ - mask = (~(qcmd_tag_to_mask(tag))); -- host_pvt.sata_dwc_sactive_queued = (host_pvt.sata_dwc_sactive_queued) \ -- & mask; -- host_pvt.sata_dwc_sactive_issued = (host_pvt.sata_dwc_sactive_issued) \ -- & mask; -+ hsdev->sactive_queued = hsdev->sactive_queued & mask; -+ hsdev->sactive_issued = hsdev->sactive_issued & mask; - ata_qc_complete(qc); - return 0; - } -@@ -767,54 +814,62 @@ static int sata_dwc_qc_complete(struct a - static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) - { - /* Enable selective interrupts by setting the interrupt maskregister*/ -- out_le32(&hsdev->sata_dwc_regs->intmr, -- SATA_DWC_INTMR_ERRM | -- SATA_DWC_INTMR_NEWFPM | -- SATA_DWC_INTMR_PMABRTM | -- SATA_DWC_INTMR_DMATM); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->intmr, -+ SATA_DWC_INTMR_ERRM | -+ SATA_DWC_INTMR_NEWFPM | -+ SATA_DWC_INTMR_PMABRTM | -+ SATA_DWC_INTMR_DMATM); - /* - * Unmask the error bits that should trigger an error interrupt by - * setting the error mask register. - */ -- out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); - - dev_dbg(hsdev->dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", -- __func__, in_le32(&hsdev->sata_dwc_regs->intmr), -- in_le32(&hsdev->sata_dwc_regs->errmr)); -+ __func__, sata_dwc_readl(&hsdev->sata_dwc_regs->intmr), -+ sata_dwc_readl(&hsdev->sata_dwc_regs->errmr)); - } - --static bool sata_dwc_dma_filter(struct dma_chan *chan, void *param) -+static void sata_dwc_setup_port(struct ata_ioports *port, void __iomem *base) - { -- struct sata_dwc_device_port *hsdevp = param; -- struct dw_dma_slave *dws = hsdevp->dws; -+ port->cmd_addr = base + 0x00; -+ port->data_addr = base + 0x00; - -- if (dws->dma_dev != chan->device->dev) -- return false; -+ port->error_addr = base + 0x04; -+ port->feature_addr = base + 0x04; - -- chan->private = dws; -- return true; --} -+ port->nsect_addr = base + 0x08; - --static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) --{ -- port->cmd_addr = (void __iomem *)base + 0x00; -- port->data_addr = (void __iomem *)base + 0x00; -+ port->lbal_addr = base + 0x0c; -+ port->lbam_addr = base + 0x10; -+ port->lbah_addr = base + 0x14; -+ -+ port->device_addr = base + 0x18; -+ port->command_addr = base + 0x1c; -+ port->status_addr = base + 0x1c; - -- port->error_addr = (void __iomem *)base + 0x04; -- port->feature_addr = (void __iomem *)base + 0x04; -+ port->altstatus_addr = base + 0x20; -+ port->ctl_addr = base + 0x20; -+} - -- port->nsect_addr = (void __iomem *)base + 0x08; -+static int sata_dwc_dma_get_channel(struct sata_dwc_device_port *hsdevp) -+{ -+ struct sata_dwc_device *hsdev = hsdevp->hsdev; -+ struct device *dev = hsdev->dev; - -- port->lbal_addr = (void __iomem *)base + 0x0c; -- port->lbam_addr = (void __iomem *)base + 0x10; -- port->lbah_addr = (void __iomem *)base + 0x14; -+#ifdef CONFIG_SATA_DWC_OLD_DMA -+ if (!of_find_property(dev->of_node, "dmas", NULL)) -+ return sata_dwc_dma_get_channel_old(hsdevp); -+#endif - -- port->device_addr = (void __iomem *)base + 0x18; -- port->command_addr = (void __iomem *)base + 0x1c; -- port->status_addr = (void __iomem *)base + 0x1c; -+ hsdevp->chan = dma_request_chan(dev, "sata-dma"); -+ if (IS_ERR(hsdevp->chan)) { -+ dev_err(dev, "failed to allocate dma channel: %ld\n", -+ PTR_ERR(hsdevp->chan)); -+ return PTR_ERR(hsdevp->chan); -+ } - -- port->altstatus_addr = (void __iomem *)base + 0x20; -- port->ctl_addr = (void __iomem *)base + 0x20; -+ return 0; - } - - /* -@@ -829,7 +884,6 @@ static int sata_dwc_port_start(struct at - struct sata_dwc_device *hsdev; - struct sata_dwc_device_port *hsdevp = NULL; - struct device *pdev; -- dma_cap_mask_t mask; - int i; - - hsdev = HSDEV_FROM_AP(ap); -@@ -853,20 +907,13 @@ static int sata_dwc_port_start(struct at - } - hsdevp->hsdev = hsdev; - -- hsdevp->dws = &sata_dwc_dma_dws; -- hsdevp->dws->dma_dev = hsdev->dev; -- -- dma_cap_zero(mask); -- dma_cap_set(DMA_SLAVE, mask); -+ err = sata_dwc_dma_get_channel(hsdevp); -+ if (err) -+ goto CLEANUP_ALLOC; - -- /* Acquire DMA channel */ -- hsdevp->chan = dma_request_channel(mask, sata_dwc_dma_filter, hsdevp); -- if (!hsdevp->chan) { -- dev_err(hsdev->dev, "%s: dma channel unavailable\n", -- __func__); -- err = -EAGAIN; -+ err = phy_power_on(hsdev->phy); -+ if (err) - goto CLEANUP_ALLOC; -- } - - for (i = 0; i < SATA_DWC_QCMD_MAX; i++) - hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; -@@ -877,18 +924,18 @@ static int sata_dwc_port_start(struct at - if (ap->port_no == 0) { - dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n", - __func__); -- out_le32(&hsdev->sata_dwc_regs->dmacr, -- SATA_DWC_DMACR_TXRXCH_CLEAR); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, -+ SATA_DWC_DMACR_TXRXCH_CLEAR); - - dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", - __func__); -- out_le32(&hsdev->sata_dwc_regs->dbtsr, -- (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | -- SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dbtsr, -+ (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | -+ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); - } - - /* Clear any error bits before libata starts issuing commands */ -- clear_serror(); -+ clear_serror(ap); - ap->private_data = hsdevp; - dev_dbg(ap->dev, "%s: done\n", __func__); - return 0; -@@ -903,11 +950,13 @@ CLEANUP: - static void sata_dwc_port_stop(struct ata_port *ap) - { - struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); -+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); - - dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id); - -- dmaengine_terminate_all(hsdevp->chan); -+ dmaengine_terminate_sync(hsdevp->chan); - dma_release_channel(hsdevp->chan); -+ phy_power_off(hsdev->phy); - - kfree(hsdevp); - ap->private_data = NULL; -@@ -924,22 +973,20 @@ static void sata_dwc_exec_command_by_tag - struct ata_taskfile *tf, - u8 tag, u32 cmd_issued) - { -- unsigned long flags; - struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); - - dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command, - ata_get_cmd_descript(tf->command), tag); - -- spin_lock_irqsave(&ap->host->lock, flags); - hsdevp->cmd_issued[tag] = cmd_issued; -- spin_unlock_irqrestore(&ap->host->lock, flags); -+ - /* - * Clear SError before executing a new command. - * sata_dwc_scr_write and read can not be used here. Clearing the PM - * managed SError register for the disk needs to be done before the - * task file is loaded. - */ -- clear_serror(); -+ clear_serror(ap); - ata_sff_exec_command(ap, tf); - } - -@@ -992,18 +1039,18 @@ static void sata_dwc_bmdma_start_by_tag( - sata_dwc_tf_dump(ap, &qc->tf); - - if (start_dma) { -- reg = core_scr_read(SCR_ERROR); -+ sata_dwc_scr_read(&ap->link, SCR_ERROR, ®); - if (reg & SATA_DWC_SERROR_ERR_BITS) { - dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n", - __func__, reg); - } - - if (dir == DMA_TO_DEVICE) -- out_le32(&hsdev->sata_dwc_regs->dmacr, -- SATA_DWC_DMACR_TXCHEN); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, -+ SATA_DWC_DMACR_TXCHEN); - else -- out_le32(&hsdev->sata_dwc_regs->dmacr, -- SATA_DWC_DMACR_RXCHEN); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, -+ SATA_DWC_DMACR_RXCHEN); - - /* Enable AHB DMA transfer on the specified channel */ - dmaengine_submit(desc); -@@ -1025,36 +1072,12 @@ static void sata_dwc_bmdma_start(struct - sata_dwc_bmdma_start_by_tag(qc, tag); - } - --/* -- * Function : sata_dwc_qc_prep_by_tag -- * arguments : ata_queued_cmd *qc, u8 tag -- * Return value : None -- * qc_prep for a particular queued command based on tag -- */ --static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) --{ -- struct dma_async_tx_descriptor *desc; -- struct ata_port *ap = qc->ap; -- struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); -- -- dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n", -- __func__, ap->port_no, get_dma_dir_descript(qc->dma_dir), -- qc->n_elem); -- -- desc = dma_dwc_xfer_setup(qc); -- if (!desc) { -- dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns NULL\n", -- __func__); -- return; -- } -- hsdevp->desc[tag] = desc; --} -- - static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) - { - u32 sactive; - u8 tag = qc->tag; - struct ata_port *ap = qc->ap; -+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); - - #ifdef DEBUG_NCQ - if (qc->tag > 0 || ap->link.sactive > 1) -@@ -1068,47 +1091,33 @@ static unsigned int sata_dwc_qc_issue(st - - if (!ata_is_ncq(qc->tf.protocol)) - tag = 0; -- sata_dwc_qc_prep_by_tag(qc, tag); -+ -+ if (ata_is_dma(qc->tf.protocol)) { -+ hsdevp->desc[tag] = dma_dwc_xfer_setup(qc); -+ if (!hsdevp->desc[tag]) -+ return AC_ERR_SYSTEM; -+ } else { -+ hsdevp->desc[tag] = NULL; -+ } - - if (ata_is_ncq(qc->tf.protocol)) { -- sactive = core_scr_read(SCR_ACTIVE); -+ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); - sactive |= (0x00000001 << tag); -- core_scr_write(SCR_ACTIVE, sactive); -+ sata_dwc_scr_write(&ap->link, SCR_ACTIVE, sactive); - - dev_dbg(qc->ap->dev, - "%s: tag=%d ap->link.sactive = 0x%08x sactive=0x%08x\n", - __func__, tag, qc->ap->link.sactive, sactive); - - ap->ops->sff_tf_load(ap, &qc->tf); -- sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag, -+ sata_dwc_exec_command_by_tag(ap, &qc->tf, tag, - SATA_DWC_CMD_ISSUED_PEND); - } else { -- ata_sff_qc_issue(qc); -+ return ata_bmdma_qc_issue(qc); - } - return 0; - } - --/* -- * Function : sata_dwc_qc_prep -- * arguments : ata_queued_cmd *qc -- * Return value : None -- * qc_prep for a particular queued command -- */ -- --static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) --{ -- if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)) -- return; -- --#ifdef DEBUG_NCQ -- if (qc->tag > 0) -- dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n", -- __func__, qc->tag, qc->ap->link.active_tag); -- -- return ; --#endif --} -- - static void sata_dwc_error_handler(struct ata_port *ap) - { - ata_sff_error_handler(ap); -@@ -1125,17 +1134,22 @@ static int sata_dwc_hardreset(struct ata - sata_dwc_enable_interrupts(hsdev); - - /* Reconfigure the DMA control register */ -- out_le32(&hsdev->sata_dwc_regs->dmacr, -- SATA_DWC_DMACR_TXRXCH_CLEAR); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, -+ SATA_DWC_DMACR_TXRXCH_CLEAR); - - /* Reconfigure the DMA Burst Transaction Size register */ -- out_le32(&hsdev->sata_dwc_regs->dbtsr, -- SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | -- SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); -+ sata_dwc_writel(&hsdev->sata_dwc_regs->dbtsr, -+ SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | -+ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); - - return ret; - } - -+static void sata_dwc_dev_select(struct ata_port *ap, unsigned int device) -+{ -+ /* SATA DWC is master only */ -+} -+ - /* - * scsi mid-layer and libata interface structures - */ -@@ -1148,7 +1162,13 @@ static struct scsi_host_template sata_dw - */ - .sg_tablesize = LIBATA_MAX_PRD, - /* .can_queue = ATA_MAX_QUEUE, */ -- .dma_boundary = ATA_DMA_BOUNDARY, -+ /* -+ * Make sure a LLI block is not created that will span 8K max FIS -+ * boundary. If the block spans such a FIS boundary, there is a chance -+ * that a DMA burst will cross that boundary -- this results in an -+ * error in the host controller. -+ */ -+ .dma_boundary = 0x1fff /* ATA_DMA_BOUNDARY */, - }; - - static struct ata_port_operations sata_dwc_ops = { -@@ -1157,7 +1177,6 @@ static struct ata_port_operations sata_d - .error_handler = sata_dwc_error_handler, - .hardreset = sata_dwc_hardreset, - -- .qc_prep = sata_dwc_qc_prep, - .qc_issue = sata_dwc_qc_issue, - - .scr_read = sata_dwc_scr_read, -@@ -1166,6 +1185,8 @@ static struct ata_port_operations sata_d - .port_start = sata_dwc_port_start, - .port_stop = sata_dwc_port_stop, - -+ .sff_dev_select = sata_dwc_dev_select, -+ - .bmdma_setup = sata_dwc_bmdma_setup, - .bmdma_start = sata_dwc_bmdma_start, - }; -@@ -1184,13 +1205,14 @@ static int sata_dwc_probe(struct platfor - struct sata_dwc_device *hsdev; - u32 idr, versionr; - char *ver = (char *)&versionr; -- u8 __iomem *base; -+ void __iomem *base; - int err = 0; - int irq; - struct ata_host *host; - struct ata_port_info pi = sata_dwc_port_info[0]; - const struct ata_port_info *ppi[] = { &pi, NULL }; - struct device_node *np = ofdev->dev.of_node; -+ struct resource *res; - - /* Allocate DWC SATA device */ - host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); -@@ -1201,57 +1223,33 @@ static int sata_dwc_probe(struct platfor - host->private_data = hsdev; - - /* Ioremap SATA registers */ -- base = of_iomap(np, 0); -- if (!base) { -+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(&ofdev->dev, res); -+ if (IS_ERR(base)) { - dev_err(&ofdev->dev, - "ioremap failed for SATA register address\n"); -- return -ENODEV; -+ return PTR_ERR(base); - } -- hsdev->reg_base = base; - dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); - - /* Synopsys DWC SATA specific Registers */ -- hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); -+ hsdev->sata_dwc_regs = base + SATA_DWC_REG_OFFSET; -+ hsdev->dmadr = res->start + SATA_DWC_REG_OFFSET + offsetof(struct sata_dwc_regs, dmadr); - - /* Setup port */ - host->ports[0]->ioaddr.cmd_addr = base; - host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; -- host_pvt.scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET; -- sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base); -+ sata_dwc_setup_port(&host->ports[0]->ioaddr, base); - - /* Read the ID and Version Registers */ -- idr = in_le32(&hsdev->sata_dwc_regs->idr); -- versionr = in_le32(&hsdev->sata_dwc_regs->versionr); -+ idr = sata_dwc_readl(&hsdev->sata_dwc_regs->idr); -+ versionr = sata_dwc_readl(&hsdev->sata_dwc_regs->versionr); - dev_notice(&ofdev->dev, "id %d, controller version %c.%c%c\n", - idr, ver[0], ver[1], ver[2]); - -- /* Get SATA DMA interrupt number */ -- hsdev->dma->irq = irq_of_parse_and_map(np, 1); -- if (hsdev->dma->irq == NO_IRQ) { -- dev_err(&ofdev->dev, "no SATA DMA irq\n"); -- err = -ENODEV; -- goto error_iomap; -- } -- -- /* Get physical SATA DMA register base address */ -- hsdev->dma->regs = of_iomap(np, 1); -- if (!hsdev->dma->regs) { -- dev_err(&ofdev->dev, -- "ioremap failed for AHBDMA register address\n"); -- err = -ENODEV; -- goto error_iomap; -- } -- - /* Save dev for later use in dev_xxx() routines */ - hsdev->dev = &ofdev->dev; - -- hsdev->dma->dev = &ofdev->dev; -- -- /* Initialize AHB DMAC */ -- err = dw_dma_probe(hsdev->dma, NULL); -- if (err) -- goto error_dma_iomap; -- - /* Enable SATA Interrupts */ - sata_dwc_enable_interrupts(hsdev); - -@@ -1263,6 +1261,25 @@ static int sata_dwc_probe(struct platfor - goto error_out; - } - -+#ifdef CONFIG_SATA_DWC_OLD_DMA -+ if (!of_find_property(np, "dmas", NULL)) { -+ err = sata_dwc_dma_init_old(ofdev, hsdev); -+ if (err) -+ goto error_out; -+ } -+#endif -+ -+ hsdev->phy = devm_phy_optional_get(hsdev->dev, "sata-phy"); -+ if (IS_ERR(hsdev->phy)) { -+ err = PTR_ERR(hsdev->phy); -+ hsdev->phy = NULL; -+ goto error_out; -+ } -+ -+ err = phy_init(hsdev->phy); -+ if (err) -+ goto error_out; -+ - /* - * Now, register with libATA core, this will also initiate the - * device discovery process, invoking our port_start() handler & -@@ -1276,12 +1293,7 @@ static int sata_dwc_probe(struct platfor - return 0; - - error_out: -- /* Free SATA DMA resources */ -- dw_dma_remove(hsdev->dma); --error_dma_iomap: -- iounmap(hsdev->dma->regs); --error_iomap: -- iounmap(base); -+ phy_exit(hsdev->phy); - return err; - } - -@@ -1293,11 +1305,13 @@ static int sata_dwc_remove(struct platfo - - ata_host_detach(host); - -+ phy_exit(hsdev->phy); -+ -+#ifdef CONFIG_SATA_DWC_OLD_DMA - /* Free SATA DMA resources */ -- dw_dma_remove(hsdev->dma); -+ sata_dwc_dma_exit_old(hsdev); -+#endif - -- iounmap(hsdev->dma->regs); -- iounmap(hsdev->reg_base); - dev_dbg(&ofdev->dev, "done\n"); - return 0; - } diff --git a/target/linux/apm821xx/patches-4.4/040-backport_leds-convert-IDE-trigger-to-common-disk-trigger.patch b/target/linux/apm821xx/patches-4.4/040-backport_leds-convert-IDE-trigger-to-common-disk-trigger.patch deleted file mode 100644 index 48714aa466..0000000000 --- a/target/linux/apm821xx/patches-4.4/040-backport_leds-convert-IDE-trigger-to-common-disk-trigger.patch +++ /dev/null @@ -1,209 +0,0 @@ -From eb25cb9956cc9384b7fa0d75dec908c9fac8c444 Mon Sep 17 00:00:00 2001 -From: Stephan Linz <linz@li-pro.net> -Date: Fri, 10 Jun 2016 07:59:56 +0200 -Subject: [PATCH] leds: convert IDE trigger to common disk trigger -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This patch converts the IDE specific LED trigger to a generic disk -activity LED trigger. The libata core is now a trigger source just -like before the IDE disk driver. It's merely a replacement of the -string ide by disk. - -The patch is taken from http://dev.gentoo.org/~josejx/ata.patch and is -widely used by any ibook/powerbook owners with great satisfaction. -Likewise, it is very often used successfully on different ARM platforms. - -Unlike the original patch, the existing 'ide-disk' trigger is still -available for backward compatibility. That reduce the amount of patches -in affected device trees out of the mainline kernel. For further -development, the new name 'disk-activity' should be used. - -Cc: Joseph Jezak <josejx@gentoo.org> -Cc: Jörg Sommer <joerg@alea.gnuu.de> -Cc: Richard Purdie <rpurdie@rpsys.net> -Signed-off-by: Stephan Linz <linz@li-pro.net> -Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com> ---- - drivers/ata/libata-core.c | 4 ++++ - drivers/ide/ide-disk.c | 2 +- - drivers/leds/trigger/Kconfig | 8 +++---- - drivers/leds/trigger/Makefile | 2 +- - drivers/leds/trigger/ledtrig-disk.c | 41 +++++++++++++++++++++++++++++++++ - drivers/leds/trigger/ledtrig-ide-disk.c | 36 ----------------------------- - include/linux/leds.h | 6 ++--- - 8 files changed, 55 insertions(+), 46 deletions(-) - create mode 100644 drivers/leds/trigger/ledtrig-disk.c - delete mode 100644 drivers/leds/trigger/ledtrig-ide-disk.c - ---- a/drivers/ata/libata-core.c -+++ b/drivers/ata/libata-core.c -@@ -67,6 +67,7 @@ - #include <asm/byteorder.h> - #include <linux/cdrom.h> - #include <linux/ratelimit.h> -+#include <linux/leds.h> - #include <linux/pm_runtime.h> - #include <linux/platform_device.h> - -@@ -4914,6 +4915,9 @@ void ata_qc_complete(struct ata_queued_c - { - struct ata_port *ap = qc->ap; - -+ /* Trigger the LED (if available) */ -+ ledtrig_disk_activity(); -+ - /* XXX: New EH and old EH use different mechanisms to - * synchronize EH with regular execution path. - * ---- a/drivers/ide/ide-disk.c -+++ b/drivers/ide/ide-disk.c -@@ -186,7 +186,7 @@ static ide_startstop_t ide_do_rw_disk(id - BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); - BUG_ON(rq->cmd_type != REQ_TYPE_FS); - -- ledtrig_ide_activity(); -+ ledtrig_disk_activity(); - - pr_debug("%s: %sing: block=%llu, sectors=%u\n", - drive->name, rq_data_dir(rq) == READ ? "read" : "writ", ---- a/drivers/leds/trigger/Kconfig -+++ b/drivers/leds/trigger/Kconfig -@@ -33,12 +33,12 @@ config LEDS_TRIGGER_ONESHOT - - If unsure, say Y. - --config LEDS_TRIGGER_IDE_DISK -- bool "LED IDE Disk Trigger" -- depends on IDE_GD_ATA -+config LEDS_TRIGGER_DISK -+ bool "LED Disk Trigger" -+ depends on IDE_GD_ATA || ATA - depends on LEDS_TRIGGERS - help -- This allows LEDs to be controlled by IDE disk activity. -+ This allows LEDs to be controlled by disk activity. - If unsure, say Y. - - config LEDS_TRIGGER_HEARTBEAT ---- a/drivers/leds/trigger/Makefile -+++ b/drivers/leds/trigger/Makefile -@@ -1,6 +1,6 @@ - obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o - obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o --obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o -+obj-$(CONFIG_LEDS_TRIGGER_DISK) += ledtrig-disk.o - obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o - obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o - obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o ---- /dev/null -+++ b/drivers/leds/trigger/ledtrig-disk.c -@@ -0,0 +1,41 @@ -+/* -+ * LED Disk Activity Trigger -+ * -+ * Copyright 2006 Openedhand Ltd. -+ * -+ * Author: Richard Purdie <rpurdie@openedhand.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/leds.h> -+ -+#define BLINK_DELAY 30 -+ -+DEFINE_LED_TRIGGER(ledtrig_disk); -+DEFINE_LED_TRIGGER(ledtrig_ide); -+ -+void ledtrig_disk_activity(void) -+{ -+ unsigned long blink_delay = BLINK_DELAY; -+ -+ led_trigger_blink_oneshot(ledtrig_disk, -+ &blink_delay, &blink_delay, 0); -+ led_trigger_blink_oneshot(ledtrig_ide, -+ &blink_delay, &blink_delay, 0); -+} -+EXPORT_SYMBOL(ledtrig_disk_activity); -+ -+static int __init ledtrig_disk_init(void) -+{ -+ led_trigger_register_simple("disk-activity", &ledtrig_disk); -+ led_trigger_register_simple("ide-disk", &ledtrig_ide); -+ -+ return 0; -+} -+device_initcall(ledtrig_disk_init); ---- a/drivers/leds/trigger/ledtrig-ide-disk.c -+++ /dev/null -@@ -1,47 +0,0 @@ --/* -- * LED IDE-Disk Activity Trigger -- * -- * Copyright 2006 Openedhand Ltd. -- * -- * Author: Richard Purdie <rpurdie@openedhand.com> -- * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. -- * -- */ -- --#include <linux/module.h> --#include <linux/kernel.h> --#include <linux/init.h> --#include <linux/leds.h> -- --#define BLINK_DELAY 30 -- --DEFINE_LED_TRIGGER(ledtrig_ide); --static unsigned long ide_blink_delay = BLINK_DELAY; -- --void ledtrig_ide_activity(void) --{ -- led_trigger_blink_oneshot(ledtrig_ide, -- &ide_blink_delay, &ide_blink_delay, 0); --} --EXPORT_SYMBOL(ledtrig_ide_activity); -- --static int __init ledtrig_ide_init(void) --{ -- led_trigger_register_simple("ide-disk", &ledtrig_ide); -- return 0; --} -- --static void __exit ledtrig_ide_exit(void) --{ -- led_trigger_unregister_simple(ledtrig_ide); --} -- --module_init(ledtrig_ide_init); --module_exit(ledtrig_ide_exit); -- --MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); --MODULE_DESCRIPTION("LED IDE Disk Activity Trigger"); --MODULE_LICENSE("GPL"); ---- a/include/linux/leds.h -+++ b/include/linux/leds.h -@@ -308,10 +308,10 @@ static inline void *led_get_trigger_data - #endif /* CONFIG_LEDS_TRIGGERS */ - - /* Trigger specific functions */ --#ifdef CONFIG_LEDS_TRIGGER_IDE_DISK --extern void ledtrig_ide_activity(void); -+#ifdef CONFIG_LEDS_TRIGGER_DISK -+extern void ledtrig_disk_activity(void); - #else --static inline void ledtrig_ide_activity(void) {} -+static inline void ledtrig_disk_activity(void) {} - #endif - - #if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE) diff --git a/target/linux/apm821xx/patches-4.4/200-add-meraki-mr24-ikarem-support.patch b/target/linux/apm821xx/patches-4.4/200-add-meraki-mr24-ikarem-support.patch deleted file mode 100644 index 018bcf21a0..0000000000 --- a/target/linux/apm821xx/patches-4.4/200-add-meraki-mr24-ikarem-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -40,6 +40,19 @@ config EBONY - help - This option enables support for the IBM PPC440GP evaluation board. - -+config IKAREM -+ bool "Ikarem" -+ depends on 44x -+ default n -+ select PPC44x_SIMPLE -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ help -+ This option enables support for the Cisco Meraki MR24 (Ikarem) Access Point. -+ - config SAM440EP - bool "Sam440ep" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -62,6 +62,7 @@ static char *board[] __initdata = { - "amcc,sequoia", - "amcc,taishan", - "amcc,yosemite", -+ "meraki,ikarem", - "mosaixtech,icon" - }; - diff --git a/target/linux/apm821xx/patches-4.4/201-add-amcc-apollo3g-support.patch b/target/linux/apm821xx/patches-4.4/201-add-amcc-apollo3g-support.patch deleted file mode 100644 index 0581a43e33..0000000000 --- a/target/linux/apm821xx/patches-4.4/201-add-amcc-apollo3g-support.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -143,6 +143,17 @@ config CANYONLANDS - help - This option enables support for the AMCC PPC460EX evaluation board. - -+config APOLLO3G -+ bool "Apollo3G" -+ depends on 44x -+ default n -+ select PPC44x_SIMPLE -+ select APM821xx -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the AMCC Apollo 3G board. -+ - config GLACIER - bool "Glacier" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -50,6 +50,7 @@ machine_device_initcall(ppc44x_simple, p - * board.c file for it rather than adding it to this list. - */ - static char *board[] __initdata = { -+ "amcc,apollo3g", - "amcc,arches", - "amcc,bamboo", - "apm,bluestone", diff --git a/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch b/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch deleted file mode 100644 index 6b6db1dddb..0000000000 --- a/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Makefile -+++ b/arch/powerpc/platforms/44x/Makefile -@@ -3,6 +3,7 @@ ifneq ($(CONFIG_PPC4xx_CPM),y) - obj-$(CONFIG_44x) += idle.o - endif - obj-$(CONFIG_PPC44x_SIMPLE) += ppc44x_simple.o -+obj-$(CONFIG_WNDR4700) += wndr4700.o - obj-$(CONFIG_EBONY) += ebony.o - obj-$(CONFIG_SAM440EP) += sam440ep.o - obj-$(CONFIG_WARP) += warp.o ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -260,6 +260,19 @@ config ICON - help - This option enables support for the AMCC PPC440SPe evaluation board. - -+config WNDR4700 -+ bool "WNDR4700" -+ depends on 44x -+ default n -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the Netgear WNDR4700/WNDR4720 board. -+ - config XILINX_VIRTEX440_GENERIC_BOARD - bool "Generic Xilinx Virtex 5 FXT board support" - depends on 44x diff --git a/target/linux/apm821xx/patches-4.4/203-add-meraki-mx60-buckminster-support.patch b/target/linux/apm821xx/patches-4.4/203-add-meraki-mx60-buckminster-support.patch deleted file mode 100644 index 8c072387d4..0000000000 --- a/target/linux/apm821xx/patches-4.4/203-add-meraki-mx60-buckminster-support.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/arch/powerpc/platforms/44x/Kconfig -+++ b/arch/powerpc/platforms/44x/Kconfig -@@ -30,6 +30,19 @@ config BLUESTONE - help - This option enables support for the APM APM821xx Evaluation board. - -+config BUCKMINSTER -+ bool "Buckminster" -+ depends on 44x -+ default n -+ select APM821xx -+ select PCI_MSI -+ select PPC4xx_MSI -+ select PPC4xx_PCI_EXPRESS -+ select IBM_EMAC_RGMII -+ select 460EX -+ help -+ This option enables support for the Cisco Meraki MX60/MX60W (Buckminster) Security Appliance -+ - config EBONY - bool "Ebony" - depends on 44x ---- a/arch/powerpc/platforms/44x/ppc44x_simple.c -+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c -@@ -63,6 +63,7 @@ static char *board[] __initdata = { - "amcc,sequoia", - "amcc,taishan", - "amcc,yosemite", -+ "meraki,buckminster", - "meraki,ikarem", - "mosaixtech,icon" - }; diff --git a/target/linux/apm821xx/patches-4.4/300-fix-atheros-nics-on-apm82181.patch b/target/linux/apm821xx/patches-4.4/300-fix-atheros-nics-on-apm82181.patch deleted file mode 100644 index 0b1affcfe7..0000000000 --- a/target/linux/apm821xx/patches-4.4/300-fix-atheros-nics-on-apm82181.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/arch/powerpc/sysdev/ppc4xx_pci.c -+++ b/arch/powerpc/sysdev/ppc4xx_pci.c -@@ -1066,15 +1066,24 @@ static int __init apm821xx_pciex_init_po - u32 val; - - /* -- * Do a software reset on PCIe ports. -- * This code is to fix the issue that pci drivers doesn't re-assign -- * bus number for PCIE devices after Uboot -- * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 -- * PT quad port, SAS LSI 1064E) -+ * Only reset the PHY when no link is currently established. -+ * This is for the Atheros PCIe board which has problems to establish -+ * the link (again) after this PHY reset. All other currently tested -+ * PCIe boards don't show this problem. - */ -- -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); -- mdelay(10); -+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); -+ if (!(val & 0x00001000)) { -+ /* -+ * Do a software reset on PCIe ports. -+ * This code is to fix the issue that pci drivers doesn't re-assign -+ * bus number for PCIE devices after Uboot -+ * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 -+ * PT quad port, SAS LSI 1064E) -+ */ -+ -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); -+ mdelay(10); -+ } - - if (port->endpoint) - val = PTYPE_LEGACY_ENDPOINT << 20; -@@ -1091,9 +1100,12 @@ static int __init apm821xx_pciex_init_po - mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); - mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); - -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); -- mdelay(50); -- mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); -+ val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); -+ if (!(val & 0x00001000)) { -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); -+ mdelay(50); -+ mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); -+ } - - mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, - mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | diff --git a/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch deleted file mode 100644 index d6a1006177..0000000000 --- a/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/arch/powerpc/sysdev/ppc4xx_pci.c -+++ b/arch/powerpc/sysdev/ppc4xx_pci.c -@@ -1913,9 +1913,9 @@ static void __init ppc4xx_configure_pcie - * if it works - */ - out_le32(mbase + PECFG_PIM0LAL, 0x00000000); -- out_le32(mbase + PECFG_PIM0LAH, 0x00000000); -+ out_le32(mbase + PECFG_PIM0LAH, 0x00000008); - out_le32(mbase + PECFG_PIM1LAL, 0x00000000); -- out_le32(mbase + PECFG_PIM1LAH, 0x00000000); -+ out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); - out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); - out_le32(mbase + PECFG_PIM01SAL, 0x00000000); - diff --git a/target/linux/apm821xx/patches-4.4/701-powerpc_ibm_apm82181_phyclk_fix.patch b/target/linux/apm821xx/patches-4.4/701-powerpc_ibm_apm82181_phyclk_fix.patch deleted file mode 100644 index 011e785e99..0000000000 --- a/target/linux/apm821xx/patches-4.4/701-powerpc_ibm_apm82181_phyclk_fix.patch +++ /dev/null @@ -1,52 +0,0 @@ ---- a/drivers/net/ethernet/ibm/emac/core.c -+++ b/drivers/net/ethernet/ibm/emac/core.c -@@ -129,6 +129,7 @@ static inline void emac_report_timeout_e - { - if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX | - EMAC_FTR_460EX_PHY_CLK_FIX | -+ EMAC_FTR_APM821XX_PHY_CLK_FIX | - EMAC_FTR_440EP_PHY_CLK_FIX)) - DBG(dev, "%s" NL, error); - else if (net_ratelimit()) -@@ -146,6 +147,10 @@ static inline void emac_rx_clk_tx(struct - if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) - dcri_clrset(SDR0, SDR0_MFR, - 0, SDR0_MFR_ECS >> dev->cell_index); -+ -+ if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX)) -+ dcri_clrset(SDR0, SDR0_ETH_CFG, -+ 0, 0x00000100 >> dev->cell_index); - #endif - } - -@@ -155,6 +160,10 @@ static inline void emac_rx_clk_default(s - if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) - dcri_clrset(SDR0, SDR0_MFR, - SDR0_MFR_ECS >> dev->cell_index, 0); -+ -+ if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX)) -+ dcri_clrset(SDR0, SDR0_ETH_CFG, -+ 0x00000100 >> dev->cell_index, 0); - #endif - } - -@@ -2587,7 +2596,7 @@ static int emac_init_config(struct emac_ - if (of_device_is_compatible(np, "ibm,emac-apm821xx")) { - dev->features |= (EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE | - EMAC_FTR_APM821XX_NO_HALF_DUPLEX | -- EMAC_FTR_460EX_PHY_CLK_FIX); -+ EMAC_FTR_APM821XX_PHY_CLK_FIX); - } - } else if (of_device_is_compatible(np, "ibm,emac4")) { - dev->features |= EMAC_FTR_EMAC4; ---- a/drivers/net/ethernet/ibm/emac/core.h -+++ b/drivers/net/ethernet/ibm/emac/core.h -@@ -333,6 +333,8 @@ struct emac_instance { - */ - #define EMAC_FTR_APM821XX_NO_HALF_DUPLEX 0x00001000 - -+#define EMAC_FTR_APM821XX_PHY_CLK_FIX 0x000002000 -+ - /* Right now, we don't quite handle the always/possible masks on the - * most optimal way as we don't have a way to say something like - * always EMAC4. Patches welcome. diff --git a/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch b/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch deleted file mode 100644 index c84e761ed0..0000000000 --- a/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch +++ /dev/null @@ -1,328 +0,0 @@ -From 59b394d0d2b2e11687e757820c52d6470d15a9c5 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Mon, 13 Jun 2016 15:42:21 +0200 -Subject: [PATCH] phy device tree support for emac - ---- - drivers/net/ethernet/ibm/emac/core.c | 261 +++++++++++++++++++++++++++++++++++ - drivers/net/ethernet/ibm/emac/core.h | 4 + - 2 files changed, 265 insertions(+) - ---- a/drivers/net/ethernet/ibm/emac/core.c -+++ b/drivers/net/ethernet/ibm/emac/core.c -@@ -42,6 +42,7 @@ - #include <linux/of_address.h> - #include <linux/of_irq.h> - #include <linux/of_net.h> -+#include <linux/of_mdio.h> - #include <linux/slab.h> - - #include <asm/processor.h> -@@ -2392,6 +2393,246 @@ static int emac_read_uint_prop(struct de - return 0; - } - -+static void emac_adjust_link(struct net_device *ndev) -+{ -+ struct emac_instance *dev = netdev_priv(ndev); -+ struct phy_device *phy = dev->phy_dev; -+ -+ dev->phy.autoneg = phy->autoneg; -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ dev->phy.advertising = phy->advertising; -+} -+ -+static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum) -+{ -+ return emac_mdio_read(bus->priv, addr, regnum); -+} -+ -+static int emac_mii_bus_write(struct mii_bus *bus, int addr, int regnum, u16 val) -+{ -+ emac_mdio_write(bus->priv, addr, regnum, val); -+ return 0; -+} -+ -+static int emac_mii_bus_reset(struct mii_bus *bus) -+{ -+ struct emac_instance *dev = netdev_priv(bus->priv); -+ -+ emac_mii_reset_phy(&dev->phy); -+ return 0; -+} -+ -+static int emac_mdio_setup_aneg(struct mii_phy *phy, u32 advertise) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ dev->phy.autoneg = AUTONEG_ENABLE; -+ dev->phy.speed = SPEED_1000; -+ dev->phy.duplex = DUPLEX_FULL; -+ dev->phy.advertising = advertise; -+ phy->autoneg = AUTONEG_ENABLE; -+ phy->speed = dev->phy.speed; -+ phy->duplex = dev->phy.duplex; -+ phy->advertising = advertise; -+ return phy_start_aneg(dev->phy_dev); -+} -+ -+static int emac_mdio_setup_forced(struct mii_phy *phy, int speed, int fd) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ dev->phy.autoneg = AUTONEG_DISABLE; -+ dev->phy.speed = speed; -+ dev->phy.duplex = fd; -+ phy->autoneg = AUTONEG_DISABLE; -+ phy->speed = speed; -+ phy->duplex = fd; -+ return phy_start_aneg(dev->phy_dev); -+} -+ -+static int emac_mdio_poll_link(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ int res; -+ -+ res = phy_read_status(dev->phy_dev); -+ if (res) { -+ dev_err(&dev->ndev->dev, "link update failed (%d).", res); -+ return ethtool_op_get_link(ndev); -+ } -+ -+ return dev->phy_dev->link; -+} -+ -+static int emac_mdio_read_link(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ int res; -+ -+ res = phy_read_status(dev->phy_dev); -+ if (res) -+ return res; -+ -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ return 0; -+} -+ -+static int emac_mdio_init_phy(struct mii_phy *phy) -+{ -+ struct net_device *ndev = phy->dev; -+ struct emac_instance *dev = netdev_priv(ndev); -+ -+ phy_start(dev->phy_dev); -+ dev->phy.autoneg = phy->autoneg; -+ dev->phy.speed = phy->speed; -+ dev->phy.duplex = phy->duplex; -+ dev->phy.advertising = phy->advertising; -+ dev->phy.pause = phy->pause; -+ dev->phy.asym_pause = phy->asym_pause; -+ -+ return phy_init_hw(dev->phy_dev); -+} -+ -+static const struct mii_phy_ops emac_dt_mdio_phy_ops = { -+ .init = emac_mdio_init_phy, -+ .setup_aneg = emac_mdio_setup_aneg, -+ .setup_forced = emac_mdio_setup_forced, -+ .poll_link = emac_mdio_poll_link, -+ .read_link = emac_mdio_read_link, -+}; -+ -+static void emac_dt_phy_mdio_cleanup(struct emac_instance *dev) -+{ -+ if (dev->mii_bus) { -+ if (dev->mii_bus->state == MDIOBUS_REGISTERED) -+ mdiobus_unregister(dev->mii_bus); -+ mdiobus_free(dev->mii_bus); -+ dev->mii_bus = NULL; -+ } -+ kfree(dev->phy.def); -+ dev->phy.def = NULL; -+} -+ -+static int emac_dt_mdio_probe(struct emac_instance *dev) -+{ -+ struct device_node *mii_np; -+ int res; -+ -+ mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio"); -+ if (!mii_np) { -+ dev_err(&dev->ndev->dev, "no mdio definition found."); -+ return -ENODEV; -+ } -+ -+ if (!of_device_is_available(mii_np)) { -+ res = 1; -+ goto err_put_node; -+ } -+ -+ dev->mii_bus = mdiobus_alloc(); -+ if (!dev->mii_bus) { -+ res = -ENOMEM; -+ goto err_cleanup_mdio; -+ } -+ -+ dev->mii_bus->priv = dev->ndev; -+ dev->mii_bus->parent = dev->ndev->dev.parent; -+ dev->mii_bus->name = "emac_mdio"; -+ dev->mii_bus->read = &emac_mii_bus_read; -+ dev->mii_bus->write = &emac_mii_bus_write; -+ dev->mii_bus->reset = &emac_mii_bus_reset; -+ snprintf(dev->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev->mii_bus->name); -+ -+ res = of_mdiobus_register(dev->mii_bus, mii_np); -+ if (res) { -+ dev_err(&dev->ndev->dev, "cannot register MDIO bus %s (%d)", -+ dev->mii_bus->name, res); -+ goto err_cleanup_mdio; -+ } -+ of_node_put(mii_np); -+ return 0; -+ -+ err_cleanup_mdio: -+ emac_dt_phy_mdio_cleanup(dev); -+ err_put_node: -+ of_node_put(mii_np); -+ return res; -+} -+ -+static int emac_dt_phy_probe(struct emac_instance *dev, -+ struct device_node *phy_handle) -+{ -+ u32 phy_flags = 0; -+ int res; -+ -+ res = of_property_read_u32(phy_handle, "phy-flags", &phy_flags); -+ if (res < 0 && res != -EINVAL) -+ return res; -+ -+ dev->phy.def = kzalloc(sizeof(*dev->phy.def), GFP_KERNEL); -+ if (!dev->phy.def) -+ return -ENOMEM; -+ -+ dev->phy_dev = of_phy_connect(dev->ndev, phy_handle, -+ &emac_adjust_link, phy_flags, -+ PHY_INTERFACE_MODE_RGMII); -+ if (!dev->phy_dev) { -+ dev_err(&dev->ndev->dev, "failed to connect to PHY."); -+ kfree(dev->phy.dev); -+ dev->phy.dev = NULL; -+ return -ENODEV; -+ } -+ -+ dev->phy.def->phy_id = dev->phy_dev->drv->phy_id; -+ dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask; -+ dev->phy.def->name = dev->phy_dev->drv->name; -+ dev->phy.def->ops = &emac_dt_mdio_phy_ops; -+ dev->phy.features = dev->phy_dev->supported; -+ dev->phy.address = dev->phy_dev->addr; -+ dev->phy.mode = dev->phy_dev->interface; -+ return 0; -+} -+ -+static int emac_probe_dt_phy(struct emac_instance *dev) -+{ -+ struct device_node *np = dev->ofdev->dev.of_node; -+ struct device_node *phy_handle; -+ int res = 0; -+ -+ phy_handle = of_parse_phandle(np, "phy-handle", 0); -+ -+ if (phy_handle) { -+ res = emac_dt_mdio_probe(dev); -+ if (res) -+ goto out; -+ -+ res = emac_dt_phy_probe(dev, phy_handle); -+ if (res) { -+ emac_dt_phy_mdio_cleanup(dev); -+ goto out; -+ } -+ -+ return 1; -+ } -+ -+ out: -+ of_node_put(phy_handle); -+ /* if no phy device was specifie in the device tree, then we fallback -+ * to the old emac_phy.c probe code for compatibility reasons. -+ */ -+ return res; -+} -+ - static int emac_init_phy(struct emac_instance *dev) - { - struct device_node *np = dev->ofdev->dev.of_node; -@@ -2462,6 +2703,18 @@ static int emac_init_phy(struct emac_ins - - emac_configure(dev); - -+ if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) { -+ int res = emac_probe_dt_phy(dev); -+ -+ if (res == 1) -+ goto init_phy; -+ if (res < 0) -+ dev_err(&dev->ndev->dev, "failed to attach dt phy (%d).", -+ res); -+ -+ /* continue with old code */ -+ } -+ - if (dev->phy_address != 0xffffffff) - phy_map = ~(1 << dev->phy_address); - -@@ -2489,6 +2742,7 @@ static int emac_init_phy(struct emac_ins - return -ENXIO; - } - -+ init_phy: - /* Init PHY */ - if (dev->phy.def->ops->init) - dev->phy.def->ops->init(&dev->phy); -@@ -2907,6 +3161,8 @@ static int emac_probe(struct platform_de - /* I have a bad feeling about this ... */ - - err_detach_tah: -+ emac_dt_phy_mdio_cleanup(dev); -+ - if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) - tah_detach(dev->tah_dev, dev->tah_port); - err_detach_rgmii: -@@ -2957,6 +3213,11 @@ static int emac_remove(struct platform_d - if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) - zmii_detach(dev->zmii_dev, dev->zmii_port); - -+ if (dev->phy_dev) -+ phy_disconnect(dev->phy_dev); -+ -+ emac_dt_phy_mdio_cleanup(dev); -+ - busy_phy_map &= ~(1 << dev->phy.address); - DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map); - ---- a/drivers/net/ethernet/ibm/emac/core.h -+++ b/drivers/net/ethernet/ibm/emac/core.h -@@ -199,6 +199,10 @@ struct emac_instance { - struct emac_instance *mdio_instance; - struct mutex mdio_lock; - -+ /* Device-tree based phy configuration */ -+ struct mii_bus *mii_bus; -+ struct phy_device *phy_dev; -+ - /* ZMII infos if any */ - u32 zmii_ph; - u32 zmii_port; diff --git a/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch deleted file mode 100644 index 5d63ddd429..0000000000 --- a/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch +++ /dev/null @@ -1,545 +0,0 @@ -From 419992bae5aaa4e06402e0b7c79fcf7bcb6b4764 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@googlemail.com> -Date: Thu, 2 Jun 2016 00:48:46 +0200 -Subject: [PATCH] usb: xhci: add firmware loader for uPD720201 and uPD720202 - w/o ROM - -This patch adds a firmware loader for the uPD720201K8-711-BAC-A -and uPD720202K8-711-BAA-A variant. Both of these chips are listed -in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as -devices which need the firmware loader on page 2 in order to -work as they "do not support the External ROM". - -The "Firmware Download Sequence" is describe in chapter -"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131. - -The firmware "K2013080.mem" is available from a USB3.0 Host to -PCIe Adapter (PP2U-E card) "Firmware download" archive. An -alternative version can be sourced from Netgear's WNDR4700 GPL -archives. - -The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3 -(2012-06-15) state that the firmware is for the following devices: - - uPD720201 ES 2.0 sample whose revision ID is 2. - - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. - - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. - -If someone from Renesas is listening: It would be great, if these -firmwares could be added to linux-firmware.git. - -Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> -Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> ---- - drivers/usb/host/xhci-pci.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 492 insertions(+) - ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -24,6 +24,8 @@ - #include <linux/slab.h> - #include <linux/module.h> - #include <linux/acpi.h> -+#include <linux/firmware.h> -+#include <asm/unaligned.h> - - #include "xhci.h" - #include "xhci-trace.h" -@@ -218,6 +220,458 @@ static void xhci_pme_acpi_rtd3_enable(st - static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } - #endif /* CONFIG_ACPI */ - -+static const struct renesas_fw_entry { -+ const char *firmware_name; -+ u16 device; -+ u8 revision; -+ u16 expected_version; -+} renesas_fw_table[] = { -+ /* -+ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A -+ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which -+ * need the software loader. -+ * -+ * PP2U/ReleaseNote_USB3-201-202-FW.txt: -+ * -+ * Note: This firmware is for the following devices. -+ * - uPD720201 ES 2.0 sample whose revision ID is 2. -+ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. -+ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. -+ */ -+ { "K2013080.mem", 0x0014, 0x02, 0x2013 }, -+ { "K2013080.mem", 0x0014, 0x03, 0x2013 }, -+ { "K2013080.mem", 0x0015, 0x02, 0x2013 }, -+}; -+ -+static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) -+{ -+ const struct renesas_fw_entry *entry; -+ size_t i; -+ -+ /* This loader will only work with a RENESAS device. */ -+ if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) -+ return NULL; -+ -+ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { -+ entry = &renesas_fw_table[i]; -+ if (entry->device == dev->device && -+ entry->revision == dev->revision) -+ return entry; -+ } -+ -+ return NULL; -+} -+ -+static int renesas_fw_download_image(struct pci_dev *dev, -+ const u32 *fw, -+ size_t step) -+{ -+ size_t i; -+ int err; -+ u8 fw_status; -+ bool data0_or_data1; -+ -+ /* -+ * The hardware does alternate between two 32-bit pages. -+ * (This is because each row of the firmware is 8 bytes). -+ * -+ * for even steps we use DATA0, for odd steps DATA1. -+ */ -+ data0_or_data1 = (step & 1) == 1; -+ -+ /* step+1. Read "Set DATAX" and confirm it is cleared. */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(dev, 0xF5, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & BIT(data0_or_data1))) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) -+ return -ETIMEDOUT; -+ -+ /* -+ * step+2. Write FW data to "DATAX". -+ * "LSB is left" => force little endian -+ */ -+ err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8, -+ (__force u32) cpu_to_le32(fw[step])); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ udelay(100); -+ -+ /* step+3. Set "Set DATAX". */ -+ err = pci_write_config_byte(dev, 0xF5, BIT(data0_or_data1)); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ return 0; -+} -+ -+static int renesas_fw_verify(struct pci_dev *dev, -+ const void *fw_data, -+ size_t length) -+{ -+ const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); -+ u16 fw_version_pointer; -+ u16 fw_version; -+ -+ if (!entry) -+ return -EINVAL; -+ -+ /* -+ * The Firmware's Data Format is describe in -+ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 -+ */ -+ -+ /* "Each row is 8 bytes". => firmware size must be a multiple of 8. */ -+ if (length % 8 != 0) { -+ dev_err(&dev->dev, "firmware size is not a multipe of 8."); -+ return -EINVAL; -+ } -+ -+ /* -+ * The bootrom chips of the big brother have sizes up to 64k, let's -+ * assume that's the biggest the firmware can get. -+ */ -+ if (length < 0x1000 || length >= 0x10000) { -+ dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", -+ length); -+ return -EINVAL; -+ } -+ -+ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ -+ if (get_unaligned_le16(fw_data) != 0x55aa) { -+ dev_err(&dev->dev, "no valid firmware header found."); -+ return -EINVAL; -+ } -+ -+ /* verify the firmware version position and print it. */ -+ fw_version_pointer = get_unaligned_le16(fw_data + 4); -+ if (fw_version_pointer + 2 >= length) { -+ dev_err(&dev->dev, "firmware version pointer is outside of the firmware image."); -+ return -EINVAL; -+ } -+ -+ fw_version = get_unaligned_le16(fw_data + fw_version_pointer); -+ dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); -+ -+ if (fw_version != entry->expected_version) { -+ dev_err(&dev->dev, "firmware version mismatch, expected version: %02x.", -+ entry->expected_version); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int renesas_fw_check_running(struct pci_dev *pdev) -+{ -+ int err; -+ u8 fw_state; -+ -+ /* -+ * Test if the device is actually needing the firmware. As most -+ * BIOSes will initialize the device for us. If the device is -+ * initialized. -+ */ -+ err = pci_read_config_byte(pdev, 0xF4, &fw_state); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* -+ * Check if "FW Download Lock" is locked. If it is and the FW is -+ * ready we can simply continue. If the FW is not ready, we have -+ * to give up. -+ */ -+ if (fw_state & BIT(1)) { -+ dev_dbg(&pdev->dev, "FW Download Lock is engaged."); -+ -+ if (fw_state & BIT(4)) -+ return 0; -+ -+ dev_err(&pdev->dev, "FW Download Lock is set and FW is not ready. Giving Up."); -+ return -EIO; -+ } -+ -+ /* -+ * Check if "FW Download Enable" is set. If someone (us?) tampered -+ * with it and it can't be resetted, we have to give up too... and -+ * ask for a forgiveness and a reboot. -+ */ -+ if (fw_state & BIT(0)) { -+ dev_err(&pdev->dev, "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); -+ return -EIO; -+ } -+ -+ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ -+ switch ((fw_state & 0x70)) { -+ case 0: /* No result yet */ -+ dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); -+ -+ /* tell the caller, that this device needs the firmware. */ -+ return 1; -+ -+ case BIT(4): /* Success, device should be working. */ -+ dev_dbg(&pdev->dev, "FW is ready."); -+ return 0; -+ -+ case BIT(5): /* Error State */ -+ dev_err(&pdev->dev, "hardware is in an error state. Giving up (poweroff/reboot needed)."); -+ return -ENODEV; -+ -+ default: /* All other states are marked as "Reserved states" */ -+ dev_err(&pdev->dev, "hardware is in an invalid state %x. Giving up (poweroff/reboot needed).", -+ (fw_state & 0x70) >> 4); -+ return -EINVAL; -+ } -+} -+ -+static int renesas_hw_check_run_stop_busy(struct pci_dev *pdev) -+{ -+#if 0 -+ u32 val; -+ -+ /* -+ * 7.1.3 Note 3: "... must not set 'FW Download Enable' when -+ * 'RUN/STOP' of USBCMD Register is set" -+ */ -+ val = readl(hcd->regs + 0x20); -+ if (val & BIT(0)) { -+ dev_err(&pdev->dev, "hardware is busy and can't receive a FW."); -+ return -EBUSY; -+ } -+#endif -+ return 0; -+} -+ -+static int renesas_fw_download(struct pci_dev *pdev, -+ const struct firmware *fw, unsigned int retry_counter) -+{ -+ const u32 *fw_data = (const u32 *) fw->data; -+ size_t i; -+ int err; -+ u8 fw_status; -+ -+ /* -+ * For more information and the big picture: please look at the -+ * "Firmware Download Sequence" in "7.1 FW Download Interface" -+ * of R19UH0078EJ0500 Rev.5.00 page 131 -+ */ -+ err = renesas_hw_check_run_stop_busy(pdev); -+ if (err) -+ return err; -+ -+ /* -+ * 0. Set "FW Download Enable" bit in the -+ * "FW Download Control & Status Register" at 0xF4 -+ */ -+ err = pci_write_config_byte(pdev, 0xF4, BIT(0)); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 1 - 10 follow one step after the other. */ -+ for (i = 0; i < fw->size / 4; i++) { -+ err = renesas_fw_download_image(pdev, fw_data, i); -+ if (err) { -+ dev_err(&pdev->dev, "Firmware Download Step %zd failed at position %zd bytes with (%d).", -+ i, i * 4, err); -+ return err; -+ } -+ } -+ -+ /* -+ * This sequence continues until the last data is written to -+ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" -+ * is cleared by the hardware beforehand. -+ */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(pdev, 0xF5, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & (BIT(0) | BIT(1)))) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) -+ dev_warn(&pdev->dev, "Final Firmware Download step timed out."); -+ -+ /* -+ * 11. After finishing writing the last data of FW, the -+ * System Software must clear "FW Download Enable" -+ */ -+ err = pci_write_config_byte(pdev, 0xF4, 0); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 12. Read "Result Code" and confirm it is good. */ -+ for (i = 0; i < 10000; i++) { -+ err = pci_read_config_byte(pdev, 0xF4, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (fw_status & BIT(4)) -+ break; -+ -+ udelay(1); -+ } -+ if (i == 10000) { -+ /* Timed out / Error - let's see if we can fix this */ -+ err = renesas_fw_check_running(pdev); -+ switch (err) { -+ case 0: /* -+ * we shouldn't end up here. -+ * maybe it took a little bit longer. -+ * But all should be well? -+ */ -+ break; -+ -+ case 1: /* (No result yet? - we can try to retry) */ -+ if (retry_counter < 10) { -+ retry_counter++; -+ dev_warn(&pdev->dev, "Retry Firmware download: %d try.", -+ retry_counter); -+ return renesas_fw_download(pdev, fw, -+ retry_counter); -+ } -+ return -ETIMEDOUT; -+ -+ default: -+ return err; -+ } -+ } -+ /* -+ * Optional last step: Engage Firmware Lock -+ * -+ * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); -+ * if (err) -+ * return pcibios_err_to_errno(err); -+ */ -+ -+ return 0; -+} -+ -+struct renesas_fw_ctx { -+ struct pci_dev *pdev; -+ const struct pci_device_id *id; -+ bool resume; -+}; -+ -+static int xhci_pci_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id); -+ -+static void renesas_fw_callback(const struct firmware *fw, -+ void *context) -+{ -+ struct renesas_fw_ctx *ctx = context; -+ struct pci_dev *pdev = ctx->pdev; -+ struct device *parent = pdev->dev.parent; -+ int err = -ENOENT; -+ -+ if (fw) { -+ err = renesas_fw_verify(pdev, fw->data, fw->size); -+ if (!err) { -+ err = renesas_fw_download(pdev, fw, 0); -+ release_firmware(fw); -+ if (!err) { -+ if (ctx->resume) -+ return; -+ -+ err = xhci_pci_probe(pdev, ctx->id); -+ if (!err) { -+ /* everything worked */ -+ devm_kfree(&pdev->dev, ctx); -+ return; -+ } -+ -+ /* in case of an error - fall through */ -+ } else { -+ dev_err(&pdev->dev, "firmware failed to download (%d).", -+ err); -+ } -+ } -+ } else { -+ dev_err(&pdev->dev, "firmware failed to load (%d).", err); -+ } -+ -+ dev_info(&pdev->dev, "Unloading driver"); -+ -+ if (parent) -+ device_lock(parent); -+ -+ device_release_driver(&pdev->dev); -+ -+ if (parent) -+ device_unlock(parent); -+ -+ pci_dev_put(pdev); -+} -+ -+static int renesas_fw_alive_check(struct pci_dev *pdev) -+{ -+ const struct renesas_fw_entry *entry; -+ int err; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ err = renesas_fw_check_running(pdev); -+ /* Also go ahead, if the firmware is running */ -+ if (err == 0) -+ return 0; -+ -+ /* At this point, we can be sure that the FW isn't ready. */ -+ return err; -+} -+ -+static int renesas_fw_download_to_hw(struct pci_dev *pdev, -+ const struct pci_device_id *id, -+ bool do_resume) -+{ -+ const struct renesas_fw_entry *entry; -+ struct renesas_fw_ctx *ctx; -+ int err; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ err = renesas_fw_check_running(pdev); -+ /* Continue ahead, if the firmware is already running. */ -+ if (err == 0) -+ return 0; -+ -+ if (err != 1) -+ return err; -+ -+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ ctx->pdev = pdev; -+ ctx->resume = do_resume; -+ ctx->id = id; -+ -+ pci_dev_get(pdev); -+ err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, -+ &pdev->dev, GFP_KERNEL, ctx, renesas_fw_callback); -+ if (err) { -+ pci_dev_put(pdev); -+ return err; -+ } -+ -+ /* -+ * The renesas_fw_callback() callback will continue the probe -+ * process, once it aquires the firmware. -+ */ -+ return 1; -+} -+ - /* called during probe() after chip reset completes */ - static int xhci_pci_setup(struct usb_hcd *hcd) - { -@@ -257,6 +711,22 @@ static int xhci_pci_probe(struct pci_dev - struct hc_driver *driver; - struct usb_hcd *hcd; - -+ /* -+ * Check if this device is a RENESAS uPD720201/2 device. -+ * Otherwise, we can continue with xhci_pci_probe as usual. -+ */ -+ retval = renesas_fw_download_to_hw(dev, id, false); -+ switch (retval) { -+ case 0: -+ break; -+ -+ case 1: /* let it load the firmware and recontinue the probe. */ -+ return 0; -+ -+ default: -+ return retval; -+ }; -+ - driver = (struct hc_driver *)id->driver_data; - - /* Prevent runtime suspending between USB-2 and USB-3 initialization */ -@@ -314,6 +784,16 @@ static void xhci_pci_remove(struct pci_d - { - struct xhci_hcd *xhci; - -+ if (renesas_fw_alive_check(dev)) { -+ /* -+ * bail out early, if this was a renesas device w/o FW. -+ * Else we might hit the NMI watchdog in xhci_handsake -+ * during xhci_reset as part of the driver's unloading. -+ * which we forced in the renesas_fw_callback(). -+ */ -+ return; -+ } -+ - xhci = hcd_to_xhci(pci_get_drvdata(dev)); - xhci->xhc_state |= XHCI_STATE_REMOVING; - if (xhci->shared_hcd) { diff --git a/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch b/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch deleted file mode 100644 index 5395cabac9..0000000000 --- a/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch +++ /dev/null @@ -1,54 +0,0 @@ -From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Thu, 23 Jun 2016 20:28:20 +0200 -Subject: [PATCH] usb: xhci: force MSI for uPD720201 and - uPD720202 - -The APM82181 does not support MSI-X. When probed, it will -produce a noisy warning. - ---- - drivers/usb/host/pci-quirks.c | 362 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 362 insertions(+) - ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -185,7 +185,7 @@ static void xhci_pci_quirks(struct devic - } - if (pdev->vendor == PCI_VENDOR_ID_RENESAS && - pdev->device == 0x0015) -- xhci->quirks |= XHCI_RESET_ON_RESUME; -+ xhci->quirks |= XHCI_RESET_ON_RESUME | XHCI_FORCE_MSI; - if (pdev->vendor == PCI_VENDOR_ID_VIA) - xhci->quirks |= XHCI_RESET_ON_RESUME; - ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -388,10 +388,14 @@ static int xhci_try_enable_msi(struct us - free_irq(hcd->irq, hcd); - hcd->irq = 0; - -- ret = xhci_setup_msix(xhci); -- if (ret) -- /* fall back to msi*/ -+ if (xhci->quirks & XHCI_FORCE_MSI) { - ret = xhci_setup_msi(xhci); -+ } else { -+ ret = xhci_setup_msix(xhci); -+ if (ret) -+ /* fall back to msi*/ -+ ret = xhci_setup_msi(xhci); -+ } - - if (!ret) - /* hcd->irq is 0, we have MSI */ ---- a/drivers/usb/host/xhci.h -+++ b/drivers/usb/host/xhci.h -@@ -1652,6 +1652,7 @@ struct xhci_hcd { - /* support xHCI 0.96 spec USB2 software LPM */ - unsigned sw_lpm_support:1; - /* support xHCI 1.0 spec USB2 hardware LPM */ -+#define XHCI_FORCE_MSI (1 << 24) - unsigned hw_lpm_support:1; - /* cached usb2 extened protocol capabilites */ - u32 *ext_caps; diff --git a/target/linux/apm821xx/patches-4.4/804-usb-dwc2-add-amcc-usb-otg-405ex.patch b/target/linux/apm821xx/patches-4.4/804-usb-dwc2-add-amcc-usb-otg-405ex.patch deleted file mode 100644 index fb353be225..0000000000 --- a/target/linux/apm821xx/patches-4.4/804-usb-dwc2-add-amcc-usb-otg-405ex.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- a/drivers/usb/dwc2/platform.c -+++ b/drivers/usb/dwc2/platform.c -@@ -115,6 +115,37 @@ static const struct dwc2_core_params par - .hibernation = -1, - }; - -+static const struct dwc2_core_params params_amcc_dwc_otg = { -+ .otg_cap = DWC2_CAP_PARAM_HNP_SRP_CAPABLE, -+ .otg_ver = -1, -+ .dma_enable = -1, -+ .dma_desc_enable = -1, -+ .speed = -1, -+ .enable_dynamic_fifo = -1, -+ .en_multiple_tx_fifo = -1, -+ .host_rx_fifo_size = -1, -+ .host_nperio_tx_fifo_size = -1, -+ .host_perio_tx_fifo_size = -1, -+ .max_transfer_size = -1, -+ .max_packet_count = -1, -+ .host_channels = -1, -+ .phy_type = -1, -+ .phy_utmi_width = -1, -+ .phy_ulpi_ddr = -1, -+ .phy_ulpi_ext_vbus = -1, -+ .i2c_enable = -1, -+ .ulpi_fs_ls = -1, -+ .host_support_fs_ls_low_power = -1, -+ .host_ls_low_power_phy_clk = -1, -+ .ts_dline = -1, -+ .reload_ctl = -1, -+ .ahbcfg = GAHBCFG_HBSTLEN_INCR16 << -+ GAHBCFG_HBSTLEN_SHIFT, -+ .uframe_sched = -1, -+ .external_id_pin_ctl = -1, -+ .hibernation = -1, -+}; -+ - static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) - { - struct platform_device *pdev = to_platform_device(hsotg->dev); -@@ -307,6 +338,7 @@ static int dwc2_driver_remove(struct pla - } - - static const struct of_device_id dwc2_of_match_table[] = { -+ { .compatible = "amcc,usb-otg-405ex", .data = ¶ms_amcc_dwc_otg }, - { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, - { .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, - { .compatible = "snps,dwc2", .data = NULL }, diff --git a/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch b/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch deleted file mode 100644 index 41f3370008..0000000000 --- a/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch +++ /dev/null @@ -1,1027 +0,0 @@ -From 5ea2e152d846bf60901107fefd81a58f792f3bc2 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Fri, 10 Jun 2016 03:00:46 +0200 -Subject: [PATCH] hwmon: add driver for Microchip TC654/TC655 PWM fan - controllers - -This patch adds a hwmon driver for the Microchip TC654 and TC655 -Dual SMBus PWM Fan Speed Controllers with Fan Fault detection. - -The chip is described in the DS2001734C Spec Document from Microchip. -It supports: - - Shared PWM Fan Drive for two fans - - Provides RPM - - automatic PWM controller (needs additional - NTC/PTC Thermistors.) - - Overtemperature alarm (when using NTC/PTC - Thermistors) - -Signed-off-by: Christian Lamparter <chunkeey@gmail.com> ---- - drivers/hwmon/Kconfig | 10 + - drivers/hwmon/Makefile | 1 + - drivers/hwmon/tc654.c | 969 +++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 980 insertions(+) - create mode 100644 drivers/hwmon/tc654.c - ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1484,6 +1484,16 @@ config SENSORS_INA2XX - This driver can also be built as a module. If so, the module - will be called ina2xx. - -+config SENSORS_TC654 -+ tristate "Microchip TC654 and TC655" -+ depends on I2C -+ help -+ If you say yes here you get support for Microchip TC655 and TC654 -+ Dual PWM Fan Speed Controllers and sensor chips. -+ -+ This driver can also be built as a module. If so, the module -+ will be called tc654. -+ - config SENSORS_TC74 - tristate "Microchip TC74" - depends on I2C ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -143,6 +143,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc4 - obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o - obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o - obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o -+obj-$(CONFIG_SENSORS_TC654) += tc654.o - obj-$(CONFIG_SENSORS_TC74) += tc74.o - obj-$(CONFIG_SENSORS_THMC50) += thmc50.o - obj-$(CONFIG_SENSORS_TMP102) += tmp102.o ---- /dev/null -+++ b/drivers/hwmon/tc654.c -@@ -0,0 +1,969 @@ -+/* -+ * tc654.c - Support for Microchip TC654/TC655 -+ * "A Dual SMBus PWM FAN Speed Controllers with Fan Fault Detection" -+ * -+ * Copyright (c) 2016 Christian Lamparter <chunkeey@gmail.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * The chip is described in the DS2001734C Spec Document from Microchip. -+ */ -+ -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/jiffies.h> -+#include <linux/i2c.h> -+#include <linux/hwmon.h> -+#include <linux/hwmon-sysfs.h> -+#include <linux/err.h> -+#include <linux/mutex.h> -+#include <linux/thermal.h> -+ -+/* Hardware definitions */ -+/* 5.1.4 Address Byte stats that TC654/TC655 are fixed at 0x1b */ -+static const unsigned short normal_i2c[] = { 0x1b, I2C_CLIENT_END }; -+ -+enum TC654_REGS { -+ TC654_REG_RPM1 = 0x00, -+ TC654_REG_RPM2, -+ TC654_REG_FAN1_FAULT_THRESH, -+ TC654_REG_FAN2_FAULT_THRESH, -+ TC654_REG_CONFIG, -+ TC654_REG_STATUS, -+ TC654_REG_DUTY_CYCLE, -+ TC654_REG_MFR_ID, -+ TC654_REG_VER_ID, -+ -+ /* keep last */ -+ __TC654_REG_NUM, -+}; -+ -+#define TC654_MFR_ID_MICROCHIP 0x84 -+#define TC654_VER_ID 0x00 -+#define TC655_VER_ID 0x01 -+ -+enum TC654_CONTROL_BITS { -+ TC654_CTRL_SDM = BIT(0), -+ TC654_CTRL_F1PPR_S = 1, -+ TC654_CTRL_F1PPR_M = (BIT(1) | BIT(2)), -+ TC654_CTRL_F2PPR_S = 3, -+ TC654_CTRL_F2PPR_M = (BIT(3) | BIT(4)), -+ TC654_CTRL_DUTYC = BIT(5), -+ TC654_CTRL_RES = BIT(6), -+ TC654_CTRL_FFCLR = BIT(7), -+}; -+ -+enum TC654_STATUS_BITS { -+ TC654_STATUS_F1F = BIT(0), -+ TC654_STATUS_F2F = BIT(1), -+ TC654_STATUS_VSTAT = BIT(2), -+ TC654_STATUS_R1CO = BIT(3), -+ TC654_STATUS_R2CO = BIT(4), -+ TC654_STATUS_OTF = BIT(5), -+}; -+ -+enum TC654_FAN { -+ TC654_FAN1 = 0, -+ TC654_FAN2, -+ -+ /* keep last */ -+ __NUM_TC654_FAN, -+}; -+ -+enum TC654_FAN_MODE { -+ TC654_PWM_OFF, /* Shutdown Mode - switch of both fans */ -+ TC654_PWM_VIN, /* Fans will be controlled via V_in analog input pin */ -+ TC654_PWM_3000, /* sets fans to 30% duty cycle */ -+ TC654_PWM_3467, -+ TC654_PWM_3933, /* default case - if V_in pin is open */ -+ TC654_PWM_4400, -+ TC654_PWM_4867, -+ TC654_PWM_5333, -+ TC654_PWM_5800, -+ TC654_PWM_6267, -+ TC654_PWM_6733, -+ TC654_PWM_7200, -+ TC654_PWM_7667, -+ TC654_PWM_8133, -+ TC654_PWM_8600, -+ TC654_PWM_9067, -+ TC654_PWM_9533, -+ TC654_PWM_10000, /* sets fans to 100% duty cycle */ -+}; -+ -+enum TC654_ALARMS { -+ TC654_ALARM_FAN1_FAULT, -+ TC654_ALARM_FAN2_FAULT, -+ TC654_ALARM_FAN1_COUNTER_OVERFLOW, -+ TC654_ALARM_FAN2_COUNTER_OVERFLOW, -+ TC654_ALARM_OVER_TEMPERATURE, -+ -+ /* KEEP LAST */ -+ __NUM_TC654_ALARMS, -+}; -+ -+static const struct pwm_table_entry { -+ u8 min; -+ enum TC654_FAN_MODE mode; -+} pwm_table[] = { -+ { 0, TC654_PWM_OFF }, -+ { 1, TC654_PWM_3000 }, -+ { 88, TC654_PWM_3467 }, -+ {101, TC654_PWM_3933 }, -+ {113, TC654_PWM_4400 }, -+ {125, TC654_PWM_4867 }, -+ {137, TC654_PWM_5333 }, -+ {148, TC654_PWM_5800 }, -+ {160, TC654_PWM_6267 }, -+ {172, TC654_PWM_6733 }, -+ {184, TC654_PWM_7200 }, -+ {196, TC654_PWM_7667 }, -+ {208, TC654_PWM_8133 }, -+ {220, TC654_PWM_8600 }, -+ {232, TC654_PWM_9067 }, -+ {244, TC654_PWM_9533 }, -+ {255, TC654_PWM_10000 }, -+}; -+ -+/* driver context */ -+struct tc654 { -+ struct i2c_client *client; -+ -+ struct mutex update_lock; -+ -+ unsigned long last_updated; /* in jiffies */ -+ u8 cached_regs[__TC654_REG_NUM]; -+ -+ bool valid; /* monitored registers are valid */ -+ u16 fan_input[__NUM_TC654_FAN]; -+ bool alarms[__NUM_TC654_ALARMS]; -+ bool vin_status; -+ bool pwm_manual; -+ -+ /* optional cooling device */ -+ struct thermal_cooling_device *cdev; -+}; -+ -+/* hardware accessors and functions */ -+static int read_tc(struct tc654 *tc, u8 reg) -+{ -+ s32 status; -+ -+ if (reg <= TC654_REG_VER_ID) { -+ /* Table 6.1 states that all registers are readable */ -+ status = i2c_smbus_read_byte_data(tc->client, reg); -+ } else -+ status = -EINVAL; -+ -+ if (status < 0) { -+ dev_warn(&tc->client->dev, "can't read register 0x%02x due to error (%d)", -+ reg, status); -+ } else { -+ tc->cached_regs[reg] = status; -+ } -+ -+ return status; -+} -+ -+static int write_tc(struct tc654 *tc, u8 i2c_reg, u8 val) -+{ -+ s32 status; -+ -+ /* -+ * Table 6.1 states that both fan threshold registers, -+ * the Config and Duty Cycle are writeable. -+ */ -+ switch (i2c_reg) { -+ case TC654_REG_FAN1_FAULT_THRESH: -+ case TC654_REG_FAN2_FAULT_THRESH: -+ case TC654_REG_DUTY_CYCLE: -+ case TC654_REG_CONFIG: -+ status = i2c_smbus_write_byte_data(tc->client, i2c_reg, val); -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ if (status < 0) { -+ dev_warn(&tc->client->dev, "can't write register 0x%02x with value 0x%02x due to error (%d)", -+ i2c_reg, val, status); -+ } else { -+ tc->cached_regs[i2c_reg] = val; -+ } -+ -+ return status; -+} -+ -+static int mod_config(struct tc654 *tc, u8 set, u8 clear) -+{ -+ u8 val = 0; -+ -+ /* a bit can't be set and cleared on the same time. */ -+ if (set & clear) -+ return -EINVAL; -+ -+ /* invalidate data to force re-read from hardware */ -+ tc->valid = false; -+ val = (tc->cached_regs[TC654_REG_CONFIG] | set) & (~clear); -+ return write_tc(tc, TC654_REG_CONFIG, val); -+} -+ -+static int read_fan_rpm(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ int ret; -+ -+ /* 6.1 RPM-OUTPUT1 and RPM-OUTPUT2 registers */ -+ ret = read_tc(tc, fan == TC654_FAN1 ? TC654_REG_RPM1 : TC654_REG_RPM2); -+ if (ret < 0) -+ return ret; -+ -+ /* -+ * The Resolution Selection Bit in 6.3 CONFIGURATION REGISTER -+ * is needed to convert the raw value to the RPM. -+ * 0 = RPM1 and RPM2 use (8-Bit) resolution => * 50 RPM -+ * 1 = RPM1 and RPM2 use (9-Bit) resolution => * 25 RPM -+ */ -+ return ret * (25 << -+ !(tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES)); -+} -+ -+static int write_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan, -+ u16 rpm) -+{ -+ u8 converted_rpm; -+ -+ if (rpm > 12750) -+ return -EINVAL; -+ -+ /* -+ * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers -+ * -+ * Both registers operate in 50 RPM mode exclusively. -+ */ -+ converted_rpm = rpm / 50; -+ -+ /* invalidate data to force re-read from hardware */ -+ tc->valid = false; -+ return write_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH : -+ TC654_REG_FAN2_FAULT_THRESH, converted_rpm); -+} -+ -+ -+static int read_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ /* -+ * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers -+ * -+ * Both registers operate in 50 RPM mode exclusively. -+ */ -+ return read_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH : -+ TC654_REG_FAN2_FAULT_THRESH) * 50; -+} -+ -+static enum TC654_FAN_MODE get_fan_mode(struct tc654 *tc) -+{ -+ if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) { -+ return TC654_PWM_OFF; -+ } else if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_DUTYC) { -+ return TC654_PWM_3000 + tc->cached_regs[TC654_REG_DUTY_CYCLE]; -+ } else if (tc->vin_status == 0) -+ return TC654_PWM_VIN; -+ -+ return -EINVAL; -+} -+ -+static int write_fan_mode(struct tc654 *tc, enum TC654_FAN_MODE mode) -+{ -+ int err; -+ u8 pwm_mode; -+ bool in_sdm; -+ -+ in_sdm = !!(tc->cached_regs[TC654_REG_CONFIG] & -+ TC654_CTRL_SDM); -+ -+ switch (mode) { -+ case TC654_PWM_OFF: -+ if (in_sdm) -+ return 0; -+ -+ /* Enter Shutdown Mode - Switches off all fans */ -+ err = mod_config(tc, TC654_CTRL_SDM, TC654_CTRL_DUTYC); -+ if (err) -+ return err; -+ -+ return 0; -+ -+ case TC654_PWM_VIN: -+ if (tc->vin_status) { -+ dev_err(&tc->client->dev, -+ "V_in pin is open, can't enable automatic mode."); -+ return -EINVAL; -+ } -+ -+ err = mod_config(tc, 0, TC654_CTRL_SDM | TC654_CTRL_DUTYC); -+ if (err) -+ return err; -+ -+ tc->pwm_manual = false; -+ return 0; -+ -+ case TC654_PWM_3000: -+ case TC654_PWM_3467: -+ case TC654_PWM_3933: -+ case TC654_PWM_4400: -+ case TC654_PWM_4867: -+ case TC654_PWM_5333: -+ case TC654_PWM_5800: -+ case TC654_PWM_6267: -+ case TC654_PWM_6733: -+ case TC654_PWM_7200: -+ case TC654_PWM_7667: -+ case TC654_PWM_8133: -+ case TC654_PWM_8600: -+ case TC654_PWM_9067: -+ case TC654_PWM_9533: -+ case TC654_PWM_10000: -+ pwm_mode = mode - TC654_PWM_3000; -+ if (!in_sdm) { -+ err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode); -+ if (err) -+ return err; -+ } -+ -+ err = mod_config(tc, TC654_CTRL_DUTYC, TC654_CTRL_SDM); -+ if (err) -+ return err; -+ -+ tc->pwm_manual = true; -+ -+ if (in_sdm) { -+ /* -+ * In case the TC654/TC655 was in SDM mode, the write -+ * above into the TC654_REG_DUTY_CYCLE register will -+ * have no effect because the chip was switched off. -+ * -+ * Note: The TC654/TC655 have a special "power-on" -+ * feature where the PWM will be forced to 100% for -+ * one full second in order to spin-up a resting fan. -+ */ -+ err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static struct tc654 *tc654_update_device(struct device *dev) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ -+ mutex_lock(&tc->update_lock); -+ -+ /* -+ * In Chapter "1.0 Electrical Characteristics", -+ * the "Fault Output Response Time" is specified as 2.4 seconds. -+ */ -+ if (time_after(jiffies, tc->last_updated + 2 * HZ + (HZ * 2) / 5) -+ || !tc->valid) { -+ size_t i; -+ int ret; -+ bool alarm_triggered; -+ -+ tc->valid = false; -+ -+ for (i = 0; i < __NUM_TC654_FAN; i++) { -+ ret = read_fan_rpm(tc, i); -+ if (ret < 0) -+ goto out; -+ -+ tc->fan_input[i] = ret; -+ } -+ -+ ret = read_tc(tc, TC654_REG_STATUS); -+ if (ret < 0) -+ goto out; -+ -+ alarm_triggered = !!(ret & (TC654_STATUS_F1F | -+ TC654_STATUS_F2F | TC654_STATUS_R1CO | -+ TC654_STATUS_R2CO | TC654_STATUS_OTF)); -+ -+ tc->alarms[TC654_ALARM_FAN1_FAULT] = !!(ret & TC654_STATUS_F1F); -+ tc->alarms[TC654_ALARM_FAN2_FAULT] = !!(ret & TC654_STATUS_F2F); -+ tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] = -+ !!(ret & TC654_STATUS_R1CO); -+ tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW] = -+ !!(ret & TC654_STATUS_R2CO); -+ tc->alarms[TC654_ALARM_OVER_TEMPERATURE] = -+ !!(ret & TC654_STATUS_OTF); -+ tc->vin_status = !!(ret & TC654_STATUS_VSTAT); -+ -+ /* -+ * From 4.5 and 6.3 -+ * -+ * ... "If the V_in pin is open when TC654_CTRL_DUTYC is not -+ * selected, then V_out duty cycle will default to 39.33%.". -+ * -+ * and most importantly 6.5: -+ * ... "V_in pin is open, the duty cycle will go to the default -+ * setting of this register, which is 0010 (39.33%)." -+ */ -+ tc->pwm_manual |= tc->vin_status && -+ (tc->cached_regs[TC654_REG_CONFIG] & -+ TC654_CTRL_DUTYC); -+ -+ if (alarm_triggered) { -+ /* -+ * as the name implies, this FLAG needs to be -+ * set in order to clear the FAN Fault error. -+ */ -+ ret = mod_config(tc, TC654_CTRL_FFCLR, 0); -+ if (ret < 0) -+ goto out; -+ } -+ -+ tc->last_updated = jiffies; -+ tc->valid = true; -+ } -+ -+out: -+ mutex_unlock(&tc->update_lock); -+ return tc; -+} -+ -+static u8 get_fan_pulse(struct tc654 *tc, enum TC654_FAN fan) -+{ -+ u8 fan_pulse_mask = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M; -+ u8 fan_pulse_shift = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S; -+ -+ return 1 << ((tc->cached_regs[TC654_REG_CONFIG] & fan_pulse_mask) >> -+ fan_pulse_shift); -+} -+ -+static int -+set_fan_pulse(struct tc654 *tc, enum TC654_FAN fan, int pulses) -+{ -+ int old_pulses; -+ int err; -+ u8 new_pulse_per_rotation; -+ u8 fan_pulse_mask = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M; -+ u8 fan_pulse_shift = fan == TC654_FAN1 ? -+ TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S; -+ -+ switch (pulses) { -+ case 1: -+ new_pulse_per_rotation = 0; -+ break; -+ case 2: -+ new_pulse_per_rotation = 1; -+ break; -+ case 4: -+ new_pulse_per_rotation = 2; -+ break; -+ case 8: -+ new_pulse_per_rotation = 3; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ new_pulse_per_rotation <<= fan_pulse_shift; -+ new_pulse_per_rotation &= fan_pulse_mask; -+ -+ old_pulses = tc->cached_regs[TC654_REG_CONFIG]; -+ old_pulses &= fan_pulse_mask; -+ -+ if (new_pulse_per_rotation == old_pulses) -+ return 0; -+ -+ mutex_lock(&tc->update_lock); -+ err = mod_config(tc, new_pulse_per_rotation, -+ old_pulses & (~new_pulse_per_rotation)); -+ mutex_unlock(&tc->update_lock); -+ -+ /* invalidate RPM data to force re-read from hardware */ -+ tc->valid = false; -+ -+ return err; -+} -+ -+static int get_fan_speed(struct tc654 *tc) -+{ -+ enum TC654_FAN_MODE mode; -+ size_t i; -+ -+ mode = get_fan_mode(tc); -+ for (i = 0; i < ARRAY_SIZE(pwm_table); i++) { -+ if (mode == pwm_table[i].mode) -+ return pwm_table[i].min; -+ } -+ -+ return -EINVAL; -+} -+ -+static int set_fan_speed(struct tc654 *tc, int new_value) -+{ -+ int result; -+ size_t i; -+ -+ if (new_value > pwm_table[ARRAY_SIZE(pwm_table) - 1].min || -+ new_value < pwm_table[0].min) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(pwm_table); i++) { -+ /* exact match */ -+ if (pwm_table[i].min == new_value) -+ break; -+ -+ /* a little bit too big - go with the previous entry */ -+ if (pwm_table[i].min > new_value) { -+ --i; -+ break; -+ } -+ } -+ -+ mutex_lock(&tc->update_lock); -+ result = write_fan_mode(tc, pwm_table[i].mode); -+ mutex_unlock(&tc->update_lock); -+ if (result < 0) -+ return result; -+ -+ return 0; -+} -+ -+/* sysfs */ -+ -+static ssize_t -+show_fan_input(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", tc->fan_input[nr]); -+} -+ -+static ssize_t -+show_fan_min(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", read_fan_fault_thresh(tc, nr)); -+} -+ -+static ssize_t -+show_fan_min_alarm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", nr == TC654_FAN1 ? -+ tc->alarms[TC654_ALARM_FAN1_FAULT] : -+ tc->alarms[TC654_ALARM_FAN2_FAULT]); -+} -+ -+static ssize_t -+show_fan_max_alarm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", nr == TC654_FAN1 ? -+ tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] : -+ tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW]); -+} -+ -+static ssize_t -+set_fan_min(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_min; -+ int nr = to_sensor_dev_attr(da)->index; -+ int old_min = read_fan_fault_thresh(tc, nr); -+ int status = kstrtol(buf, 10, &new_min); -+ -+ if (status < 0) -+ return status; -+ -+ new_min = (new_min / 50) * 50; -+ if (new_min == old_min) /* No change */ -+ return count; -+ -+ if (new_min < 0 || new_min > 12750) -+ return -EINVAL; -+ -+ mutex_lock(&tc->update_lock); -+ status = write_fan_fault_thresh(tc, nr, new_min); -+ mutex_unlock(&tc->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_fan_max(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int max_rpm = tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES ? -+ (((1 << 9) - 1) * 25) /* ((2**9) - 1) * 25 RPM */: -+ (((1 << 8) - 1) * 50) /* ((2**8) - 1) * 50 RPM */; -+ -+ return sprintf(buf, "%d\n", max_rpm); -+} -+ -+static ssize_t -+show_fan_fault(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ u8 fan_fault_mask = nr == TC654_FAN1 ? -+ TC654_STATUS_F1F : TC654_STATUS_F2F; -+ -+ return sprintf(buf, "%d\n", -+ !!(tc->cached_regs[TC654_REG_STATUS] & fan_fault_mask)); -+} -+ -+static ssize_t -+show_fan_pulses(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ int nr = to_sensor_dev_attr(da)->index; -+ -+ return sprintf(buf, "%d\n", get_fan_pulse(tc, nr)); -+} -+ -+static ssize_t -+set_fan_pulses(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_pulse; -+ int nr = to_sensor_dev_attr(da)->index; -+ int status = kstrtol(buf, 10, &new_pulse); -+ -+ if (status < 0) -+ return status; -+ -+ status = set_fan_pulse(tc, nr, new_pulse); -+ if (status < 0) -+ return status; -+ -+ return count; -+} -+ -+static ssize_t -+show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int pwm_enabled; -+ -+ if ((tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) && -+ !tc->pwm_manual) { -+ pwm_enabled = 0; /* full off */ -+ } else { -+ if (tc->valid && tc->vin_status == 0) -+ pwm_enabled = 2; /* automatic fan speed control */ -+ -+ pwm_enabled = 1; /* PWM Mode */ -+ } -+ -+ return sprintf(buf, "%d\n", pwm_enabled); -+} -+ -+static ssize_t -+set_pwm_enable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_value; -+ -+ int result = kstrtol(buf, 10, &new_value); -+ -+ if (result < 0) -+ return result; -+ -+ mutex_lock(&tc->update_lock); -+ switch (new_value) { -+ case 0: /* no fan control (i.e. is OFF) */ -+ result = write_fan_mode(tc, TC654_PWM_OFF); -+ tc->pwm_manual = false; -+ break; -+ -+ case 1: /* manual fan control enabled (using pwm) */ -+ result = write_fan_mode(tc, TC654_PWM_10000); -+ break; -+ -+ case 2: /* automatic fan speed control enabled */ -+ result = write_fan_mode(tc, TC654_PWM_VIN); -+ break; -+ -+ default: -+ result = -EINVAL; -+ } -+ -+ mutex_unlock(&tc->update_lock); -+ return result < 0 ? result : count; -+} -+ -+static ssize_t -+show_pwm(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ int ret; -+ -+ ret = get_fan_speed(tc); -+ if (ret < 0) -+ return ret; -+ -+ return sprintf(buf, "%d\n", ret); -+} -+ -+static ssize_t -+set_pwm(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct tc654 *tc = dev_get_drvdata(dev); -+ long new_value = -1; -+ int result = kstrtol(buf, 10, &new_value); -+ -+ if (result < 0) -+ return result; -+ -+ if (new_value < 0 || new_value > INT_MAX) -+ return -EINVAL; -+ -+ if (!tc->pwm_manual) -+ return -EINVAL; -+ -+ result = set_fan_speed(tc, new_value); -+ if (result < 0) -+ return result; -+ -+ return count; -+} -+ -+static ssize_t -+show_temp_alarm_otf(struct device *dev, struct device_attribute *da, char *buf) -+{ -+ struct tc654 *tc = tc654_update_device(dev); -+ -+ return sprintf(buf, "%d\n", tc->alarms[TC654_ALARM_OVER_TEMPERATURE]); -+} -+ -+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, -+ set_fan_min, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_fan_min_alarm, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, show_fan_max_alarm, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, -+ NULL, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan1_pulses, S_IRUGO | S_IWUSR, show_fan_pulses, -+ set_fan_pulses, TC654_FAN1); -+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, show_fan_min, -+ set_fan_min, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_min_alarm, S_IRUGO, show_fan_min_alarm, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_max_alarm, S_IRUGO, show_fan_max_alarm, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, -+ NULL, TC654_FAN2); -+static SENSOR_DEVICE_ATTR(fan2_pulses, S_IRUGO | S_IWUSR, show_fan_pulses, -+ set_fan_pulses, TC654_FAN2); -+ -+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, -+ set_pwm_enable); -+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); -+ -+static DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_temp_alarm_otf, NULL); -+ -+/* sensors present on all models */ -+static struct attribute *tc654_attrs[] = { -+ &sensor_dev_attr_fan1_input.dev_attr.attr, -+ &sensor_dev_attr_fan1_min.dev_attr.attr, -+ &sensor_dev_attr_fan1_max.dev_attr.attr, -+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan1_max_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan1_fault.dev_attr.attr, -+ &sensor_dev_attr_fan1_pulses.dev_attr.attr, -+ &sensor_dev_attr_fan2_input.dev_attr.attr, -+ &sensor_dev_attr_fan2_min.dev_attr.attr, -+ &sensor_dev_attr_fan2_max.dev_attr.attr, -+ &sensor_dev_attr_fan2_min_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan2_max_alarm.dev_attr.attr, -+ &sensor_dev_attr_fan2_fault.dev_attr.attr, -+ &sensor_dev_attr_fan2_pulses.dev_attr.attr, -+ -+ &dev_attr_pwm1_enable.attr, -+ &dev_attr_pwm1.attr, -+ -+ &dev_attr_temp1_emergency_alarm.attr, -+ NULL -+}; -+ -+ATTRIBUTE_GROUPS(tc654); -+ -+/* cooling device */ -+ -+static int tc654_get_max_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ *state = 255; -+ return 0; -+} -+ -+static int tc654_get_cur_state(struct thermal_cooling_device *cdev, -+ unsigned long *state) -+{ -+ struct tc654 *tc = cdev->devdata; -+ int ret; -+ -+ if (!tc) -+ return -EINVAL; -+ -+ ret = get_fan_speed(tc); -+ if (ret < 0) -+ return ret; -+ -+ *state = ret; -+ return 0; -+} -+ -+static int tc654_set_cur_state(struct thermal_cooling_device *cdev, -+ unsigned long state) -+{ -+ struct tc654 *tc = cdev->devdata; -+ -+ if (!tc) -+ return -EINVAL; -+ -+ if (state > INT_MAX) -+ return -EINVAL; -+ -+ return set_fan_speed(tc, state); -+} -+ -+static const struct thermal_cooling_device_ops tc654_fan_cool_ops = { -+ .get_max_state = tc654_get_max_state, -+ .get_cur_state = tc654_get_cur_state, -+ .set_cur_state = tc654_set_cur_state, -+}; -+ -+ -+/* hardware probe and detection */ -+ -+static int -+tc654_probe(struct i2c_client *client, const struct i2c_device_id *id) -+{ -+ struct tc654 *tc; -+ struct device *hwmon_dev; -+ int ret, i; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return -EIO; -+ -+ tc = devm_kzalloc(&client->dev, sizeof(*tc), GFP_KERNEL); -+ if (!tc) -+ return -ENOMEM; -+ -+ i2c_set_clientdata(client, tc); -+ tc->client = client; -+ mutex_init(&tc->update_lock); -+ -+ /* cache all 8 registers */ -+ for (i = 0; i < __TC654_REG_NUM; i++) { -+ ret = read_tc(tc, i); -+ if (ret < 0) -+ return ret; -+ } -+ -+ /* sysfs hooks */ -+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, -+ client->name, tc, -+ tc654_groups); -+ if (IS_ERR(hwmon_dev)) -+ return PTR_ERR(hwmon_dev); -+ -+#if IS_ENABLED(CONFIG_OF) -+ /* Optional cooling device register for Device tree platforms */ -+ tc->cdev = thermal_of_cooling_device_register(client->dev.of_node, -+ "tc654", tc, -+ &tc654_fan_cool_ops); -+#else /* CONFIG_OF */ -+ /* Optional cooling device register for non Device tree platforms */ -+ tc->cdev = thermal_cooling_device_register("tc654", tc, -+ &tc654_fan_cool_ops); -+#endif /* CONFIG_OF */ -+ -+ dev_info(&client->dev, "%s: sensor '%s'\n", -+ dev_name(hwmon_dev), client->name); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id tc654_ids[] = { -+ { "tc654", 0, }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, tc654_ids); -+ -+/* Return 0 if detection is successful, -ENODEV otherwise */ -+static int -+tc654_detect(struct i2c_client *new_client, struct i2c_board_info *info) -+{ -+ struct i2c_adapter *adapter = new_client->adapter; -+ int manufacturer, product; -+ -+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return -ENODEV; -+ -+ manufacturer = i2c_smbus_read_byte_data(new_client, TC654_REG_MFR_ID); -+ if (manufacturer != TC654_MFR_ID_MICROCHIP) -+ return -ENODEV; -+ -+ product = i2c_smbus_read_byte_data(new_client, TC654_REG_VER_ID); -+ if (!((product == TC654_VER_ID) || (product == TC655_VER_ID))) -+ return -ENODEV; -+ -+ strlcpy(info->type, "tc654", I2C_NAME_SIZE); -+ return 0; -+} -+ -+static struct i2c_driver tc654_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "tc654", -+ }, -+ .probe = tc654_probe, -+ .id_table = tc654_ids, -+ .detect = tc654_detect, -+ .address_list = normal_i2c, -+}; -+ -+module_i2c_driver(tc654_driver); -+ -+MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>"); -+MODULE_DESCRIPTION("Microchip TC654/TC655 hwmon driver"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/apm821xx/patches-4.4/911-hwmon-lm90-split-set-and-show-temp-as-common-codes.patch b/target/linux/apm821xx/patches-4.4/911-hwmon-lm90-split-set-and-show-temp-as-common-codes.patch deleted file mode 100644 index e450f9045f..0000000000 --- a/target/linux/apm821xx/patches-4.4/911-hwmon-lm90-split-set-and-show-temp-as-common-codes.patch +++ /dev/null @@ -1,292 +0,0 @@ -From: Wei Ni <wni@nvidia.com> -Subject: hwmon: lm90: split set and show temp as common codes - -Split set and show temp codes as common functions, so we can use -it directly when implement linux thermal framework. -And handle error return value for the lm90_select_remote_channel -and write_tempx, then set_temp8 and set_temp11 could return it -to user-space. - -Discussed in: -http://www.spinics.net/lists/linux-tegra/msg14020.html . -Applied with Jean. - -BUG=chrome-os-partner:30834 -TEST=None - -Signed-off-by: Wei Ni <wni@nvidia.com> -Signed-off-by: Jean Delvare <khali@linux-fr.org> -Reviewed-on: https://chromium-review.googlesource.com/175114 -Tested-by: Wei Ni <wni.nvidia@gmail.com> -Commit-Queue: Dylan Reid <dgreid@chromium.org> -Reviewed-by: Dylan Reid <dgreid@chromium.org> -(cherry picked from commit 614a96decdc7a3784128c9f21c5471367e2c627d) -Change-Id: Idbe3948812c6737cba94810cd147c29cc527c3cf -Reviewed-on: https://chromium-review.googlesource.com/212413 -Reviewed-by: Olof Johansson <olofj@chromium.org> -Commit-Queue: Olof Johansson <olofj@chromium.org> ---- ---- a/drivers/hwmon/lm90.c -+++ b/drivers/hwmon/lm90.c -@@ -473,20 +473,29 @@ static int lm90_read16(struct i2c_client - * various registers have different meanings as a result of selecting a - * non-default remote channel. - */ --static inline void lm90_select_remote_channel(struct i2c_client *client, -- struct lm90_data *data, -- int channel) -+static inline int lm90_select_remote_channel(struct i2c_client *client, -+ struct lm90_data *data, -+ int channel) - { - u8 config; -+ int err; - - if (data->kind == max6696) { - lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); - config &= ~0x08; - if (channel) - config |= 0x08; -- i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, -- config); -+ err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, -+ config); -+ if (err < 0) { -+ dev_err(&client->dev, -+ "Failed to select remote channel %d, err %d\n", -+ channel, err); -+ return err; -+ } - } -+ -+ return 0; - } - - /* -@@ -759,29 +768,34 @@ static u16 temp_to_u16_adt7461(struct lm - * Sysfs stuff - */ - --static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, -- char *buf) -+static int read_temp8(struct device *dev, int index) - { -- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - int temp; - - if (data->kind == adt7461 || data->kind == tmp451) -- temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); -+ temp = temp_from_u8_adt7461(data, data->temp8[index]); - else if (data->kind == max6646) -- temp = temp_from_u8(data->temp8[attr->index]); -+ temp = temp_from_u8(data->temp8[index]); - else -- temp = temp_from_s8(data->temp8[attr->index]); -+ temp = temp_from_s8(data->temp8[index]); - - /* +16 degrees offset for temp2 for the LM99 */ -- if (data->kind == lm99 && attr->index == 3) -+ if (data->kind == lm99 && index == 3) - temp += 16000; - -- return sprintf(buf, "%d\n", temp); -+ return temp; - } - --static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, -- const char *buf, size_t count) -+static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -+ -+ return sprintf(buf, "%d\n", read_temp8(dev, attr->index)); -+} -+ -+static int write_temp8(struct device *dev, int index, long val) - { - static const u8 reg[TEMP8_REG_NUM] = { - LM90_REG_W_LOCAL_LOW, -@@ -794,60 +808,79 @@ static ssize_t set_temp8(struct device * - MAX6659_REG_W_REMOTE_EMERG, - }; - -- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; -- int nr = attr->index; -- long val; - int err; - -- err = kstrtol(buf, 10, &val); -- if (err < 0) -- return err; -- - /* +16 degrees offset for temp2 for the LM99 */ -- if (data->kind == lm99 && attr->index == 3) -+ if (data->kind == lm99 && index == 3) - val -= 16000; - - mutex_lock(&data->update_lock); - if (data->kind == adt7461 || data->kind == tmp451) -- data->temp8[nr] = temp_to_u8_adt7461(data, val); -+ data->temp8[index] = temp_to_u8_adt7461(data, val); - else if (data->kind == max6646) -- data->temp8[nr] = temp_to_u8(val); -+ data->temp8[index] = temp_to_u8(val); - else -- data->temp8[nr] = temp_to_s8(val); -- -- lm90_select_remote_channel(client, data, nr >= 6); -- i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); -- lm90_select_remote_channel(client, data, 0); -+ data->temp8[index] = temp_to_s8(val); - -+ if ((err = lm90_select_remote_channel(client, data, index >= 6)) || -+ (err = i2c_smbus_write_byte_data(client, reg[index], -+ data->temp8[index])) || -+ (err = lm90_select_remote_channel(client, data, 0))) -+ dev_err(dev, "write_temp8 failed, err %d\n", err); - mutex_unlock(&data->update_lock); -+ -+ return err; -+} -+ -+static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -+ int index = attr->index; -+ long val; -+ int err; -+ -+ err = kstrtol(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ err = write_temp8(dev, index, val); -+ if (err < 0) -+ return err; -+ - return count; - } - --static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, -- char *buf) -+static int read_temp11(struct device *dev, int index) - { -- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); - struct lm90_data *data = lm90_update_device(dev); - int temp; - - if (data->kind == adt7461 || data->kind == tmp451) -- temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); -+ temp = temp_from_u16_adt7461(data, data->temp11[index]); - else if (data->kind == max6646) -- temp = temp_from_u16(data->temp11[attr->index]); -+ temp = temp_from_u16(data->temp11[index]); - else -- temp = temp_from_s16(data->temp11[attr->index]); -+ temp = temp_from_s16(data->temp11[index]); - - /* +16 degrees offset for temp2 for the LM99 */ -- if (data->kind == lm99 && attr->index <= 2) -+ if (data->kind == lm99 && index <= 2) - temp += 16000; - -- return sprintf(buf, "%d\n", temp); -+ return temp; - } - --static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, -- const char *buf, size_t count) -+static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, -+ char *buf) -+{ -+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); -+ -+ return sprintf(buf, "%d\n", read_temp11(dev, attr->index)); -+} -+ -+static int write_temp11(struct device *dev, int nr, int index, long val) - { - struct { - u8 high; -@@ -861,18 +894,10 @@ static ssize_t set_temp11(struct device - { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } - }; - -- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); - struct lm90_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; -- int nr = attr->nr; -- int index = attr->index; -- long val; - int err; - -- err = kstrtol(buf, 10, &val); -- if (err < 0) -- return err; -- - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) - val -= 16000; -@@ -887,15 +912,50 @@ static ssize_t set_temp11(struct device - else - data->temp11[index] = temp_to_s8(val) << 8; - -- lm90_select_remote_channel(client, data, reg[nr].channel); -- i2c_smbus_write_byte_data(client, reg[nr].high, -- data->temp11[index] >> 8); -- if (data->flags & LM90_HAVE_REM_LIMIT_EXT) -- i2c_smbus_write_byte_data(client, reg[nr].low, -- data->temp11[index] & 0xff); -- lm90_select_remote_channel(client, data, 0); -+ err = lm90_select_remote_channel(client, data, reg[nr].channel); -+ if (err) -+ goto error; -+ -+ err = i2c_smbus_write_byte_data(client, reg[nr].high, -+ data->temp11[index] >> 8); -+ if (err) -+ goto error; -+ -+ if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { -+ err = i2c_smbus_write_byte_data(client, reg[nr].low, -+ data->temp11[index] & 0xff); -+ if (err) -+ goto error; -+ } -+ -+ err = lm90_select_remote_channel(client, data, 0); -+ -+error: -+ if (err) -+ dev_err(dev, "write_temp11 failed, err %d\n", err); - - mutex_unlock(&data->update_lock); -+ -+ return err; -+} -+ -+static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, -+ const char *buf, size_t count) -+{ -+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); -+ int nr = attr->nr; -+ int index = attr->index; -+ long val; -+ int err; -+ -+ err = kstrtol(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ err = write_temp11(dev, nr, index, val); -+ if (err < 0) -+ return err; -+ - return count; - } - diff --git a/target/linux/apm821xx/patches-4.4/912-hwmon-lm90-expose-to-thermal-fw-via-DT.patch b/target/linux/apm821xx/patches-4.4/912-hwmon-lm90-expose-to-thermal-fw-via-DT.patch deleted file mode 100644 index 9eb9ceeaac..0000000000 --- a/target/linux/apm821xx/patches-4.4/912-hwmon-lm90-expose-to-thermal-fw-via-DT.patch +++ /dev/null @@ -1,149 +0,0 @@ -From: Wei Ni <wni@nvidia.com> -Subject: hwmon: lm90: expose to thermal fw via DT nodes - -This patch adds to lm90 temperature sensor the possibility -to expose itself as thermal zone device, registered on the -thermal framework. - -The thermal zone is built only if a device tree node -describing a thermal zone for this sensor is present -inside the lm90 DT node. Otherwise, the driver behavior -will be the same. - -Discussed in: -http://www.gossamer-threads.com/lists/linux/kernel/1992853 - -BUG=chrome-os-partner:30834 -TEST=Verified. Build and boot up system. - -Signed-off-by: Wei Ni <wni@nvidia.com> -Reviewed-on: https://chromium-review.googlesource.com/181447 -Reviewed-by: Dylan Reid <dgreid@chromium.org> -Tested-by: Dylan Reid <dgreid@chromium.org> -Commit-Queue: Dylan Reid <dgreid@chromium.org> -Change-Id: Id356b94d7e8f4b49ec15e46b17a1fa2ff0cbf8cf -Reviewed-on: https://chromium-review.googlesource.com/212414 -Tested-by: Wei Ni <wni.nvidia@gmail.com> -Reviewed-by: Olof Johansson <olofj@chromium.org> -Commit-Queue: Olof Johansson <olofj@chromium.org> ---- ---- a/drivers/hwmon/lm90.c -+++ b/drivers/hwmon/lm90.c -@@ -96,6 +96,8 @@ - #include <linux/sysfs.h> - #include <linux/interrupt.h> - #include <linux/regulator/consumer.h> -+#include <linux/of.h> -+#include <linux/thermal.h> - - /* - * Addresses to scan -@@ -119,6 +121,13 @@ static const unsigned short normal_i2c[] - enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004, g781, tmp451 }; - -+enum sensor_id { -+ LOCAL = 0, -+ REMOTE, -+ REMOTE2, -+ SENSOR_ID_END, -+}; -+ - /* - * The LM90 registers - */ -@@ -368,6 +377,7 @@ struct lm90_data { - struct i2c_client *client; - struct device *hwmon_dev; - const struct attribute_group *groups[6]; -+ struct thermal_zone_device *tz[SENSOR_ID_END]; - struct mutex update_lock; - struct regulator *regulator; - char valid; /* zero until following fields are valid */ -@@ -880,6 +890,24 @@ static ssize_t show_temp11(struct device - return sprintf(buf, "%d\n", read_temp11(dev, attr->index)); - } - -+static int lm90_read_local_temp(void *dev, int *temp) -+{ -+ *temp = read_temp11(dev, 4); -+ return 0; -+} -+ -+static int lm90_read_remote_temp(void *dev, int *temp) -+{ -+ *temp = read_temp11(dev, 0); -+ return 0; -+} -+ -+static int lm90_read_remote2_temp(void *dev, int *temp) -+{ -+ *temp = read_temp11(dev, 5); -+ return 0; -+} -+ - static int write_temp11(struct device *dev, int nr, int index, long val) - { - struct { -@@ -1210,6 +1238,18 @@ static const struct attribute_group lm90 - .attrs = lm90_temp3_attributes, - }; - -+static const struct thermal_zone_of_device_ops local_temp_sensor = { -+ .get_temp = lm90_read_local_temp, -+}; -+ -+static const struct thermal_zone_of_device_ops remote_temp_sensor = { -+ .get_temp = lm90_read_remote_temp, -+}; -+ -+static const struct thermal_zone_of_device_ops remote2_temp_sensor = { -+ .get_temp = lm90_read_remote2_temp, -+}; -+ - /* pec used for ADM1032 only */ - static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, - char *buf) -@@ -1659,6 +1699,30 @@ static int lm90_probe(struct i2c_client - } - } - -+ data->tz[LOCAL] = thermal_zone_of_sensor_register(&client->dev, -+ LOCAL, -+ &client->dev, -+ &local_temp_sensor); -+ if (IS_ERR(data->tz[LOCAL])) -+ data->tz[LOCAL] = NULL; -+ -+ data->tz[REMOTE] = thermal_zone_of_sensor_register(&client->dev, -+ REMOTE, -+ &client->dev, -+ &remote_temp_sensor); -+ if (IS_ERR(data->tz[REMOTE])) -+ data->tz[REMOTE] = NULL; -+ -+ if (data->flags & LM90_HAVE_TEMP3) { -+ data->tz[REMOTE2] = thermal_zone_of_sensor_register( -+ &client->dev, -+ REMOTE2, -+ &client->dev, -+ &remote2_temp_sensor); -+ if (IS_ERR(data->tz[REMOTE2])) -+ data->tz[REMOTE2] = NULL; -+ } -+ - return 0; - - exit_unregister: -@@ -1674,8 +1738,11 @@ exit_restore: - - static int lm90_remove(struct i2c_client *client) - { -+ int i; - struct lm90_data *data = i2c_get_clientdata(client); - -+ for (i = 0; i < SENSOR_ID_END; i++) -+ thermal_zone_of_sensor_unregister(&client->dev, data->tz[i]); - hwmon_device_unregister(data->hwmon_dev); - device_remove_file(&client->dev, &dev_attr_pec); - lm90_restore_conf(client, data); |