Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Bartlomiej Zolnierkiewicz @ 2018-06-13 16:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKv+Gu9+M8zOtACPQ4_UMgp-M1LfZnciYe1VTQNqn_zNWJea4g@mail.gmail.com>

On Wednesday, June 13, 2018 05:45:48 PM Ard Biesheuvel wrote:
> On 18 May 2018 at 16:17, Sinan Kaya <okaya@codeaurora.org> wrote:
> > A host bridge is allowed to remap BAR addresses using _TRA attribute in
> > _CRS windows.
> >
> > pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff window] (bus address [0x00100000-0x1fffffff])
> > pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
> >
> > When a VGA device is behind such a host bridge and the resource is
> > translated efifb driver is trying to do ioremap against bus address
> > rather than the resource address and is failing to probe.
> >
> > efifb: probing for efifb
> > efifb: cannot reserve video memory at 0x1e000000
> > efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
> > efifb: mode is 800x600x32, linelength=3200, pages=1
> > efifb: scrolling: redraw
> > efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
> >
> > Use the host bridge offset information to convert bus address to
> > resource address in the fixup.
> >
> > Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
> 
> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> 
> Bartlomiej, could you please take these via the fbdev tree for v4.19?

Sure, I will queue it after the current merge window.

> Peter already gave his ack but Sinan dropped it (presumably because of
> the split in v2)

Peter, can I (re)add your ACK to V2 patches?

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics

^ permalink raw reply

* [PATCH 0/2] mtd: rawnand: meson: add Amlogic NAND driver support
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

  These two patches try to add initial NAND driver support for Amlogic Meson
SoCs, current it has been tested on GXL(p212) and AXG(s400) platform.

Liang Yang (2):
  dt-bindings: nand: meson: add Amlogic NAND controller driver
  mtd: rawnand: meson: add support for Amlogic NAND flash controller

 .../bindings/mtd/amlogic,meson-nand.txt       |  118 ++
 drivers/mtd/nand/raw/Kconfig                  |    8 +
 drivers/mtd/nand/raw/Makefile                 |    3 +
 drivers/mtd/nand/raw/meson_nand.c             | 1422 +++++++++++++++++
 4 files changed, 1551 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
 create mode 100644 drivers/mtd/nand/raw/meson_nand.c

-- 
2.17.1

^ permalink raw reply

* [PATCH 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613161314.14894-1-yixun.lan@amlogic.com>

From: Liang Yang <liang.yang@amlogic.com>

Add Amlogic NAND controller dt-bindings for Meson SoC,
Current this driver support GXBB/GXL/AXG platform.

Signed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
 .../bindings/mtd/amlogic,meson-nand.txt       | 118 ++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
new file mode 100644
index 000000000000..eac9f9433d5d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
@@ -0,0 +1,118 @@
+Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
+
+This file documents the properties in addition to those available in
+the MTD NAND bindings.
+
+Required properties:
+- compatible : contains one of:
+  - "amlogic,meson-gxl-nfc"
+  - "amlogic,meson-axg-nfc"
+- clocks     :
+	A list of phandle + clock-specifier pairs for the clocks listed
+	in clock-names.
+
+- clock-names: Should contain the following:
+	"core" - NFC module gate clock
+	"clkin0" - Parent clock of internal mux
+	"clkin1" - Other parent clock of internal mux
+
+- pins     : Select pins which NFC need.
+- nand_pins: Detail NAND pins information.
+		nand_pins: nand {
+			mux {
+				groups = "emmc_nand_d0",
+					"emmc_nand_d1",
+					"emmc_nand_d2",
+					"emmc_nand_d3",
+					"emmc_nand_d4",
+					"emmc_nand_d5",
+					"emmc_nand_d6",
+					"emmc_nand_d7",
+					"nand_ce0",
+					"nand_rb0",
+					"nand_ale",
+					"nand_cle",
+					"nand_wen_clk",
+					"nand_ren_wr";
+				function = "nand";
+			};
+		};
+
+- amlogic,mmc-syscon	: Required for NAND clocks, it's shared with SD/eMMC
+				controller port C
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Optional properties:
+- meson-nand-user-mode :
+	only set 2 or 16 which mean the way of reading OOB bytes by NFC.
+- meson-nand-ran-mode :
+	setting 0 or 1, means disable/enable scrambler which keeps the balence
+	of 0 and 1
+
+Other properties:
+see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
+
+Example demonstrate on AXG SoC:
+
+	sd_emmc_c: mmc at 7000 {
+		compatible = "simple-bus", "syscon";
+		reg = <0x0 0x7000 0x0 0x800>;
+		status = "okay";
+	};
+
+	nand: nfc at 7800 {
+		compatible = "amlogic,meson-axg-nfc";
+		reg = <0x0 0x7800 0x0 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
+		status = "disabled";
+		clocks = <&clkc CLKID_SD_EMMC_C>,
+				<&clkc CLKID_SD_EMMC_C_CLK0>,
+				<&clkc CLKID_FCLK_DIV2>;
+		clock-names = "core", "clkin0", "clkin1";
+		amlogic,mmc-syscon = <&sd_mmc_c>;
+
+		status = "okay";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&nand_pins>;
+
+		nand at 0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			nand-on-flash-bbt;
+			nand-ecc-mode = "hw";
+			nand-ecc-strength = <8>;
+			nand-ecc-step-size = <1024>;
+
+			meson-nand-user-mode = <2>;
+			meson-nand-ran-mode = <1>;
+
+			partition at 0 {
+				label = "boot";
+				reg = <0x00000000 0x00200000>;
+				read-only;
+			};
+			partition at 200000 {
+				label = "env";
+				reg = <0x00200000 0x00400000>;
+			};
+			partition at 600000 {
+				label = "system";
+				reg = <0x00600000 0x00a00000>;
+			};
+			partition at 1000000 {
+				label = "rootfs";
+				reg = <0x01000000 0x03000000>;
+			};
+			partition at 4000000 {
+				label = "media";
+				reg = <0x04000000 0x8000000>;
+			};
+		};
+	};
-- 
2.17.1

^ permalink raw reply related

* [PATCH 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613161314.14894-1-yixun.lan@amlogic.com>

From: Liang Yang <liang.yang@amlogic.com>

Add initial support for the Amlogic NAND flash controller which found
in the Meson-GXBB/GXL/AXG SoCs.

Singed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
 drivers/mtd/nand/raw/Kconfig      |    8 +
 drivers/mtd/nand/raw/Makefile     |    3 +
 drivers/mtd/nand/raw/meson_nand.c | 1422 +++++++++++++++++++++++++++++
 3 files changed, 1433 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/meson_nand.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 19a2b283fbbe..b3c17a3ca8f4 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -534,4 +534,12 @@ config MTD_NAND_MTK
 	  Enables support for NAND controller on MTK SoCs.
 	  This controller is found on mt27xx, mt81xx, mt65xx SoCs.
 
+config MTD_NAND_MESON
+	tristate "Support for NAND flash controller on Amlogic's Meson SoCs"
+	depends on ARCH_MESON || COMPILE_TEST
+	select COMMON_CLK_REGMAP_MESON
+	select MFD_SYSCON
+	help
+	  Enables support for NAND controller on Amlogic's Meson SoCs.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 165b7ef9e9a1..cdf6162f38c3 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
+ccflags-$(CONFIG_MTD_NAND_MESON) += -I$(srctree)/drivers/clk/meson
+
 obj-$(CONFIG_MTD_NAND)			+= nand.o
 obj-$(CONFIG_MTD_NAND_ECC)		+= nand_ecc.o
 obj-$(CONFIG_MTD_NAND_BCH)		+= nand_bch.o
@@ -56,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
 obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
 obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
+obj-$(CONFIG_MTD_NAND_MESON)		+= meson_nand.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_amd.o
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
new file mode 100644
index 000000000000..28abc3684772
--- /dev/null
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -0,0 +1,1422 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson Nand Flash Controller Driver
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Liang Yang <liang.yang@amlogic.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "clk-regmap.h"
+
+#define NFC_REG_CMD		0x00
+#define NFC_REG_CFG		0x04
+#define NFC_REG_DADR		0x08
+#define NFC_REG_IADR		0x0c
+#define NFC_REG_BUF		0x10
+#define NFC_REG_INFO		0x14
+#define NFC_REG_DC		0x18
+#define NFC_REG_ADR		0x1c
+#define NFC_REG_DL		0x20
+#define NFC_REG_DH		0x24
+#define NFC_REG_CADR		0x28
+#define NFC_REG_SADR		0x2c
+#define NFC_REG_PINS		0x30
+#define NFC_REG_VER		0x38
+
+
+#define NFC_CMD_DRD		(0x8 << 14)
+#define NFC_CMD_IDLE		(0xc << 14)
+#define NFC_CMD_DWR		(0x4 << 14)
+#define NFC_CMD_CLE		(0x5 << 14)
+#define NFC_CMD_ALE		(0x6 << 14)
+#define NFC_CMD_ADL		((0 << 16) | (3 << 20))
+#define NFC_CMD_ADH		((1 << 16) | (3 << 20))
+#define NFC_CMD_AIL		((2 << 16) | (3 << 20))
+#define NFC_CMD_AIH		((3 << 16) | (3 << 20))
+#define NFC_CMD_SEED		((8 << 16) | (3 << 20))
+#define NFC_CMD_M2N		((0 << 17) | (2 << 20))
+#define NFC_CMD_N2M		((1 << 17) | (2 << 20))
+#define NFC_CMD_RB		(1 << 20)
+#define NFC_CMD_IO6		((0xb << 10) | (1 << 18))
+
+#define NFC_RB_USED		(1 << 23)
+#define NFC_LARGE_PAGE		(1 << 22)
+#define NFC_RW_OPS		(2 << 20)
+
+#define NAND_TWB_TIME_CYCLE	10
+
+#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
+	(								\
+		(cmd_dir)			|			\
+		((ran) << 19)			|			\
+		((bch) << 14)			| 			\
+		((short_mode) << 13)		|			\
+		(((page_size) & 0x7f) << 6)	|			\
+		((pages) & 0x3f)					\
+	)
+
+#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
+#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
+#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
+#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
+
+#define RB_STA(x)		(1 << (26 + x))
+
+#define ECC_CHECK_RETURN_FF	(-1)
+
+#define NAND_CE0		(0xe << 10)
+#define NAND_CE1		(0xd << 10)
+
+#define DMA_BUSY_TIMEOUT	0x100000
+
+#define MAX_CE_NUM		2
+#define RAN_ENABLE		1
+
+#define SD_EMMC_CLOCK 		0x00
+#define CLK_ALWAYS_ON		BIT(28)
+#define CLK_SELECT_NAND		BIT(31)
+#define CLK_DIV_MASK		GENMASK(5, 0)
+#define CLK_SRC_MASK		GENMASK(7, 6)
+
+#define NFC_CLK_CYCLE		6
+
+/* nand flash controller delay 3 ns */
+#define NFC_DEFAULT_DELAY 3000
+
+#define MAX_ECC_INDEX	10
+
+#define MUX_CLK_NUM_PARENTS 2
+
+struct meson_nfc_info_format {
+	u16 info_bytes;
+	u8 zero_cnt; /* bit0~5 is valid */
+	struct ecc_sta {
+		u8 eccerr_cnt : 6;
+		u8 notused : 1;
+		u8 completed : 1;
+	} ecc;
+	u32 reserved;
+};
+
+#define	PER_INFO_BYTE	(sizeof(struct meson_nfc_info_format))
+
+struct meson_nfc_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+	/*
+	 * Then NAND controller support two oob modes:
+	 *  a) 2 user bytes with each ecc page;
+	 *  b) 16 user bytes with 1st ecc page and zero user byte
+	 *      with the other ecc pages.
+	 * when using as mtd mode, the driver prefer to use 2 user bytes mode.
+	 */
+	int user_mode;
+	int rand_mode; /* 0: disable scramble, 1: enable scramble */
+	int bch_mode;
+	int cs;
+
+	u8 *data_buf;
+	u8 *info_buf;
+};
+
+/*
+ * While booting from NAND, a page0 data is needed to tell ROM boot code
+ * to read SPL image, and the ROM boot code need to know which ecc mode
+ * is selected and whether scramble is enabled or not, and so on.
+ *
+ * So when updating SPL image, the driver need to store these informations
+ * into the page0, and SPL image will be loadded into next page - the page1.
+ */
+struct meson_nand_setup {
+	u32 d32;
+	u16 id;
+	u16 max;
+};
+
+struct meson_nand_page0 {
+	struct meson_nand_setup nand_setup;
+	unsigned char page_list[16];
+	unsigned short reserved[32];
+};
+
+struct meson_nand_ecc {
+	int bch;
+	int strength;
+	int parity;
+};
+
+struct meson_nfc_data {
+	struct meson_nand_ecc *ecc;
+	int ecc_num;
+	int bch_mode;
+	int short_bch;
+};
+
+struct meson_nfc_param {
+	int chip_select;
+	int rb_select;
+
+	int page_size;
+	int oob_size;
+	int ecc_size;
+	int ecc_bytes;
+
+	int rand_mode;
+	int oob_mode;
+	int bch_mode;
+	int ecc_step;
+
+	int ecc_max;
+};
+
+struct meson_nfc {
+	struct nand_hw_control controller;
+	struct clk *core_clk;
+	struct clk *device_clk;
+
+	struct device *dev;
+	void __iomem *reg_base;
+	struct regmap *reg_clk;
+
+	struct completion completion;
+	struct list_head chips;
+	struct meson_nfc_data *data;
+	struct meson_nfc_param param;
+	struct meson_nand_page0 *page0;
+
+	u8 *data_buf;
+	u8 *info_buf;
+};
+
+enum {
+	NFC_ECC_NONE	= 0,
+	NFC_ECC_BCH8,		/* bch8 with ecc page size of 512B */
+	NFC_ECC_BCH8_1K,	/* bch8 with ecc page size of 1024B */
+	NFC_ECC_BCH24_1K,
+	NFC_ECC_BCH30_1K,
+	NFC_ECC_BCH40_1K,
+	NFC_ECC_BCH50_1K,
+	NFC_ECC_BCH60_1K,
+
+	/*
+	 * Short mode is special only for page 0 when inplement booting
+	 * from nand, which means a small size(384 bit / 8 = 48 Byte) of
+	 * ecc page is used with a fixed ecc mode. rom code will use short mode
+	 * to read page0 for getting nand parameters such as ecc, scramber, etc.
+	 *
+	 * Example, in GXL SoC, the first page adopt the short mode with
+	 * 60bit ecc, while in AXG SoC, it adopt short mode with 8bit ecc.
+	 */
+	NFC_ECC_BCH_SHORT,
+};
+
+enum {
+	NFC_USER2_OOB_BYTES		= 2,
+	NFC_USER16_OOB_BYTES		= 16,
+};
+
+#define MESON_ECC_DATA(b, s, p)				\
+	{ .bch = (b),	.strength = (s),	.parity = (p) }
+
+struct meson_nand_ecc meson_gxl_ecc[] = {
+	MESON_ECC_DATA(NFC_ECC_NONE, 0, 0),
+	MESON_ECC_DATA(NFC_ECC_BCH8, 8, 14),
+	MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 14),
+	MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24, 42),
+	MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30, 54),
+	MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40, 70),
+	MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50, 88),
+	MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60, 106),
+	MESON_ECC_DATA(NFC_ECC_BCH_SHORT, 0xff, 0xff),
+};
+
+struct meson_nand_ecc meson_axg_ecc[] = {
+	MESON_ECC_DATA(NFC_ECC_NONE, 0, 0),
+	MESON_ECC_DATA(NFC_ECC_BCH8, 8, 14),
+	MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 14),
+	MESON_ECC_DATA(NFC_ECC_BCH_SHORT, 0xff, 0xff),
+};
+
+static inline struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
+{
+	return container_of(nand, struct meson_nfc_nand_chip, nand);
+}
+
+static int meson_nfc_page0_gen(struct meson_nfc *nfc)
+{
+	u32 cmd;
+
+	nfc->page0 = devm_kzalloc(nfc->dev,
+		sizeof(struct meson_nand_page0), GFP_KERNEL);
+	if(!nfc->page0)
+		return -ENOMEM;
+
+	cmd = CMDRWGEN(NFC_CMD_N2M, nfc->param.rand_mode,
+		nfc->param.bch_mode, 0,
+		nfc->param.ecc_size >> 3,
+		nfc->param.ecc_step);
+	cmd |= NFC_RB_USED | NFC_LARGE_PAGE | NFC_RW_OPS;
+	nfc->page0->nand_setup.d32 = cmd;
+
+	return 0;
+}
+
+static void meson_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+	if (chip != meson_chip->cs)
+		return;
+
+	nfc->param.chip_select = chip ? NAND_CE1 : NAND_CE0;
+	nfc->param.rb_select = chip ? NAND_CE1 : NAND_CE0;
+	nfc->param.oob_mode =
+		(meson_chip->user_mode == NFC_USER2_OOB_BYTES) ? 0 : 1;
+	nfc->param.rand_mode = meson_chip->rand_mode;
+	nfc->param.bch_mode = meson_chip->bch_mode;
+
+	nfc->param.ecc_step = mtd->writesize / nand->ecc.size;
+	nfc->param.ecc_size = nand->ecc.size;
+	nfc->param.ecc_bytes =  nand->ecc.bytes;
+	nfc->param.page_size = mtd->writesize;
+	nfc->param.oob_size =  mtd->oobsize;
+	nfc->param.ecc_max = nand->ecc.strength;
+
+	nfc->data_buf = meson_chip->data_buf;
+	nfc->info_buf = meson_chip->info_buf;
+}
+
+static inline void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
+{
+	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
+			nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_ctrl(struct mtd_info *mtd,
+					int cmd, unsigned int ctrl)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	cmd = nfc->param.chip_select | (cmd & 0xff);
+	cmd |= (ctrl & NAND_CLE) ? NFC_CMD_CLE : NFC_CMD_ALE;
+
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static inline void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
+{
+	writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
+			nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_m2n(struct meson_nfc *nfc, int raw)
+{
+	u32 cmd, pagesize, pages, shortm = 0;
+	int bch = nfc->param.bch_mode;
+	int len = nfc->param.page_size;
+
+	pagesize = nfc->param.ecc_size;
+
+	if (unlikely(raw)) {
+		bch = NAND_ECC_NONE;
+		len = nfc->param.page_size + nfc->param.oob_size;
+		cmd = NFC_CMD_M2N |
+			(len & 0x3fff) | (nfc->param.rand_mode << 19);
+		writel(cmd, nfc->reg_base + NFC_REG_CMD);
+		return;
+	}
+
+	if (unlikely(bch == NFC_ECC_BCH_SHORT)) {
+		bch = nfc->data->short_bch;
+		pagesize = 384 >> 3;
+		pages = len / nfc->param.ecc_size;
+		memcpy(nfc->data_buf,
+			nfc->page0, sizeof(struct meson_nand_page0));
+		shortm = 1;
+	} else
+		pages = len / nfc->param.ecc_size;
+
+	cmd = CMDRWGEN(NFC_CMD_M2N,
+		nfc->param.rand_mode, bch, shortm, pagesize, pages);
+
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_n2m(struct meson_nfc *nfc, int raw)
+{
+	u32 cmd, pagesize, pages, shortm = 0;
+	int bch = nfc->param.bch_mode;
+	int len = nfc->param.page_size;
+
+	pagesize = nfc->param.ecc_size;
+
+	if (unlikely(raw)) {
+		bch = NAND_ECC_NONE;
+		len = nfc->param.page_size + nfc->param.oob_size;
+		cmd = (len & 0x3fff) | (nfc->param.rand_mode << 19) |
+			NFC_CMD_N2M;
+		writel(cmd, nfc->reg_base + NFC_REG_CMD);
+		return;
+	}
+
+	if (unlikely(bch == NFC_ECC_BCH_SHORT)) {
+		bch = nfc->data->short_bch;
+		pagesize = 384 >> 3;
+		pages = len / nfc->param.ecc_size;
+		shortm = 1;
+	} else
+		pages = len / nfc->param.ecc_size;
+
+	cmd = CMDRWGEN(NFC_CMD_N2M,
+		nfc->param.rand_mode, bch, shortm, pagesize, pages);
+
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
+					unsigned int timeout_ms)
+{
+	u32 cmd_size = 0;
+	int ret;
+
+	/* wait cmd fifo is empty */
+	ret = readl_poll_timeout(nfc->reg_base + NFC_REG_CMD,
+				cmd_size,
+				!((cmd_size >> 22) & 0x1f),
+				10, timeout_ms * 1000);
+	if (ret)
+		dev_err(nfc->dev, "wait for empty cmd FIFO time out\n");
+
+	return ret;
+}
+
+static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
+{
+	meson_nfc_cmd_idle(nfc, 0);
+	meson_nfc_cmd_idle(nfc, 0);
+
+	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
+}
+
+static inline struct meson_nfc_info_format *nfc_info_ptr(struct meson_nfc *nfc,
+	int index)
+{
+	return (struct meson_nfc_info_format *) &nfc->info_buf[index * 8];
+}
+
+static u8 *meson_nfc_oob_ptr(struct meson_nfc *nfc, int i)
+{
+	int x, len;
+	int ecc_bytes = nfc->param.ecc_bytes, temp = nfc->param.ecc_size;
+
+	x = i ? 16 : 0;
+	len = (nfc->param.oob_mode) ? (temp * (i + 1) + ecc_bytes * i + x) :
+				(temp * (i + 1) + (ecc_bytes + 2) * i);
+
+	return nfc->data_buf + len;
+}
+
+static u8 *meson_nfc_data_ptr(struct meson_nfc *nfc, int i)
+{
+	int len, x;
+	int temp = nfc->param.ecc_size + nfc->param.ecc_bytes;
+
+	x = i ? 16 : 0;
+	len = nfc->param.oob_mode ? (temp * i + x) : (temp + 2) * i;
+
+	return nfc->data_buf + len;
+}
+
+static void meson_nfc_prase_data_oob(struct meson_nfc *nfc, u8 *buf, u8 *oob)
+{
+	int i, oob_len = 0;
+	u8 *dsrc, *osrc;
+
+	for (i = 0; i < nfc->param.ecc_step; i++) {
+		if (buf) {
+			dsrc = meson_nfc_data_ptr(nfc, i);
+			memcpy(buf, dsrc, nfc->param.ecc_size);
+			buf += nfc->param.ecc_size;
+		}
+
+		if (nfc->param.oob_mode)
+			oob_len = (i) ? nfc->param.ecc_bytes :
+						nfc->param.ecc_bytes + 16;
+		else
+			oob_len = nfc->param.ecc_bytes + 2;
+
+		osrc = meson_nfc_oob_ptr(nfc, i);
+		memcpy(oob, osrc, oob_len);
+		oob += oob_len;
+	}
+}
+
+static void meson_nfc_format_data_oob(struct meson_nfc *nfc,
+						const u8 *buf, u8 *oob)
+{
+	int i, oob_len = 0;
+	u8 *dsrc, *osrc;
+
+	for (i = 0; i < nfc->param.ecc_step; i++) {
+		if (buf) {
+			dsrc = meson_nfc_data_ptr(nfc, i);
+			memcpy(dsrc, buf, nfc->param.ecc_size);
+			buf += nfc->param.ecc_size;
+		}
+
+		if (nfc->param.oob_mode)
+			oob_len = i ? nfc->param.ecc_bytes :
+						nfc->param.ecc_bytes + 16;
+		else
+			oob_len = nfc->param.ecc_bytes + 2;
+
+		osrc = meson_nfc_oob_ptr(nfc, i);
+		memcpy(osrc, oob, oob_len);
+		oob += oob_len;
+	}
+}
+
+static int meson_nfc_queue_rb(struct meson_nfc *nfc)
+{
+	u32 cmd, cfg;
+	int ret = 0;
+
+	init_completion(&nfc->completion);
+
+	cfg = readl(nfc->reg_base + NFC_REG_CFG);
+	cfg |= (1 << 21);
+	writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | (NAND_CMD_STATUS & 0xff);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+
+	cmd = NFC_CMD_RB | NFC_CMD_IO6 | (1 << 16) | (0x18 & 0x1f);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 2);
+
+	ret = wait_for_completion_timeout(&nfc->completion,
+					msecs_to_jiffies(1000));
+	if (ret == 0) {
+		dev_err(nfc->dev, "wait nand irq timeout\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static void meson_nfc_set_user_byte(struct mtd_info *mtd,
+					struct nand_chip *chip, u8 *oob_buf)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	struct meson_nfc_info_format *info;
+	int i, count;
+
+	if (nfc->param.oob_mode) {
+		memcpy(nfc->info_buf, oob_buf, 16);
+		return;
+	}
+
+	for (i = 0, count = 0; i < chip->ecc.steps; i++, count += 2) {
+		info = nfc_info_ptr(nfc, i);
+		info->info_bytes =
+			oob_buf[count] | (oob_buf[count + 1] << 8);
+	}
+}
+
+static void meson_nfc_get_user_byte(struct mtd_info *mtd,
+					struct nand_chip *chip, u8 *oob_buf)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	struct meson_nfc_info_format *info;
+	int i, count;
+
+	if (nfc->param.oob_mode) {
+		memcpy(oob_buf,  nfc->info_buf, 16);
+		return;
+	}
+
+	for (i = 0, count = 0; i < chip->ecc.steps; i++, count += 2) {
+		info = nfc_info_ptr(nfc, i);
+		oob_buf[count] = info->info_bytes & 0xff;
+		oob_buf[count + 1] = (info->info_bytes >> 8) & 0xff;
+	}
+}
+
+static int meson_nfc_ecc_correct(struct mtd_info *mtd,
+						struct nand_chip *chip)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	struct meson_nfc_info_format *info;
+	u32 bitflips = 0, i;
+	u8 zero_cnt;
+
+	for (i = 0; i < nfc->param.ecc_step; i++) {
+		info = nfc_info_ptr(nfc, i);
+		if (info->ecc.eccerr_cnt == 0x3f) {
+			zero_cnt = info->zero_cnt & 0x3f;
+			if (nfc->param.rand_mode
+				&& (zero_cnt < nfc->param.ecc_max))
+				return ECC_CHECK_RETURN_FF;
+			mtd->ecc_stats.failed++;
+			continue;
+		}
+		mtd->ecc_stats.corrected += info->ecc.eccerr_cnt;
+		bitflips = max_t(u32, bitflips, info->ecc.eccerr_cnt);
+	}
+
+	return bitflips;
+}
+
+static inline u8 meson_nfc_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	u32 cmd;
+
+	cmd = nfc->param.chip_select | NFC_CMD_DRD | 0;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_cmd_idle(nfc, 0);
+	meson_nfc_cmd_idle(nfc, 0);
+
+	meson_nfc_wait_cmd_finish(nfc, 1000);
+
+	return readb(nfc->reg_base + NFC_REG_BUF);
+}
+
+static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = meson_nfc_read_byte(mtd);
+}
+
+static void meson_nfc_write_byte(struct mtd_info *mtd, u8 byte)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+	u32 cmd;
+
+	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+
+	cmd = nfc->param.chip_select | NFC_CMD_DWR | (byte & 0xff);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+	meson_nfc_cmd_idle(nfc, 0);
+
+	meson_nfc_wait_cmd_finish(nfc, 1000);
+}
+
+static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		meson_nfc_write_byte(mtd, buf[i]);
+}
+
+static int meson_nfc_write_page_sub(struct mtd_info *mtd,
+		struct nand_chip *chip, const u8 *buf, int page, int raw)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	dma_addr_t daddr, iaddr;
+	u32 cmd;
+	int ret;
+
+	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+	daddr = dma_map_single(nfc->dev, (void *)nfc->data_buf,
+			mtd->writesize + mtd->oobsize, DMA_TO_DEVICE);
+	ret = dma_mapping_error(nfc->dev, daddr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	iaddr = dma_map_single(nfc->dev, (void *)nfc->info_buf,
+			nfc->param.ecc_step * PER_INFO_BYTE, DMA_TO_DEVICE);
+	ret = dma_mapping_error(nfc->dev, iaddr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	cmd = GENCMDDADDRL(NFC_CMD_ADL, daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	cmd = GENCMDDADDRH(NFC_CMD_ADH, daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	cmd = GENCMDIADDRL(NFC_CMD_AIL, iaddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	cmd = GENCMDIADDRH(NFC_CMD_AIH, iaddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_cmd_seed(nfc, page);
+
+	meson_nfc_cmd_m2n(nfc, raw);
+
+	ret = meson_nfc_wait_dma_finish(nfc);
+
+	dma_unmap_single(nfc->dev, daddr,
+			mtd->writesize + mtd->oobsize, DMA_TO_DEVICE);
+	dma_unmap_single(nfc->dev, iaddr,
+			nfc->param.ecc_step * PER_INFO_BYTE, DMA_TO_DEVICE);
+
+	return nand_prog_page_end_op(chip);
+}
+
+static int meson_nfc_write_page_raw(struct mtd_info *mtd,
+	struct nand_chip *chip, const u8 *buf, int oob_required, int page)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	u8 *oob_buf = chip->oob_poi;
+
+	meson_nfc_format_data_oob(nfc, buf, oob_buf);
+
+	return meson_nfc_write_page_sub(mtd, chip, nfc->data_buf, page, 1);
+}
+
+static int meson_nfc_write_page_hwecc(struct mtd_info *mtd,
+				    struct nand_chip *chip, const u8 *buf,
+				    int oob_required, int page)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	u8 *oob_buf = chip->oob_poi;
+
+	memcpy(nfc->data_buf, buf, mtd->writesize);
+	meson_nfc_set_user_byte(mtd, chip, oob_buf);
+
+	return meson_nfc_write_page_sub(mtd, chip, nfc->data_buf, page, 0);
+}
+
+static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, int raw)
+{
+	struct meson_nfc_info_format *info;
+	int neccpages, i;
+
+	neccpages = raw ? 1 : nfc->param.ecc_step;
+
+	for (i = 0; i < neccpages; i++) {
+		info = nfc_info_ptr(nfc, neccpages - 1);
+		if (info->ecc.completed == 0)
+			dev_err(nfc->dev, "seems eccpage is invalid\n");
+	}
+}
+
+static int meson_nfc_read_page_sub(struct mtd_info *mtd,
+	struct nand_chip *chip, const u8 *buf, int page, int raw)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	dma_addr_t daddr, iaddr;
+	u32 cmd;
+	int ret;
+
+	nand_read_page_op(chip, page, 0, NULL, 0);
+
+	daddr = dma_map_single(nfc->dev, nfc->data_buf,
+			mtd->writesize + mtd->oobsize, DMA_FROM_DEVICE);
+	ret = dma_mapping_error(nfc->dev, daddr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	iaddr = dma_map_single(nfc->dev, nfc->info_buf,
+			nfc->param.ecc_step * PER_INFO_BYTE, DMA_FROM_DEVICE);
+	ret = dma_mapping_error(nfc->dev, iaddr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	cmd = GENCMDDADDRL(NFC_CMD_ADL, daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	cmd = GENCMDDADDRH(NFC_CMD_ADH, daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	cmd = GENCMDIADDRL(NFC_CMD_AIL, iaddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	cmd = GENCMDIADDRH(NFC_CMD_AIH, iaddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_cmd_seed(nfc, page);
+
+	meson_nfc_cmd_n2m(nfc, raw);
+
+	ret = meson_nfc_wait_dma_finish(nfc);
+
+	meson_nfc_queue_rb(nfc);
+
+	meson_nfc_check_ecc_pages_valid(nfc, raw);
+
+	dma_unmap_single(nfc->dev, daddr,
+			mtd->writesize + mtd->oobsize, DMA_FROM_DEVICE);
+	dma_unmap_single(nfc->dev, iaddr,
+			nfc->param.ecc_step * PER_INFO_BYTE, DMA_FROM_DEVICE);
+
+	return ret;
+}
+
+static int meson_nfc_read_page_raw(struct mtd_info *mtd,
+	struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	u8 *oob_buf = chip->oob_poi;
+	int ret;
+
+	ret = meson_nfc_read_page_sub(mtd, chip, nfc->data_buf, page, 1);
+	if (ret)
+		return ret;
+
+	meson_nfc_prase_data_oob(nfc, buf, oob_buf);
+
+	return 0;
+}
+
+static int meson_nfc_read_page_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	u8 *oob_buf = chip->oob_poi;
+	int ret;
+
+	ret = meson_nfc_read_page_sub(mtd, chip, nfc->data_buf, page, 0);
+	if (ret)
+		return ret;
+
+	meson_nfc_get_user_byte(mtd, chip, oob_buf);
+
+	ret = meson_nfc_ecc_correct(mtd, chip);
+	if (ret == ECC_CHECK_RETURN_FF) {
+		if (buf)
+			memset(buf, 0xff, mtd->writesize);
+
+		memset(oob_buf, 0xff, mtd->oobsize);
+		return 0;
+	}
+	if (buf && (buf != nfc->data_buf))
+		memcpy(buf, nfc->data_buf, mtd->writesize);
+
+	return ret;
+}
+
+static int meson_nfc_read_oob_raw(struct mtd_info *mtd,
+				struct nand_chip *chip, int page)
+{
+	return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+				int page)
+{
+	return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page);
+}
+
+static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
+				  struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+	int free_oob;
+
+	if (section > chip->ecc.steps)
+		return -ERANGE;
+
+	free_oob = nfc->param.oob_mode ? 16 : (chip->ecc.steps * 2);
+	oobregion->offset = section * chip->ecc.bytes + free_oob;
+	oobregion->length = chip->ecc.bytes;
+
+	return 0;
+}
+
+static int meson_ooblayout_free(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct meson_nfc *nfc = nand_get_controller_data(chip);
+
+	if (section > chip->ecc.steps)
+		return -ERANGE;
+
+	oobregion->offset = 0;
+	oobregion->length = nfc->param.oob_mode ? 16 : (chip->ecc.steps * 2);
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
+	.ecc = meson_ooblayout_ecc,
+	.free = meson_ooblayout_free,
+};
+
+static int meson_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	struct meson_nand_ecc *meson_ecc = nfc->data->ecc;
+	int num = nfc->data->ecc_num;
+	int nsectors, i, bytes;
+
+	/* support only ecc hw mode */
+	if (nand->ecc.mode != NAND_ECC_HW) {
+		dev_err(dev, "ecc.mode not supported\n");
+		return -EINVAL;
+	}
+
+	if (!nand->ecc.size || !nand->ecc.strength) {
+		/* use datasheet requirements */
+		nand->ecc.strength = nand->ecc_strength_ds;
+		nand->ecc.size = nand->ecc_step_ds;
+	}
+
+	if (nand->ecc.options & NAND_ECC_MAXIMIZE) {
+		nand->ecc.size = 1024;
+		nsectors = mtd->writesize / nand->ecc.size;
+
+		/* Reserve 2 bytes for each ecc page */
+		if (meson_chip->user_mode == NFC_USER2_OOB_BYTES)
+			bytes = mtd->oobsize - 2 * nsectors;
+		else
+			bytes = mtd->oobsize - 16;
+
+		bytes /= nsectors;
+
+		/* and bytes has to be even. */
+		if (bytes % 2)
+			bytes--;
+
+		nand->ecc.strength = bytes * 8 / fls(8 * nand->ecc.size);
+	} else {
+		if (nand->ecc.strength > meson_ecc[num - 1].strength) {
+			dev_err(dev, "not support ecc strength\n");
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < num; i++) {
+		if ((meson_ecc[i].strength == 0xff)
+			|| (nand->ecc.strength < meson_ecc[i].strength))
+			break;
+	}
+
+	if (!i) {
+		nand->ecc.strength = 0;
+	} else {
+		nand->ecc.strength = meson_ecc[i - 1].strength;
+		nand->ecc.bytes = meson_ecc[i - 1].parity;
+	}
+
+	meson_chip->bch_mode = meson_ecc[i - 1].bch;
+
+	if (nand->ecc.size != 512 && nand->ecc.size != 1024)
+		return -EINVAL;
+
+	nsectors = mtd->writesize / nand->ecc.size;
+	bytes =(meson_chip->user_mode == NFC_USER2_OOB_BYTES) ? nsectors * 2 : 16;
+	if (mtd->oobsize < (nand->ecc.bytes * nsectors + bytes))
+		return -EINVAL;
+
+	return 0;
+}
+
+static const char * sd_emmc_ext_clk0_parent_names[MUX_CLK_NUM_PARENTS];
+
+static struct clk_regmap sd_emmc_c_ext_clk0_sel = {
+	.data = &(struct clk_regmap_mux_data){
+		.offset = SD_EMMC_CLOCK,
+		.mask = 0x3,
+		.shift = 6,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "sd_emmc_c_nand_clk_mux",
+		.ops = &clk_regmap_mux_ops,
+		.parent_names = sd_emmc_ext_clk0_parent_names,
+		.num_parents = ARRAY_SIZE(sd_emmc_ext_clk0_parent_names),
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap sd_emmc_c_ext_clk0_div = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = SD_EMMC_CLOCK,
+		.shift = 0,
+		.width = 6,
+		.flags = CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ONE_BASED,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "sd_emmc_c_nand_clk_div",
+		.ops = &clk_regmap_divider_ops,
+		.parent_names = (const char *[]){ "sd_emmc_c_nand_clk_mux" },
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static int meson_nfc_clk_init(struct meson_nfc *nfc)
+{
+	struct clk_regmap *mux = &sd_emmc_c_ext_clk0_sel;
+	struct clk_regmap *div = &sd_emmc_c_ext_clk0_div;
+	struct clk *clk;
+	int i, ret;
+
+	/* request core clock */
+	nfc->core_clk = devm_clk_get(nfc->dev, "core");
+	if (IS_ERR(nfc->core_clk)) {
+		dev_err(nfc->dev, "failed to get core clk\n");
+		return PTR_ERR(nfc->core_clk);
+	}
+
+	/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
+	regmap_update_bits(nfc->reg_clk, 0,
+			CLK_SELECT_NAND | CLK_ALWAYS_ON | CLK_DIV_MASK,
+			CLK_SELECT_NAND | CLK_ALWAYS_ON | CLK_DIV_MASK);
+
+	/* get the mux parents */
+	for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
+		char name[16];
+
+		snprintf(name, sizeof(name), "clkin%d", i);
+		clk = devm_clk_get(nfc->dev, name);
+		if (IS_ERR(clk)) {
+			if (clk != ERR_PTR(-EPROBE_DEFER))
+				dev_err(nfc->dev, "Missing clock %s\n", name);
+			return PTR_ERR(clk);
+		}
+
+		sd_emmc_ext_clk0_parent_names[i] = __clk_get_name(clk);
+	}
+
+	mux->map = nfc->reg_clk;
+	clk = devm_clk_register(nfc->dev, &mux->hw);
+	if (WARN_ON(IS_ERR(clk)))
+		return PTR_ERR(clk);
+
+	div->map = nfc->reg_clk;
+	nfc->device_clk = devm_clk_register(nfc->dev, &div->hw);
+	if (WARN_ON(IS_ERR(nfc->device_clk)))
+		return PTR_ERR(nfc->device_clk);
+
+	ret = clk_prepare_enable(nfc->core_clk);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable core clk\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(nfc->device_clk);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable device clk\n");
+		clk_disable_unprepare(nfc->core_clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void meson_nfc_disable_clk(struct meson_nfc *nfc)
+{
+	clk_disable_unprepare(nfc->device_clk);
+	clk_disable_unprepare(nfc->core_clk);
+}
+
+static int meson_nfc_buffer_init(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	struct device *dev = nfc->dev;
+	int info_bytes, page_bytes;
+	int nsectors;
+
+	nsectors = mtd->writesize / nand->ecc.size;
+	info_bytes = nsectors * PER_INFO_BYTE;
+	page_bytes = mtd->writesize + mtd->oobsize;
+
+	if ((meson_chip->data_buf) && (meson_chip->info_buf))
+		return 0;
+
+	meson_chip->data_buf = devm_kzalloc(dev, page_bytes, GFP_KERNEL);
+	if (!meson_chip->data_buf)
+		return  -ENOMEM;
+
+	meson_chip->info_buf = devm_kzalloc(dev, info_bytes, GFP_KERNEL);
+	if (!meson_chip->info_buf)
+		return  -ENOMEM;
+
+	return 0;
+}
+
+static int meson_nfc_calc_set_timing(struct meson_nfc *nfc,
+				int rc_min, int rea_max, int rhoh_min)
+{
+	int div, bt_min, bt_max, bus_timing;
+	int ret;
+
+	div = DIV_ROUND_UP((rc_min / 1000), NFC_CLK_CYCLE);
+	ret = clk_set_rate(nfc->device_clk, 1000000000 / div);
+	if (ret) {
+		dev_err(nfc->dev, "failed to set nand clock rate\n");
+		return ret;
+	}
+
+	bt_min = (rea_max + NFC_DEFAULT_DELAY) / div;
+	bt_max = (NFC_DEFAULT_DELAY + rhoh_min + rc_min / 2) / div;
+
+	bt_min = DIV_ROUND_UP(bt_min, 1000);
+	bt_max = DIV_ROUND_UP(bt_max, 1000);
+
+	if (bt_max < bt_min)
+		return -EINVAL;
+
+	bus_timing = (bt_min + bt_max) / 2 + 1;
+
+	writel((1 << 21), nfc->reg_base + NFC_REG_CFG);
+	writel((NFC_CLK_CYCLE - 1) | (bus_timing << 5),
+				nfc->reg_base + NFC_REG_CFG);
+
+	writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
+
+	return 0;
+}
+
+static int meson_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
+				const struct nand_data_interface *conf)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	const struct nand_sdr_timings *timings;
+
+	timings = nand_get_sdr_timings(conf);
+	if (IS_ERR(timings))
+		return -ENOTSUPP;
+
+	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+		return 0;
+
+	meson_nfc_calc_set_timing(nfc, timings->tRC_min,
+				timings->tREA_max, timings->tRHOH_min);
+
+	return 0;
+}
+
+static int meson_nfc_get_nand_chip_dts(struct meson_nfc *nfc,
+		struct meson_nfc_nand_chip *chip, struct device_node *np)
+{
+	struct device *dev = nfc->dev;
+
+	if (of_property_read_u32(np, "reg", &chip->cs)) {
+		dev_err(dev, "can not get ce number\n");
+		return -EINVAL;
+	}
+
+	if (chip->cs > MAX_CE_NUM) {
+		dev_err(dev, "ce number is beyond\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(np, "meson-nand-user-mode", &chip->user_mode)) {
+		dev_err(dev, "can not get user oob mode\n");
+		return -EINVAL;
+	}
+
+	if ((chip->user_mode != NFC_USER2_OOB_BYTES)
+			|| (chip->user_mode != NFC_USER16_OOB_BYTES))
+		chip->user_mode = NFC_USER2_OOB_BYTES;
+
+	if (of_property_read_u32(np, "meson-nand-ran-mode", &chip->rand_mode)) {
+		dev_err(dev, "can not get scramble mode\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int meson_nfc_nand_chip_init(struct device *dev, struct meson_nfc *nfc,
+				  struct device_node *np)
+{
+	struct meson_nfc_nand_chip *chip;
+	struct nand_chip *nand;
+	struct mtd_info *mtd;
+	int ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	ret = meson_nfc_get_nand_chip_dts(nfc, chip, np);
+	if (ret)
+		return ret;
+
+	nand = &chip->nand;
+	nand_set_flash_node(nand, np);
+	nand_set_controller_data(nand, nfc);
+
+	nand->options |= NAND_USE_BOUNCE_BUFFER;
+	nand->select_chip = meson_nfc_select_chip;
+	nand->write_byte = meson_nfc_write_byte;
+	nand->write_buf = meson_nfc_write_buf;
+	nand->read_byte = meson_nfc_read_byte;
+	nand->read_buf = meson_nfc_read_buf;
+	nand->cmd_ctrl = meson_nfc_cmd_ctrl;
+	nand->setup_data_interface = meson_nfc_setup_data_interface;
+
+	nand->chip_delay = 200;
+	nand->ecc.mode = NAND_ECC_HW;
+
+	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
+	nand->ecc.write_page = meson_nfc_write_page_hwecc;
+	nand->ecc.write_oob_raw = nand_write_oob_std;
+	nand->ecc.write_oob = nand_write_oob_std;
+
+	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
+	nand->ecc.read_page = meson_nfc_read_page_hwecc;
+	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
+	nand->ecc.read_oob = meson_nfc_read_oob;
+
+	mtd = nand_to_mtd(nand);
+	mtd->owner = THIS_MODULE;
+	mtd->dev.parent = dev;
+	mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
+				   "%s:nand", dev_name(dev));
+	if (!mtd->name) {
+		dev_err(nfc->dev, "Failed to allocate mtd->name\n");
+		return -ENOMEM;
+	}
+
+	mtd_set_ooblayout(mtd, &meson_ooblayout_ops);
+
+	ret = nand_scan_ident(mtd, 1, NULL);
+	if (ret) {
+		dev_err(dev, "failed to can ident\n");
+		return -ENODEV;
+	}
+
+	/* store bbt magic in page, cause OOB is not protected */
+	if (nand->bbt_options & NAND_BBT_USE_FLASH)
+		nand->bbt_options |= NAND_BBT_NO_OOB;
+
+	nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+	ret = meson_nfc_ecc_init(dev, mtd);
+	if (ret) {
+		dev_err(dev, "failed to ecc init\n");
+		return -EINVAL;
+	}
+
+	if (nand->options & NAND_BUSWIDTH_16) {
+		dev_err(dev, "16bits buswidth not supported");
+		return -EINVAL;
+	}
+
+	ret = meson_nfc_buffer_init(mtd);
+	if (ret)
+		return -ENOMEM;
+
+	ret = nand_scan_tail(mtd);
+	if (ret)
+		return -ENODEV;
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device: %d\n", ret);
+		nand_release(mtd);
+		return ret;
+	}
+
+	list_add_tail(&chip->node, &nfc->chips);
+
+	return 0;
+}
+
+static int meson_nfc_nand_chips_init(struct device *dev, struct meson_nfc *nfc)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int ret;
+
+	for_each_child_of_node(np, nand_np) {
+		ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
+		if (ret) {
+			of_node_put(nand_np);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static irqreturn_t meson_nfc_irq(int irq, void *id)
+{
+	struct meson_nfc *nfc = id;
+	u32 cfg;
+
+	cfg = readl(nfc->reg_base + NFC_REG_CFG);
+	cfg |= (1 << 21);
+	writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+	complete(&nfc->completion);
+	return IRQ_HANDLED;
+}
+
+static const struct meson_nfc_data meson_gxl_data = {
+	.short_bch	= NFC_ECC_BCH60_1K,
+	.ecc		= meson_gxl_ecc,
+	.ecc_num	= ARRAY_SIZE(meson_gxl_ecc),
+};
+
+static const struct meson_nfc_data meson_axg_data = {
+	.short_bch	= NFC_ECC_BCH8_1K,
+	.ecc		= meson_axg_ecc,
+	.ecc_num	= ARRAY_SIZE(meson_axg_ecc),
+};
+
+static const struct of_device_id meson_nfc_id_table[] = {
+	{
+		.compatible = "amlogic,meson-gxl-nfc",
+		.data = &meson_gxl_data,
+	}, {
+		.compatible = "amlogic,meson-axg-nfc",
+		.data = &meson_axg_data,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
+
+static int meson_nfc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct meson_nfc *nfc;
+	struct resource *res;
+	const struct of_device_id *of_nfc_id;
+	int ret, irq;
+
+	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return -ENOMEM;
+
+	of_nfc_id = of_match_device(meson_nfc_id_table, &pdev->dev);
+	if (!of_nfc_id)
+		return -ENODEV;
+
+	nfc->data = (struct meson_nfc_data *)of_nfc_id->data;
+
+	spin_lock_init(&nfc->controller.lock);
+	init_waitqueue_head(&nfc->controller.wq);
+	INIT_LIST_HEAD(&nfc->chips);
+
+	nfc->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "Failed to nfc reg resource\n");
+		return -EINVAL;
+	}
+
+	nfc->reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(nfc->reg_base)) {
+		dev_err(dev, "Failed to lookup nfi reg base\n");
+		return PTR_ERR(nfc->reg_base);
+	}
+
+	nfc->reg_clk = syscon_regmap_lookup_by_phandle(dev->of_node,
+							"amlogic,mmc-syscon");
+	if (IS_ERR(nfc->reg_clk)) {
+		dev_err(dev, "Failed to lookup clock base\n");
+		return PTR_ERR(nfc->reg_clk);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no nfi irq resource\n");
+		return -EINVAL;
+	}
+
+	ret = meson_nfc_clk_init(nfc);
+	if (ret) {
+		dev_err(dev, "failed to initialize nand clk\n");
+		goto err_clk;
+	}
+
+	ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
+	if (ret) {
+		dev_err(dev, "failed to request nfi irq\n");
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "failed to set dma mask\n");
+		goto err_clk;
+	}
+
+	platform_set_drvdata(pdev, nfc);
+
+	ret = meson_nfc_nand_chips_init(dev, nfc);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto err_clk;
+	}
+
+	meson_nfc_page0_gen(nfc);
+	return 0;
+
+err_clk:
+	clk_disable_unprepare(nfc->device_clk);
+	clk_disable_unprepare(nfc->core_clk);
+
+	return ret;
+}
+
+static int meson_nfc_remove(struct platform_device *pdev)
+{
+	struct meson_nfc *nfc = platform_get_drvdata(pdev);
+	struct meson_nfc_nand_chip *chip;
+
+	while (!list_empty(&nfc->chips)) {
+		chip = list_first_entry(&nfc->chips, struct meson_nfc_nand_chip,
+					node);
+		nand_release(nand_to_mtd(&chip->nand));
+		list_del(&chip->node);
+	}
+
+	meson_nfc_disable_clk(nfc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver meson_nfc_driver = {
+	.probe  = meson_nfc_probe,
+	.remove = meson_nfc_remove,
+	.driver = {
+		.name  = "meson_nand",
+		.of_match_table = meson_nfc_id_table,
+	},
+};
+
+module_platform_driver(meson_nfc_driver);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");
-- 
2.17.1

^ permalink raw reply related

* [linux-sunxi] Re: [PATCH v2 14/27] drm/sun4i: Don't check for panel or bridge on TV TCONs
From: Jernej Škrabec @ 2018-06-13 16:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGb2v66QWr2CQhcfQ9BQds-0xeG=pS4MDzaNn7_HGz+q3gfkWA@mail.gmail.com>

Dne sreda, 13. junij 2018 ob 10:04:20 CEST je Chen-Yu Tsai napisal(a):
> On Wed, Jun 13, 2018 at 3:46 PM, Maxime Ripard
> 
> <maxime.ripard@bootlin.com> wrote:
> > On Tue, Jun 12, 2018 at 10:00:23PM +0200, Jernej Skrabec wrote:
> >> TV TCONs are always connected to TV or HDMI encoder, so it doesn't make
> >> sense to check if panel or bridge is connected to them.
> >> 
> >> Check if TCON has channel 0 and only then check for connected panel or
> >> bridges.
> >> 
> >> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> >> ---
> >> 
> >>  drivers/gpu/drm/sun4i/sun4i_tcon.c | 12 +++++++++---
> >>  1 file changed, 9 insertions(+), 3 deletions(-)
> >> 
> >> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> b/drivers/gpu/drm/sun4i/sun4i_tcon.c index b1205a7bc20f..c9ffa5381185
> >> 100644
> >> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> @@ -1189,13 +1189,19 @@ static const struct component_ops sun4i_tcon_ops
> >> = {>> 
> >>  static int sun4i_tcon_probe(struct platform_device *pdev)
> >>  {
> >>  
> >>       struct device_node *node = pdev->dev.of_node;
> >> 
> >> +     const struct sun4i_tcon_quirks *quirks;
> >> 
> >>       struct drm_bridge *bridge;
> >>       struct drm_panel *panel;
> >>       int ret;
> >> 
> >> -     ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
> >> -     if (ret == -EPROBE_DEFER)
> >> -             return ret;
> >> +     quirks = of_device_get_match_data(&pdev->dev);
> > 
> > We should probably check ofr the pointer value before dereferencing it.
> 
> I think we've discussed this before. If the driver has data structures
> for all the supported compatible strings, and it is device tree only,
> then we should just let it blow up in the user's face, since they are
> obviously doing something they shouldn't be doing to get the driver
> to probe without a compatible string match.

TCON can't work with no quirks specified, since that would mean that neither 
channels are present. Additionally, sun4i_tcon_bind() also doesn't check if 
quirks are NULL or not. So I concur with Chen-Yu here.

Best regards,
Jernej

^ permalink raw reply

* [PATCH v7 0/6] Driver for at91 usart in spi mode
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This is the second version of driver. I added a mfd driver which by
default probes atmel_serial driver and if in dt is specified to probe
the spi driver, then the spi-at91-usart driver will be probed. The
compatible for atmel_serial is now the compatible for at91-usart mfd
driver and compatilbe for atmel_serial driver was changed in order to
keep the bindings for serial as they are.

Changes in v7:
- synced up  SPDIX license with module license
- numbering of usart modes starts from 0 insteand of 1

Changes in v6:
- removed unused compatible strings from serial and spi drivers

Changes in v5:
- fixed usage of stdout-path property with atmel_serial driver

Changes in v4:
- modified the spi driver to use cs gpio support form spi subsystem
- fixed dma transfers for serial driver
- squashed binding for spi and serial and moved them to mfd/atmel-usart.txt

Changes in v3:
- fixed spi slaves probing

Changes in v2:
- added at91-usart mfd driver
- modified spi-at91-usart driver to work as mfd driver child
- modified atmel_serial driver to work as mfd driver child

Changes in v1:
- added spi-at91-usart driver

Radu Pirea (6):
  MAINTAINERS: add at91 usart mfd driver
  dt-bindings: add binding for atmel-usart in SPI mode
  mfd: at91-usart: added mfd driver for usart
  MAINTAINERS: add at91 usart spi driver
  spi: at91-usart: add driver for at91-usart as spi
  tty/serial: atmel: change the driver to work under at91-usart mfd

 .../bindings/{serial => mfd}/atmel-usart.txt  |  25 +-
 MAINTAINERS                                   |  16 +
 drivers/mfd/Kconfig                           |   9 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/at91-usart.c                      |  69 +++
 drivers/spi/Kconfig                           |   9 +
 drivers/spi/Makefile                          |   1 +
 drivers/spi/spi-at91-usart.c                  | 434 ++++++++++++++++++
 drivers/tty/serial/Kconfig                    |   1 +
 drivers/tty/serial/atmel_serial.c             |  42 +-
 include/dt-bindings/mfd/at91-usart.h          |  17 +
 11 files changed, 607 insertions(+), 17 deletions(-)
 rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
 create mode 100644 drivers/mfd/at91-usart.c
 create mode 100644 drivers/spi/spi-at91-usart.c
 create mode 100644 include/dt-bindings/mfd/at91-usart.h

-- 
2.17.1

^ permalink raw reply

* [PATCH v7 1/6] MAINTAINERS: add at91 usart mfd driver
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

Added entry for at91 usart mfd driver.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8e2a2fddbd19..12203d07c6af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9160,6 +9160,7 @@ M:	Richard Genoud <richard.genoud@gmail.com>
 S:	Maintained
 F:	drivers/tty/serial/atmel_serial.c
 F:	drivers/tty/serial/atmel_serial.h
+F:	Documentation/devicetree/bindings/mfd/atmel-usart.txt
 
 MICROCHIP / ATMEL DMA DRIVER
 M:	Ludovic Desroches <ludovic.desroches@microchip.com>
@@ -9192,6 +9193,14 @@ S:	Supported
 F:	drivers/mtd/nand/raw/atmel/*
 F:	Documentation/devicetree/bindings/mtd/atmel-nand.txt
 
+MICROCHIP AT91 USART MFD DRIVER
+M:	Radu Pirea <radu.pirea@microchip.com>
+L:	linux-kernel at vger.kernel.org
+S:	Supported
+F:	drivers/mfd/at91-usart.c
+F:	include/dt-bindings/mfd/at91-usart.h
+F:	Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
 MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 M:	Woojung Huh <Woojung.Huh@microchip.com>
 M:	Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
-- 
2.17.1

^ permalink raw reply related

* [PATCH v7 2/6] dt-bindings: add binding for atmel-usart in SPI mode
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

This patch moves the bindings for serial from serial/atmel-usart.txt to
mfd/atmel-usart.txt and adds bindings for USART in SPI mode.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/{serial => mfd}/atmel-usart.txt  | 25 +++++++++++++++++--
 include/dt-bindings/mfd/at91-usart.h          | 17 +++++++++++++
 2 files changed, 40 insertions(+), 2 deletions(-)
 rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
 create mode 100644 include/dt-bindings/mfd/at91-usart.h

diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
similarity index 76%
rename from Documentation/devicetree/bindings/serial/atmel-usart.txt
rename to Documentation/devicetree/bindings/mfd/atmel-usart.txt
index 7c0d6b2f53e4..3b9e18642c3b 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
@@ -1,6 +1,6 @@
 * Atmel Universal Synchronous Asynchronous Receiver/Transmitter (USART)
 
-Required properties:
+Required properties for USART:
 - compatible: Should be "atmel,<chip>-usart" or "atmel,<chip>-dbgu"
   The compatible <chip> indicated will be the first SoC to support an
   additional mode or an USART new feature.
@@ -11,7 +11,13 @@ Required properties:
 	Required elements: "usart"
 - clocks: phandles to input clocks.
 
-Optional properties:
+Required properties for USART in SPI mode:
+- #size-cells      : Must be <0>
+- #address-cells   : Must be <1>
+- cs-gpios: chipselects (internal cs not supported)
+- atmel,usart-mode : Must be <USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
+
+Optional properties in serial mode:
 - atmel,use-dma-rx: use of PDC or DMA for receiving data
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
 - {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
@@ -62,3 +68,18 @@ Example:
 		dma-names = "tx", "rx";
 		atmel,fifo-size = <32>;
 	};
+
+- SPI mode:
+	#include <dt-bindings/mfd/at91-usart.h>
+
+	spi0: spi at f001c000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "atmel,at91rm9200-usart", "atmel,at91sam9260-usart";
+		atmel,usart-mode = <USART_MODE_SPI>;
+		reg = <0xf001c000 0x100>;
+		interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&usart0_clk>;
+		clock-names = "usart";
+		cs-gpios = <&pioB 3 0>;
+	};
diff --git a/include/dt-bindings/mfd/at91-usart.h b/include/dt-bindings/mfd/at91-usart.h
new file mode 100644
index 000000000000..2de5bc312e1e
--- /dev/null
+++ b/include/dt-bindings/mfd/at91-usart.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides macros for AT91 USART DT bindings.
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#ifndef __DT_BINDINGS_AT91_USART_H__
+#define __DT_BINDINGS_AT91_USART_H__
+
+#define AT91_USART_MODE_SERIAL	0
+#define AT91_USART_MODE_SPI	1
+
+#endif /* __DT_BINDINGS_AT91_USART_H__ */
-- 
2.17.1

^ permalink raw reply related

* [PATCH v7 3/6] mfd: at91-usart: added mfd driver for usart
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

This mfd driver is just a wrapper over atmel_serial driver and
spi-at91-usart driver. Selection of one of the drivers is based on a
property from device tree. If the property is not specified, the default
driver is atmel_serial.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Acked-by: Rob Herring <robh@kernel.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig      |  9 ++++++
 drivers/mfd/Makefile     |  1 +
 drivers/mfd/at91-usart.c | 69 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 79 insertions(+)
 create mode 100644 drivers/mfd/at91-usart.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5aa194..a886672b960d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -99,6 +99,15 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_AT91_USART
+	tristate "AT91 USART Driver"
+	select MFD_CORE
+	help
+	  Select this to get support for AT91 USART IP. This is a wrapper
+	  over at91-usart-serial driver and usart-spi-driver. Only one function
+	  can be used at a time. The choice is done at boot time by the probe
+	  function of this MFD driver according to a device tree property.
+
 config MFD_ATMEL_FLEXCOM
 	tristate "Atmel Flexcom (Flexible Serial Communication Unit)"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d9d2cf0d32ef..db1332aa96db 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC)	+= qcom-spmi-pmic.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_AT91_USART)	+= at91-usart.o
 obj-$(CONFIG_MFD_ATMEL_FLEXCOM)	+= atmel-flexcom.o
 obj-$(CONFIG_MFD_ATMEL_HLCDC)	+= atmel-hlcdc.o
 obj-$(CONFIG_MFD_ATMEL_SMC)	+= atmel-smc.o
diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c
new file mode 100644
index 000000000000..3014ce532644
--- /dev/null
+++ b/drivers/mfd/at91-usart.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AT91 USART
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#include <dt-bindings/mfd/at91-usart.h>
+
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/property.h>
+
+static struct mfd_cell at91_usart_spi_subdev = {
+		.name = "at91_usart_spi",
+		.of_compatible = "microchip,at91sam9g45-usart-spi",
+	};
+
+static struct mfd_cell at91_usart_serial_subdev = {
+		.name = "atmel_usart_serial",
+		.of_compatible = "atmel,at91rm9200-usart-serial",
+	};
+
+static int at91_usart_mode_probe(struct platform_device *pdev)
+{
+	struct mfd_cell cell;
+	u32 opmode = AT91_USART_MODE_SERIAL;
+
+	device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode);
+
+	switch (opmode) {
+	case AT91_USART_MODE_SPI:
+		cell = at91_usart_spi_subdev;
+		break;
+	case AT91_USART_MODE_SERIAL:
+		cell = at91_usart_serial_subdev;
+		break;
+	default:
+		break;
+	}
+
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1,
+			      NULL, 0, NULL);
+}
+
+static const struct of_device_id at91_usart_mode_of_match[] = {
+	{ .compatible = "atmel,at91rm9200-usart" },
+	{ .compatible = "atmel,at91sam9260-usart" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_flexcom_of_match);
+
+static struct platform_driver at91_usart_mfd = {
+	.probe	= at91_usart_mode_probe,
+	.driver	= {
+		.name		= "at91_usart_mode",
+		.of_match_table	= at91_usart_mode_of_match,
+	},
+};
+
+module_platform_driver(at91_usart_mfd);
+
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_DESCRIPTION("AT91 USART MFD driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1

^ permalink raw reply related

* [PATCH v7 4/6] MAINTAINERS: add at91 usart spi driver
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

Added entry for at91 usart mfd driver.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 12203d07c6af..dae31df711fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9201,6 +9201,13 @@ F:	drivers/mfd/at91-usart.c
 F:	include/dt-bindings/mfd/at91-usart.h
 F:	Documentation/devicetree/bindings/mfd/atmel-usart.txt
 
+MICROCHIP AT91 USART SPI DRIVER
+M:	Radu Pirea <radu.pirea@microchip.com>
+L:	linux-spi at vger.kernel.org
+S:	Supported
+F:	drivers/spi/spi-at91-usart.c
+F:	Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
 MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 M:	Woojung Huh <Woojung.Huh@microchip.com>
 M:	Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
-- 
2.17.1

^ permalink raw reply related

* [PATCH v7 5/6] spi: at91-usart: add driver for at91-usart as spi
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

This is the driver for at91-usart in spi mode. The USART IP can be configured
to work in many modes and one of them is SPI.

The driver was tested on sama5d3-xplained and sama5d4-xplained boards with
enc28j60 ethernet controller as slave.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
 drivers/spi/Kconfig          |   9 +
 drivers/spi/Makefile         |   1 +
 drivers/spi/spi-at91-usart.c | 434 +++++++++++++++++++++++++++++++++++
 3 files changed, 444 insertions(+)
 create mode 100644 drivers/spi/spi-at91-usart.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6fb0347a24f2..1a002a32d7aa 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,6 +77,15 @@ config SPI_ATMEL
 	  This selects a driver for the Atmel SPI Controller, present on
 	  many AT91 (ARM) chips.
 
+config SPI_AT91_USART
+	tristate "Atmel USART Controller SPI driver"
+	depends on HAS_DMA
+	depends on (ARCH_AT91 || COMPILE_TEST)
+	select MFD_AT91_USART
+	help
+	  This selects a driver for the AT91 USART Controller as SPI Master,
+	  present on AT91 and SAMA5 SoC series.
+
 config SPI_AU1550
 	tristate "Au1550/Au1200/Au1300 SPI Controller"
 	depends on MIPS_ALCHEMY
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 34c5f2832ddf..fb6cb42f4eaa 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST)		+= spi-loopback-test.o
 obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
 obj-$(CONFIG_SPI_ARMADA_3700)		+= spi-armada-3700.o
 obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
+obj-$(CONFIG_SPI_AT91_USART)		+= spi-at91-usart.o
 obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
 obj-$(CONFIG_SPI_AXI_SPI_ENGINE)	+= spi-axi-spi-engine.o
diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
new file mode 100644
index 000000000000..ae889aa6bda3
--- /dev/null
+++ b/drivers/spi/spi-at91-usart.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AT91 USART Controllers as SPI
+ *
+ * Copyright (C) 2018 Microchip Technology Inc.
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+
+#define US_CR			0x00
+#define US_MR			0x04
+#define US_IER			0x08
+#define US_IDR			0x0C
+#define US_CSR			0x14
+#define US_RHR			0x18
+#define US_THR			0x1C
+#define US_BRGR			0x20
+#define US_VERSION		0xFC
+
+#define US_CR_RSTRX		BIT(2)
+#define US_CR_RSTTX		BIT(3)
+#define US_CR_RXEN		BIT(4)
+#define US_CR_RXDIS		BIT(5)
+#define US_CR_TXEN		BIT(6)
+#define US_CR_TXDIS		BIT(7)
+
+#define US_MR_SPI_MASTER	0x0E
+#define US_MR_CHRL		GENMASK(7, 6)
+#define US_MR_CPHA		BIT(8)
+#define US_MR_CPOL		BIT(16)
+#define US_MR_CLKO		BIT(18)
+#define US_MR_WRDBT		BIT(20)
+#define US_MR_LOOP		BIT(15)
+
+#define US_IR_RXRDY		BIT(0)
+#define US_IR_TXRDY		BIT(1)
+#define US_IR_OVRE		BIT(5)
+
+#define US_BRGR_SIZE		BIT(16)
+
+#define US_MIN_CLK_DIV		0x06
+#define US_MAX_CLK_DIV		BIT(16)
+
+#define US_RESET		(US_CR_RSTRX | US_CR_RSTTX)
+#define US_DISABLE		(US_CR_RXDIS | US_CR_TXDIS)
+#define US_ENABLE		(US_CR_RXEN | US_CR_TXEN)
+#define US_OVRE_RXRDY_IRQS	(US_IR_OVRE | US_IR_RXRDY)
+
+#define US_INIT \
+	(US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT)
+
+/* Register access macros */
+#define at91_usart_spi_readl(port, reg) \
+	readl_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writel(port, reg, value) \
+	writel_relaxed((value), (port)->regs + US_##reg)
+
+#define at91_usart_spi_readb(port, reg) \
+	readb_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writeb(port, reg, value) \
+	writeb_relaxed((value), (port)->regs + US_##reg)
+
+struct at91_usart_spi {
+	struct spi_transfer	*current_transfer;
+	void __iomem		*regs;
+	struct device		*dev;
+	struct clk		*clk;
+
+	/*used in interrupt to protect data reading*/
+	spinlock_t		lock;
+
+	int			irq;
+	unsigned int		current_tx_remaining_bytes;
+	unsigned int		current_rx_remaining_bytes;
+	int			done_status;
+
+	u32			spi_clk;
+	u32			status;
+
+	bool			xfer_failed;
+};
+
+static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus)
+{
+	return aus->status & US_IR_TXRDY;
+}
+
+static inline u32 at91_usart_spi_rx_ready(struct at91_usart_spi *aus)
+{
+	return aus->status & US_IR_RXRDY;
+}
+
+static inline u32 at91_usart_spi_check_overrun(struct at91_usart_spi *aus)
+{
+	return aus->status & US_IR_OVRE;
+}
+
+static inline u32 at91_usart_spi_read_status(struct at91_usart_spi *aus)
+{
+	aus->status = at91_usart_spi_readl(aus, CSR);
+	return aus->status;
+}
+
+static inline void at91_usart_spi_tx(struct at91_usart_spi *aus)
+{
+	unsigned int len = aus->current_transfer->len;
+	unsigned int remaining = aus->current_tx_remaining_bytes;
+	const u8  *tx_buf = aus->current_transfer->tx_buf;
+
+	if (!remaining)
+		return;
+
+	if (at91_usart_spi_tx_ready(aus)) {
+		at91_usart_spi_writeb(aus, THR, tx_buf[len - remaining]);
+		aus->current_tx_remaining_bytes--;
+	}
+}
+
+static inline void at91_usart_spi_rx(struct at91_usart_spi *aus)
+{
+	int len = aus->current_transfer->len;
+	int remaining = aus->current_rx_remaining_bytes;
+	u8  *rx_buf = aus->current_transfer->rx_buf;
+
+	if (!remaining)
+		return;
+
+	rx_buf[len - remaining] = at91_usart_spi_readb(aus, RHR);
+	aus->current_rx_remaining_bytes--;
+}
+
+static inline void
+at91_usart_spi_set_xfer_speed(struct at91_usart_spi *aus,
+			      struct spi_transfer *xfer)
+{
+	at91_usart_spi_writel(aus, BRGR,
+			      DIV_ROUND_UP(aus->spi_clk, xfer->speed_hz));
+}
+
+static irqreturn_t at91_usart_spi_interrupt(int irq, void *dev_id)
+{
+	struct spi_controller *controller = dev_id;
+	struct at91_usart_spi *aus = spi_master_get_devdata(controller);
+
+	spin_lock(&aus->lock);
+	at91_usart_spi_read_status(aus);
+
+	if (at91_usart_spi_check_overrun(aus)) {
+		aus->xfer_failed = true;
+		aus->done_status = -EIO;
+		at91_usart_spi_writel(aus, IDR, US_IR_OVRE | US_IR_RXRDY);
+		spin_unlock(&aus->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (at91_usart_spi_rx_ready(aus)) {
+		at91_usart_spi_rx(aus);
+		spin_unlock(&aus->lock);
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock(&aus->lock);
+
+	return IRQ_NONE;
+}
+
+static int at91_usart_spi_setup(struct spi_device *spi)
+{
+	struct at91_usart_spi *aus = spi_master_get_devdata(spi->controller);
+	u32 *ausd = spi->controller_state;
+	unsigned int mr = at91_usart_spi_readl(aus, MR);
+	u8 bits = spi->bits_per_word;
+
+	if (bits != 8) {
+		dev_dbg(&spi->dev, "Only 8 bits per word are supported\n");
+		return -EINVAL;
+	}
+
+	if (spi->mode & SPI_CPOL)
+		mr |= US_MR_CPOL;
+	else
+		mr &= ~US_MR_CPOL;
+
+	if (spi->mode & SPI_CPHA)
+		mr |= US_MR_CPHA;
+	else
+		mr &= ~US_MR_CPHA;
+
+	if (spi->mode & SPI_LOOP)
+		mr |= US_MR_LOOP;
+	else
+		mr &= ~US_MR_LOOP;
+
+	if (!ausd) {
+		ausd = kzalloc(sizeof(*ausd), GFP_KERNEL);
+		if (!ausd)
+			return -ENOMEM;
+
+		spi->controller_state = ausd;
+	}
+
+	*ausd = mr;
+
+	dev_dbg(&spi->dev,
+		"setup: bpw %u mode 0x%x -> mr %d %08x\n",
+		bits, spi->mode, spi->chip_select, mr);
+
+	return 0;
+}
+
+int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
+				struct spi_device *spi,
+				struct spi_transfer *xfer)
+{
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+	at91_usart_spi_set_xfer_speed(aus, xfer);
+	aus->done_status = 0;
+	aus->xfer_failed = false;
+	aus->current_transfer = xfer;
+	aus->current_tx_remaining_bytes = xfer->len;
+	aus->current_rx_remaining_bytes = xfer->len;
+
+	while ((aus->current_tx_remaining_bytes ||
+		aus->current_rx_remaining_bytes) && !aus->xfer_failed) {
+		at91_usart_spi_read_status(aus);
+		at91_usart_spi_tx(aus);
+		cpu_relax();
+	}
+	if (aus->xfer_failed) {
+		dev_err(aus->dev, "Overrun!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int at91_usart_spi_prepare_message(struct spi_controller *ctlr,
+				   struct spi_message *message)
+{
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+	struct spi_device *spi = message->spi;
+	u32 *ausd = spi->controller_state;
+
+	at91_usart_spi_writel(aus, CR, US_ENABLE);
+	at91_usart_spi_writel(aus, IER, US_OVRE_RXRDY_IRQS);
+	at91_usart_spi_writel(aus, MR, *ausd);
+
+	return 0;
+}
+
+int at91_usart_spi_unprepare_message(struct spi_controller *ctlr,
+				     struct spi_message *message)
+{
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+	at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+	at91_usart_spi_writel(aus, IDR, US_OVRE_RXRDY_IRQS);
+
+	return 0;
+}
+
+static void at91_usart_spi_cleanup(struct spi_device *spi)
+{
+	struct at91_usart_spi_device *ausd = spi->controller_state;
+
+	spi->controller_state = NULL;
+	kfree(ausd);
+}
+
+static void at91_usart_spi_init(struct at91_usart_spi *aus)
+{
+	at91_usart_spi_writel(aus, MR, US_INIT);
+	at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+}
+
+static int at91_usart_gpio_setup(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.parent->of_node;
+	int i;
+	int ret;
+	int nb;
+
+	if (!np)
+		return -EINVAL;
+
+	nb = of_gpio_named_count(np, "cs-gpios");
+	for (i = 0; i < nb; i++) {
+		int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
+
+		if (cs_gpio < 0)
+			return cs_gpio;
+
+		if (gpio_is_valid(cs_gpio)) {
+			ret = devm_gpio_request_one(&pdev->dev, cs_gpio,
+						    GPIOF_DIR_OUT,
+						    dev_name(&pdev->dev));
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int at91_usart_spi_probe(struct platform_device *pdev)
+{
+	struct resource *regs;
+	struct spi_controller *controller;
+	struct at91_usart_spi *aus;
+	struct clk *clk;
+	int irq;
+	int ret;
+
+	regs = platform_get_resource(to_platform_device(pdev->dev.parent),
+				     IORESOURCE_MEM, 0);
+	if (!regs)
+		return -EINVAL;
+
+	irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
+	if (irq < 0)
+		return irq;
+
+	clk = devm_clk_get(pdev->dev.parent, "usart");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = -ENOMEM;
+	controller = spi_alloc_master(&pdev->dev, sizeof(*aus));
+	if (!controller)
+		goto at91_usart_spi_probe_fail;
+
+	ret = at91_usart_gpio_setup(pdev);
+	if (ret)
+		goto at91_usart_spi_probe_fail;
+
+	controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
+	controller->dev.of_node = pdev->dev.parent->of_node;
+	controller->bits_per_word_mask = SPI_BPW_MASK(8);
+	controller->setup = at91_usart_spi_setup;
+	controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+	controller->transfer_one = at91_usart_spi_transfer_one;
+	controller->prepare_message = at91_usart_spi_prepare_message;
+	controller->unprepare_message = at91_usart_spi_unprepare_message;
+	controller->cleanup = at91_usart_spi_cleanup;
+	controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+						US_MIN_CLK_DIV);
+	controller->min_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+						US_MAX_CLK_DIV);
+	platform_set_drvdata(pdev, controller);
+
+	aus = spi_master_get_devdata(controller);
+
+	aus->dev = &pdev->dev;
+	aus->regs = devm_ioremap_resource(&pdev->dev, regs);
+	if (IS_ERR(aus->regs)) {
+		ret = PTR_ERR(aus->regs);
+		goto at91_usart_spi_probe_fail;
+	}
+
+	aus->irq = irq;
+	aus->clk = clk;
+
+	ret = devm_request_irq(&pdev->dev, irq, at91_usart_spi_interrupt, 0,
+			       dev_name(&pdev->dev), controller);
+	if (ret)
+		goto at91_usart_spi_probe_fail;
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		goto at91_usart_spi_probe_fail;
+
+	aus->spi_clk = clk_get_rate(clk);
+	at91_usart_spi_init(aus);
+
+	spin_lock_init(&aus->lock);
+	ret = devm_spi_register_master(&pdev->dev, controller);
+	if (ret)
+		goto fail_register_master;
+
+	dev_info(&pdev->dev,
+		 "Atmel USART SPI Controller version 0x%x at 0x%08x (irq %d)\n",
+		 at91_usart_spi_readl(aus, VERSION),
+		 regs->start, irq);
+
+	return 0;
+
+fail_register_master:
+	clk_disable_unprepare(clk);
+at91_usart_spi_probe_fail:
+	spi_master_put(controller);
+	return ret;
+}
+
+static int at91_usart_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(master);
+
+	clk_disable_unprepare(aus->clk);
+
+	return 0;
+}
+
+static const struct of_device_id at91_usart_spi_dt_ids[] = {
+	{ .compatible = "microchip,at91sam9g45-usart-spi"},
+	{ /* sentinel */}
+};
+
+MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
+
+static struct platform_driver at91_usart_spi_driver = {
+	.driver = {
+		.name = "at91_usart_spi",
+	},
+	.probe = at91_usart_spi_probe,
+	.remove = at91_usart_spi_remove,
+};
+
+module_platform_driver(at91_usart_spi_driver);
+
+MODULE_DESCRIPTION("Microchip AT91 USART SPI Controller driver");
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:at91_usart_spi");
-- 
2.17.1

^ permalink raw reply related

* [PATCH v7 6/6] tty/serial: atmel: change the driver to work under at91-usart mfd
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>

This patch modifies the place where resources and device tree properties
are searched.

Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Acked-by: Richard Genoud <richard.genoud@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
 drivers/tty/serial/Kconfig        |  1 +
 drivers/tty/serial/atmel_serial.c | 42 ++++++++++++++++++++-----------
 2 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3682fd3e960c..25e55332f8b1 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -119,6 +119,7 @@ config SERIAL_ATMEL
 	depends on ARCH_AT91 || COMPILE_TEST
 	select SERIAL_CORE
 	select SERIAL_MCTRL_GPIO if GPIOLIB
+	select MFD_AT91_USART
 	help
 	  This enables the driver for the on-chip UARTs of the Atmel
 	  AT91 processors.
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index df46a9e88c34..107114d73c5d 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -193,8 +193,7 @@ static struct console atmel_console;
 
 #if defined(CONFIG_OF)
 static const struct of_device_id atmel_serial_dt_ids[] = {
-	{ .compatible = "atmel,at91rm9200-usart" },
-	{ .compatible = "atmel,at91sam9260-usart" },
+	{ .compatible = "atmel,at91rm9200-usart-serial" },
 	{ /* sentinel */ }
 };
 #endif
@@ -915,6 +914,7 @@ static void atmel_tx_dma(struct uart_port *port)
 static int atmel_prepare_tx_dma(struct uart_port *port)
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct device *mfd_dev = port->dev->parent;
 	dma_cap_mask_t		mask;
 	struct dma_slave_config config;
 	int ret, nent;
@@ -922,7 +922,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
 	dma_cap_zero(mask);
 	dma_cap_set(DMA_SLAVE, mask);
 
-	atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+	atmel_port->chan_tx = dma_request_slave_channel(mfd_dev, "tx");
 	if (atmel_port->chan_tx == NULL)
 		goto chan_err;
 	dev_info(port->dev, "using %s for tx DMA transfers\n",
@@ -1093,6 +1093,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
 static int atmel_prepare_rx_dma(struct uart_port *port)
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct device *mfd_dev = port->dev->parent;
 	struct dma_async_tx_descriptor *desc;
 	dma_cap_mask_t		mask;
 	struct dma_slave_config config;
@@ -1104,7 +1105,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
 	dma_cap_zero(mask);
 	dma_cap_set(DMA_CYCLIC, mask);
 
-	atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+	atmel_port->chan_rx = dma_request_slave_channel(mfd_dev, "rx");
 	if (atmel_port->chan_rx == NULL)
 		goto chan_err;
 	dev_info(port->dev, "using %s for rx DMA transfers\n",
@@ -2222,8 +2223,8 @@ static const char *atmel_type(struct uart_port *port)
  */
 static void atmel_release_port(struct uart_port *port)
 {
-	struct platform_device *pdev = to_platform_device(port->dev);
-	int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+	struct platform_device *mpdev = to_platform_device(port->dev->parent);
+	int size = resource_size(mpdev->resource);
 
 	release_mem_region(port->mapbase, size);
 
@@ -2238,8 +2239,8 @@ static void atmel_release_port(struct uart_port *port)
  */
 static int atmel_request_port(struct uart_port *port)
 {
-	struct platform_device *pdev = to_platform_device(port->dev);
-	int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+	struct platform_device *mpdev = to_platform_device(port->dev->parent);
+	int size = resource_size(mpdev->resource);
 
 	if (!request_mem_region(port->mapbase, size, "atmel_serial"))
 		return -EBUSY;
@@ -2341,27 +2342,28 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 {
 	int ret;
 	struct uart_port *port = &atmel_port->uart;
+	struct platform_device *mpdev = to_platform_device(pdev->dev.parent);
 
 	atmel_init_property(atmel_port, pdev);
 	atmel_set_ops(port);
 
-	uart_get_rs485_mode(&pdev->dev, &port->rs485);
+	uart_get_rs485_mode(&mpdev->dev, &port->rs485);
 
 	port->iotype		= UPIO_MEM;
 	port->flags		= UPF_BOOT_AUTOCONF | UPF_IOREMAP;
 	port->ops		= &atmel_pops;
 	port->fifosize		= 1;
 	port->dev		= &pdev->dev;
-	port->mapbase	= pdev->resource[0].start;
-	port->irq	= pdev->resource[1].start;
+	port->mapbase		= mpdev->resource[0].start;
+	port->irq		= mpdev->resource[1].start;
 	port->rs485_config	= atmel_config_rs485;
-	port->membase	= NULL;
+	port->membase		= NULL;
 
 	memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
 
 	/* for console, the clock could already be configured */
 	if (!atmel_port->clk) {
-		atmel_port->clk = clk_get(&pdev->dev, "usart");
+		atmel_port->clk = clk_get(&mpdev->dev, "usart");
 		if (IS_ERR(atmel_port->clk)) {
 			ret = PTR_ERR(atmel_port->clk);
 			atmel_port->clk = NULL;
@@ -2694,13 +2696,22 @@ static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port,
 static int atmel_serial_probe(struct platform_device *pdev)
 {
 	struct atmel_uart_port *atmel_port;
-	struct device_node *np = pdev->dev.of_node;
+	struct device_node *np = pdev->dev.parent->of_node;
 	void *data;
 	int ret = -ENODEV;
 	bool rs485_enabled;
 
 	BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
 
+	/*
+	 * In device tree there is no node with "atmel,at91rm9200-usart-serial"
+	 * as compatible string. This driver is probed by at91-usart mfd driver
+	 * which is just a wrapper over the atmel_serial driver and
+	 * spi-at91-usart driver. All attributes needed by this driver are
+	 * found in of_node of parent.
+	 */
+	pdev->dev.of_node = np;
+
 	ret = of_alias_get_id(np, "serial");
 	if (ret < 0)
 		/* port id not found in platform data nor device-tree aliases:
@@ -2835,6 +2846,7 @@ static int atmel_serial_remove(struct platform_device *pdev)
 
 	clk_put(atmel_port->clk);
 	atmel_port->clk = NULL;
+	pdev->dev.of_node = NULL;
 
 	return ret;
 }
@@ -2845,7 +2857,7 @@ static struct platform_driver atmel_serial_driver = {
 	.suspend	= atmel_serial_suspend,
 	.resume		= atmel_serial_resume,
 	.driver		= {
-		.name			= "atmel_usart",
+		.name			= "atmel_usart_serial",
 		.of_match_table		= of_match_ptr(atmel_serial_dt_ids),
 	},
 };
-- 
2.17.1

^ permalink raw reply related

* [PATCH v2] arm64: dts: renesas: condor/v3hsk: specify Ethernet PHY IRQs
From: Sergei Shtylyov @ 2018-06-13 16:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4acc208e-c593-1e8a-00ca-fc9a5574074e@cogentembedded.com>

Specify Ethernet PHY IRQs in the Condor/V3HSK board device trees, now that
we have the GPIO support (previously phylib had  to resort to polling).

Based on the original (and large) patch by Vladimir Barinov.

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
Changes in version 2:
- merged in the analogous V3HSK patch, renamed the patch, and updated the
  patch description accordingly.

 arch/arm64/boot/dts/renesas/r8a77980-condor.dts |    2 ++
 arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts  |    2 ++
 2 files changed, 4 insertions(+)

Index: renesas/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
===================================================================
--- renesas.orig/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
+++ renesas/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
@@ -59,6 +59,8 @@
 	phy0: ethernet-phy at 0 {
 		rxc-skew-ps = <1500>;
 		reg = <0>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
 	};
 };
 
Index: renesas/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
===================================================================
--- renesas.orig/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
+++ renesas/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
@@ -48,6 +48,8 @@
 
 	phy0: ethernet-phy at 0 {
 		reg = <0>;
+		interrupt-parent = <&gpio4>;
+		interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
 	};
 };
 

^ permalink raw reply

* [PATCH v2] arm64: dts: renesas: condor/v3hsk: specify Ethernet PHY IRQs
From: Sergei Shtylyov @ 2018-06-13 16:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <42861d8f-3570-03b9-987c-bb15fa92a9c7@cogentembedded.com>

On 06/13/2018 07:42 PM, Sergei Shtylyov wrote:

> Specify Ethernet PHY IRQs in the Condor/V3HSK board device trees, now that
> we have the GPIO support (previously phylib had  to resort to polling).
> 
> Based on the original (and large) patch by Vladimir Barinov.
> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---

   Forgot to add the the patch is against the 'renesas-devel-20180613-v4.17' tag.

[...]

MBR, Sergei

^ permalink raw reply

* [PATCH V5] ARM: shmobile: Rework the PMIC IRQ line quirk
From: Geert Uytterhoeven @ 2018-06-13 16:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613143626.rrdxq4i4guy2oryg@verge.net.au>

Hi Simon,

On Wed, Jun 13, 2018 at 4:36 PM Simon Horman <horms@verge.net.au> wrote:
> On Wed, Jun 13, 2018 at 01:21:34PM +0200, Geert Uytterhoeven wrote:
> > On Wed, Jun 13, 2018 at 1:06 PM Simon Horman <horms@verge.net.au> wrote:
> > > On Mon, Jun 11, 2018 at 02:15:13PM +0200, Marek Vasut wrote:
> > > > Rather than hard-coding the quirk topology, which stopped scaling,
> > > > parse the information from DT. The code looks for all compatible
> > > > PMICs -- da9063 and da9210 -- and checks if their IRQ line is tied
> > > > to the same pin. If so, the code sends a matching sequence to the
> > > > PMIC to deassert the IRQ.
> > > >
> > > > Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
> > > > Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> > > > Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> > > > Cc: Simon Horman <horms+renesas@verge.net.au>
> > > > Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>
> > > > Cc: linux-renesas-soc at vger.kernel.org
> > > > Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> > > > Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> (on Koelsch)
> > >
> > > This looks fine to me but I will wait to see if there are other reviews
> > > before applying.
> > >
> > > Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
> >
> > Note that applying it now will break gose, as its DTS doesn't describe all
> > regulators yet.
>
> Ok, so old DT will break with new kernels?

For Lager, it will break with pre-v4.2 DTS, so I think that's not a problem.
For Koelsch, the regulators were added even earlier.
Stout had all regulators from the initial support in upstream.

So Gose is the only issue. Do we care?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH v2 1/2] arm64: dts: qcom: sdm845: Add I2C, SPI, and UART9 nodes
From: Douglas Anderson @ 2018-06-13 16:53 UTC (permalink / raw)
  To: linux-arm-kernel

This adds nodes to SDM845-dtsi for all the I2C ports, all the SPI
ports, and UART9.  Note that I2C / SPI / UART are a bit strange on
sdm845 because each "serial engine" has 4 pins associated with it and
depending on which firmware has been loaded into the serial engine
(loaded by the BIOS) the serial engine can behave like an I2C port, a
SPI port, or a UART.  As per the landed bindings that means that we
need to create one node for each possible mode that the port could be
in.  With 16 serial engines that means 16 x 3 = 48 nodes.

We get away with only creating 33 nodes for now because it seems very
likely that SDM845-based boards will actually all use the same UART
(UART 9) for debug purposes.  While another UART could be used for
something like Bluetooth communication we can cross that path when we
come to it.  Some documentation that I saw implied that using a UART
for "high speed" communications actually needs yet another different
serial engine firmware anyway.

Note that quick measurements adding all these nodes adds <10k of extra
space per dtb that they're included with.  If this becomes a problem
we may need to think of a different way to structure this so that
boards only get the nodes they need (or figure out how to get dtc to
strip 'disabled' nodes).  For now it seems OK.

These nodes were programmatically generated with a fairly dumb python
script.  See http://crosreview.com/1091631 for the source.

NOTE: at the moment SPI chip select doesn't appear to work in my tests
with the latest posted SPI driver.  All testing of SPI with this patch
has been done by hacking SPI to GPIO chip select.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---

Changes in v2:
- Got rid of all sleep pinctrl states for now
- Add Bjorn's tags.

 arch/arm64/boot/dts/qcom/sdm845.dtsi | 733 +++++++++++++++++++++++++++
 1 file changed, 733 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index cdaabeb3c995..2407d39f74df 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -5,6 +5,7 @@
  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
  */
 
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
 / {
@@ -13,6 +14,41 @@
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	aliases {
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c2 = &i2c2;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		i2c6 = &i2c6;
+		i2c7 = &i2c7;
+		i2c8 = &i2c8;
+		i2c9 = &i2c9;
+		i2c10 = &i2c10;
+		i2c11 = &i2c11;
+		i2c12 = &i2c12;
+		i2c13 = &i2c13;
+		i2c14 = &i2c14;
+		i2c15 = &i2c15;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		spi2 = &spi2;
+		spi3 = &spi3;
+		spi4 = &spi4;
+		spi5 = &spi5;
+		spi6 = &spi6;
+		spi7 = &spi7;
+		spi8 = &spi8;
+		spi9 = &spi9;
+		spi10 = &spi10;
+		spi11 = &spi11;
+		spi12 = &spi12;
+		spi13 = &spi13;
+		spi14 = &spi14;
+		spi15 = &spi15;
+	};
+
 	chosen { };
 
 	memory at 80000000 {
@@ -206,6 +242,456 @@
 			#power-domain-cells = <1>;
 		};
 
+		qupv3_id_0: geniqup at 8c0000 {
+			compatible = "qcom,geni-se-qup";
+			reg = <0x8c0000 0x6000>;
+			clock-names = "m-ahb", "s-ahb";
+			clocks = <&gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
+				 <&gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+
+			i2c0: i2c at 880000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x880000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c0_default>;
+				interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi0: spi at 880000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x880000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi0_default>;
+				interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c1: i2c at 884000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x884000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c1_default>;
+				interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi1: spi at 884000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x884000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi1_default>;
+				interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c2: i2c at 888000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x888000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c2_default>;
+				interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi2: spi at 888000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x888000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi2_default>;
+				interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c3: i2c at 88c000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x88c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c3_default>;
+				interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi3: spi at 88c000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x88c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi3_default>;
+				interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c4: i2c at 890000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x890000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c4_default>;
+				interrupts = <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi4: spi at 890000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x890000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi4_default>;
+				interrupts = <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c5: i2c at 894000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x894000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c5_default>;
+				interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi5: spi at 894000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x894000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi5_default>;
+				interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c6: i2c at 898000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x898000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c6_default>;
+				interrupts = <GIC_SPI 607 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi6: spi at 898000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x898000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi6_default>;
+				interrupts = <GIC_SPI 607 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c7: i2c at 89c000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0x89c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c7_default>;
+				interrupts = <GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi7: spi at 89c000 {
+				compatible = "qcom,geni-spi";
+				reg = <0x89c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi7_default>;
+				interrupts = <GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+		};
+
+		qupv3_id_1: geniqup at ac0000 {
+			compatible = "qcom,geni-se-qup";
+			reg = <0xac0000 0x6000>;
+			clock-names = "m-ahb", "s-ahb";
+			clocks = <&gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
+				 <&gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+			status = "disabled";
+
+			i2c8: i2c at a80000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa80000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c8_default>;
+				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi8: spi at a80000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa80000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi8_default>;
+				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c9: i2c at a84000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa84000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c9_default>;
+				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi9: spi at a84000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa84000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi9_default>;
+				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			uart9: serial at a84000 {
+				compatible = "qcom,geni-debug-uart";
+				reg = <0xa84000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_uart9_default>;
+				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
+				status = "disabled";
+			};
+
+			i2c10: i2c at a88000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa88000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c10_default>;
+				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi10: spi at a88000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa88000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi10_default>;
+				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c11: i2c at a8c000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa8c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c11_default>;
+				interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi11: spi at a8c000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa8c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi11_default>;
+				interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c12: i2c at a90000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa90000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c12_default>;
+				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi12: spi at a90000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa90000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi12_default>;
+				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c13: i2c at a94000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa94000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c13_default>;
+				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi13: spi at a94000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa94000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi13_default>;
+				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c14: i2c at a98000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa98000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c14_default>;
+				interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi14: spi at a98000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa98000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi14_default>;
+				interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			i2c15: i2c at a9c000 {
+				compatible = "qcom,geni-i2c";
+				reg = <0xa9c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_i2c15_default>;
+				interrupts = <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+
+			spi15: spi at a9c000 {
+				compatible = "qcom,geni-spi";
+				reg = <0xa9c000 0x4000>;
+				clock-names = "se";
+				clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&qup_spi15_default>;
+				interrupts = <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "disabled";
+			};
+		};
+
 		tcsr_mutex_regs: syscon at 1f40000 {
 			compatible = "syscon";
 			reg = <0x1f40000 0x40000>;
@@ -219,6 +705,253 @@
 			#gpio-cells = <2>;
 			interrupt-controller;
 			#interrupt-cells = <2>;
+
+			qup_i2c0_default: qup-i2c0-default {
+				pinmux {
+					pins = "gpio0", "gpio1";
+					function = "qup0";
+				};
+			};
+
+			qup_i2c1_default: qup-i2c1-default {
+				pinmux {
+					pins = "gpio17", "gpio18";
+					function = "qup1";
+				};
+			};
+
+			qup_i2c2_default: qup-i2c2-default {
+				pinmux {
+					pins = "gpio27", "gpio28";
+					function = "qup2";
+				};
+			};
+
+			qup_i2c3_default: qup-i2c3-default {
+				pinmux {
+					pins = "gpio41", "gpio42";
+					function = "qup3";
+				};
+			};
+
+			qup_i2c4_default: qup-i2c4-default {
+				pinmux {
+					pins = "gpio89", "gpio90";
+					function = "qup4";
+				};
+			};
+
+			qup_i2c5_default: qup-i2c5-default {
+				pinmux {
+					pins = "gpio85", "gpio86";
+					function = "qup5";
+				};
+			};
+
+			qup_i2c6_default: qup-i2c6-default {
+				pinmux {
+					pins = "gpio45", "gpio46";
+					function = "qup6";
+				};
+			};
+
+			qup_i2c7_default: qup-i2c7-default {
+				pinmux {
+					pins = "gpio93", "gpio94";
+					function = "qup7";
+				};
+			};
+
+			qup_i2c8_default: qup-i2c8-default {
+				pinmux {
+					pins = "gpio65", "gpio66";
+					function = "qup8";
+				};
+			};
+
+			qup_i2c9_default: qup-i2c9-default {
+				pinmux {
+					pins = "gpio6", "gpio7";
+					function = "qup9";
+				};
+			};
+
+			qup_i2c10_default: qup-i2c10-default {
+				pinmux {
+					pins = "gpio55", "gpio56";
+					function = "qup10";
+				};
+			};
+
+			qup_i2c11_default: qup-i2c11-default {
+				pinmux {
+					pins = "gpio31", "gpio32";
+					function = "qup11";
+				};
+			};
+
+			qup_i2c12_default: qup-i2c12-default {
+				pinmux {
+					pins = "gpio49", "gpio50";
+					function = "qup12";
+				};
+			};
+
+			qup_i2c13_default: qup-i2c13-default {
+				pinmux {
+					pins = "gpio105", "gpio106";
+					function = "qup13";
+				};
+			};
+
+			qup_i2c14_default: qup-i2c14-default {
+				pinmux {
+					pins = "gpio33", "gpio34";
+					function = "qup14";
+				};
+			};
+
+			qup_i2c15_default: qup-i2c15-default {
+				pinmux {
+					pins = "gpio81", "gpio82";
+					function = "qup15";
+				};
+			};
+
+			qup_spi0_default: qup-spi0-default {
+				pinmux {
+					pins = "gpio0", "gpio1",
+					       "gpio2", "gpio3";
+					function = "qup0";
+				};
+			};
+
+			qup_spi1_default: qup-spi1-default {
+				pinmux {
+					pins = "gpio17", "gpio18",
+					       "gpio19", "gpio20";
+					function = "qup1";
+				};
+			};
+
+			qup_spi2_default: qup-spi2-default {
+				pinmux {
+					pins = "gpio27", "gpio28",
+					       "gpio29", "gpio30";
+					function = "qup2";
+				};
+			};
+
+			qup_spi3_default: qup-spi3-default {
+				pinmux {
+					pins = "gpio41", "gpio42",
+					       "gpio43", "gpio44";
+					function = "qup3";
+				};
+			};
+
+			qup_spi4_default: qup-spi4-default {
+				pinmux {
+					pins = "gpio89", "gpio90",
+					       "gpio91", "gpio92";
+					function = "qup4";
+				};
+			};
+
+			qup_spi5_default: qup-spi5-default {
+				pinmux {
+					pins = "gpio85", "gpio86",
+					       "gpio87", "gpio88";
+					function = "qup5";
+				};
+			};
+
+			qup_spi6_default: qup-spi6-default {
+				pinmux {
+					pins = "gpio45", "gpio46",
+					       "gpio47", "gpio48";
+					function = "qup6";
+				};
+			};
+
+			qup_spi7_default: qup-spi7-default {
+				pinmux {
+					pins = "gpio93", "gpio94",
+					       "gpio95", "gpio96";
+					function = "qup7";
+				};
+			};
+
+			qup_spi8_default: qup-spi8-default {
+				pinmux {
+					pins = "gpio65", "gpio66",
+					       "gpio67", "gpio68";
+					function = "qup8";
+				};
+			};
+
+			qup_spi9_default: qup-spi9-default {
+				pinmux {
+					pins = "gpio6", "gpio7",
+					       "gpio4", "gpio5";
+					function = "qup9";
+				};
+			};
+
+			qup_spi10_default: qup-spi10-default {
+				pinmux {
+					pins = "gpio55", "gpio56",
+					       "gpio53", "gpio54";
+					function = "qup10";
+				};
+			};
+
+			qup_spi11_default: qup-spi11-default {
+				pinmux {
+					pins = "gpio31", "gpio32",
+					       "gpio33", "gpio34";
+					function = "qup11";
+				};
+			};
+
+			qup_spi12_default: qup-spi12-default {
+				pinmux {
+					pins = "gpio49", "gpio50",
+					       "gpio51", "gpio52";
+					function = "qup12";
+				};
+			};
+
+			qup_spi13_default: qup-spi13-default {
+				pinmux {
+					pins = "gpio105", "gpio106",
+					       "gpio107", "gpio108";
+					function = "qup13";
+				};
+			};
+
+			qup_spi14_default: qup-spi14-default {
+				pinmux {
+					pins = "gpio33", "gpio34",
+					       "gpio31", "gpio32";
+					function = "qup14";
+				};
+			};
+
+			qup_spi15_default: qup-spi15-default {
+				pinmux {
+					pins = "gpio81", "gpio82",
+					       "gpio83", "gpio84";
+					function = "qup15";
+				};
+			};
+
+			qup_uart9_default: qup-uart9-default {
+				pinmux {
+					pins = "gpio4", "gpio5";
+					function = "qup9";
+				};
+			};
 		};
 
 		spmi_bus: spmi at c440000 {
-- 
2.18.0.rc1.244.gcf134e6275-goog

^ permalink raw reply related

* [PATCH v2 2/2] arm64: dts: qcom: sdm845: Enable debug UART and I2C10 on sdm845-mtp
From: Douglas Anderson @ 2018-06-13 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613165352.139060-1-dianders@chromium.org>

The debug UART is very useful to have.  I2C10 is enabled as an example
of a I2C port we can talk on for now.  Eventually we'll want to put
peripherals under it.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Tested-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---

Changes in v2:
- Got rid of all sleep pinctrl states for now
- Add Bjorn's tags.

 arch/arm64/boot/dts/qcom/sdm845-mtp.dts | 45 +++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
index 979ab49913f1..6d651f314193 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
@@ -12,4 +12,49 @@
 / {
 	model = "Qualcomm Technologies, Inc. SDM845 MTP";
 	compatible = "qcom,sdm845-mtp";
+
+	aliases {
+		serial0 = &uart9;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&i2c10 {
+	status = "okay";
+	clock-frequency = <400000>;
+};
+
+&qupv3_id_1 {
+	status = "okay";
+};
+
+&uart9 {
+	status = "okay";
+};
+
+/* PINCTRL - additions to nodes defined in sdm845.dtsi */
+
+&qup_i2c10_default {
+	pinconf {
+		pins = "gpio55", "gpio56";
+		drive-strength = <2>;
+		bias-disable;
+	};
+};
+
+&qup_uart9_default {
+	pinconf-tx {
+		pins = "gpio4";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
+	pinconf-rx {
+		pins = "gpio5";
+		drive-strength = <2>;
+		bias-pull-up;
+	};
 };
-- 
2.18.0.rc1.244.gcf134e6275-goog

^ permalink raw reply related

* [RFC PATCH 6/8] dts: coresight: Clean up the device tree graph bindings
From: Suzuki K Poulose @ 2018-06-13 17:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <HE1PR0802MB24127EC935B9AF6F82AB3B6EEE7E0@HE1PR0802MB2412.eurprd08.prod.outlook.com>

Hi Matt,

On 13/06/18 16:47, Matt Sealey wrote:
> Hi Suzuki,
> 
>>> Why not use ?unit??
>>>
>>> I believe we had this discussion years ago about numbering serial ports
>>> and sdhci (i.e. how do you know it?s UART0 or UART1 from just the address?
>>> Some SoC?s don?t address sequentially *or* in a forward direction) - I
>>> believe it?s not exactly codified in ePAPR, not am I sure where it may be
>>> otherwise, but it exists.
>>
>> We have different situation here. We need to know *the port number* as
>> understood by the hardware, so that we can enable *the specific* port for
>> a given path.
> 
> For the purposes of abstraction, each port will have the property of having
> a node which is pointed to by other nodes, and in the case of a true ATB
> endpoint, no other nodes behind it.
> 
> It doesn't matter what the HW numbers it as as long as the driver can derive
> it from whatever you put in the DT. So a funnel (which is ~8 ports muxed into
> one output):
> 
>     f1p0: port {
>        unit = <0>;
>        endpoint = <&f1out>;
>     };
>     f1p1: port {
>        unit = <4>;
>        endpoint = <&f1out>;
>     };
>     f1out: port {
>        endpoint = <&etf1>;
>     };
> 
> "unit" here is specific to the driver's understanding of ports within it's

I may be missing, but is "unit" something that already exists and used by
DT bindings already ? Or is this something new that we are proposing ?

> own cycle of the graph. For a replicator you can invert the logic - input
> ports don't need a unit, but the two outputs are filtered in CoreSight not

I would prefer to make the new property mandatory for all the ports to avoid
a potential problem in the future.

How do you represent a TMC-ETF which has one input and one output connection ?
Also what happens if we ever get a component which has m-to-n connections ?

> by leg but by transiting ATB ID in groups of 16 IDs. In that case maybe
> you would want to describe all 8 possible units on each leg with the first
> ID it would filter? Or just list tuples of filter IDs <id, first, last>

I am failing to follow the ATB ID group description above. As per the TRM,
e.g, replicator filters the "trace stream" based on the "trace ID", which I
believe can be programmed via IDFILTER<n> register. So why would we need that
to be part of the DT ?

> 
> Who cares, really, as long as the driver knows what it means.
> 
> You don't need to namespace every property.
> 
>> As I mentioned above, we need the hardware numbers to enable the
>> "specific" port.
> 
> Okay and how is this not able to be prescribed in a binding for "arm,coresight-funnel"
> that:
> 
> "input ports are numbered from 0 to N where N is the maximum input port
> number. This number is identified with the "unit" property, which directly
> corresponds to the bit position in the funnel Ctrl_Reg register, and the
> bit position multiplied by 3 for each 3-bit priority in the funnel
> Priority_Ctrl_Reg, with N having a maximum of the defined register bitfield
> DEVID[PORTCOUNT], minus one, for that component"

The description looks over complicated to me at least, even after having known
bit of the programming interface of the components. I would prefer staying
closer to the terms used in the TRM ("slave/master" interfaces) and make it
easier for people to write the DT.

> 
> Or a replicator:
> 
> "output ports are numbered per the CoreSight ATB Replicator specification,
> unit corresponding to the IDFILTERn register controlling ID filters for
> that leg, with a maximum of the defined register bitfield DEVID[PORTNUM],
> minus one"
> 
> One could clarify it, even, with labels for readability ("label" definitely
> is a well defined if also completely arbitrary property).
> 
> ..
> 
>> static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
>> {
>>           u32 functl;
>>
>>           CS_UNLOCK(drvdata->base);
>>
>>           functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
>>           functl &= ~FUNNEL_HOLDTIME_MASK;
>>           functl |= FUNNEL_HOLDTIME;
>>           functl |= (1 << port);
>>           writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
>>           writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
>>
>>           CS_LOCK(drvdata->base);
>> }
>>
>> No we don't need to parse it in both ways, up and down. Btw, the trace
>> paths are not statically created. They are done at runtime, as configured
>> by the user.
> 
> You do realize this isn't how the hardware works, correct?

The "trace paths" mentioned above were indeed the software path, which
was constructed at runtime. The graph connections are indeed a one time
parsing at probe time and as you said they don't change. And by configuring,
I mean selecting the "source" and the "sink".

> 
> Trace paths are fixed, they may diverge with different configurations, but
> the full CoreSight topology (all funnels, replicators and intermediary
> Components) is entirely unchangeable.
> 
> The DT should provide the information to provide a reference acyclic directed
> graph of the entire topology (or entirely reasonably programmable topology where
> at all possible) - if a user wants to trace from ETM_0 then they only
> have particular paths to particular sinks, for instance ETM_0 and ETF_0
> may be on their own path, so you cannot just "configure as a user"
> a path from ETM_1 to ETF_0 since there isn't one.

> 
> Walking said graphs with the knowledge that CoreSight specifically disallows
> loopbacks in ATB topology is basic computer science problem - literally a
> matter of topological sorting. But let's build a graph once and traverse it -
> don't build the graph partially each time or try and build it to cross-check
> every time. The paths are wires in the design, lets not fake to the user
> that there is any configurability in that or try and encode that in the
> DT.

Sorry for the confusion, as explained above, it is indeed a one time pass.

> 
>> Coming back to your suggestion of "unit", what does it imply ?
> 
> Whatever the driver likes. For uart and mmc, it was just a spurious number
> but it could be applied as the end of, say, ttyS<N> or mmcblk<N>p3 or used
> in any other driver-specific manner. The number you put in is up to you,
> but the valid numbers would be in the binding for that particular device.
> 
>> Its too generic a term for something as concrete as a port number.
> 
> Is it?
> 
> Why would you need a whole other property type to encode a u32 that
> describes an arbitrary number specific to that hardware device?

So, if the suggestion is to use an existing property "unit", I am fine
with it, if people agree to it.


Thanks for the comments.

Cheers,
Suzuki

^ permalink raw reply

* [PATCH] PCI: xilinx: add missing of_node_put()
From: Nicholas Mc Guire @ 2018-06-13 17:20 UTC (permalink / raw)
  To: linux-arm-kernel

 The call to of_get_next_child() returns a node pointer with refcount
incremented thus it must be explicitly decremented here after the last
usage.

Signed-off-by: Nicholas Mc Guire <hofrat@osadl.org>
Fixes: commit 8961def56845 ("PCI: xilinx: Add Xilinx AXI PCIe Host Bridge IP driver")
---

Problem found by an experimental cocinelle script

Patch was compile tested with: multi_v7_defconfig + ARCH_ZYNQ=y,
COMPILE_TEST=y, PCIE_XILINX=y
(with one sparse warning though not related to the change proposed)

Patch is against 4.17.0 (localversion-next is next-20180613)

 drivers/pci/controller/pcie-xilinx.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c
index b110a3a..7b1389d 100644
--- a/drivers/pci/controller/pcie-xilinx.c
+++ b/drivers/pci/controller/pcie-xilinx.c
@@ -509,6 +509,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
 	port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
 						 &intx_domain_ops,
 						 port);
+	of_node_put(pcie_intc_node);
 	if (!port->leg_domain) {
 		dev_err(dev, "Failed to get a INTx IRQ domain\n");
 		return -ENODEV;
-- 
2.1.4

^ permalink raw reply related

* [PATCH] arm64: dts: hikey: Define wl1835 power capabilities
From: Valentin Schneider @ 2018-06-13 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180613151305.17240-1-ryan@edited.us>

Hi,

On 13/06/18 16:13, Ryan Grachek wrote:
> These properties are required for compatibility with runtime PM.
> Without these properties, MMC host controller will not be aware
> of power capabilities. When the wlcore driver attempts to power
> on the device, it will erroneously fail with -EACCES.
> 

I don't have a hikey620 to test this, but the hikey960 is suffering from the
same issue (see [1]). I added these properties to the dwmmc2 node of the
hikey960 (see snippet) and wlan0 does show up. Would you mind sending a
similar patch for that board ?

Thanks !

Valentin

[1]: https://lkml.org/lkml/2018/6/12/930

--->8

diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
index 85aadf1b..c706f70 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
@@ -613,6 +613,8 @@
        vmmc-supply = <&wlan_en>;
        ti,non-removable;
        non-removable;
+       cap-power-off-card;
+       keep-power-in-suspend;
        #address-cells = <0x1>;
        #size-cells = <0x0>;
        status = "ok";

^ permalink raw reply related

* [PATCH 3/4] ARM: mach-omap2: omap5/dra7: Enable ACTLR[0] (Enable invalidates of BTB) to facilitate CVE_2017-5715 WA in OS
From: Russell King - ARM Linux @ 2018-06-13 17:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <780935f6-6506-0915-dfb2-b584c074b70d@gmail.com>

On Wed, Jun 13, 2018 at 01:06:13AM +0200, Marek Vasut wrote:
> On 06/12/2018 10:24 PM, Nishanth Menon wrote:
> > Enable CVE_2017_5715 and since we have our own v7_arch_cp15_set_acr
> > function to setup the bits, we are able to override the settings.
> > 
> > Without this enabled, Linux kernel reports:
> > CPU0: Spectre v2: firmware did not set auxiliary control register IBE bit, system vulnerable
> > 
> > With this enabled, Linux kernel reports:
> > CPU0: Spectre v2: using ICIALLU workaround
> > 
> > NOTE: This by itself does not enable the workaround for CPU1 (on
> > OMAP5 and DRA72/AM572 SoCs) and may require additional kernel patches.
> > 
> > Signed-off-by: Nishanth Menon <nm@ti.com>
> > ---
> >  arch/arm/mach-omap2/Kconfig | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> > index 3bb1ecb58de0..77820cc8d1e4 100644
> > --- a/arch/arm/mach-omap2/Kconfig
> > +++ b/arch/arm/mach-omap2/Kconfig
> > @@ -53,6 +53,7 @@ config OMAP54XX
> >  	bool "OMAP54XX SoC"
> >  	select ARM_ERRATA_798870
> >  	select SYS_THUMB_BUILD
> > +	select ARM_CORTEX_A15_CVE_2017_5715
> >  	imply NAND_OMAP_ELM
> >  	imply NAND_OMAP_GPMC
> >  	imply SPL_DISPLAY_PRINT
> > 
> 
> Can this be enabled for all CA15 systems somehow ? I am sure there are
> more that are vulnerable.

I think you're missing the point.

Spectre affects the _entire_ system.  Working around it in just the
kernel does not mean that the system is no longer vulnerable.

Fixing the "system" means implementing the fixes also in the secure
world, which on A15 and A8 also means setting the IBE bit there.  If
the IBE bit is set in the secure world, it will also be set in the
non-secure world.

The fact that the kernel is complaining is telling you that the
system as a whole does not have the workarounds in place to mitigate
against the vulnerability.  Merely setting the IBE bit via some
secure API doesn't "magically" fix the secure world.

So, even if you were to set the IBE bit via some magic secure API,
the fact still remains: even with these workarounds in place, as I
understand it, the _system as a whole_ remains vulnerable - you
might as well _not_ have the kernel workarounds.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up

^ permalink raw reply

* [PATCH 1/2] arm64: avoid alloc memory on offline node
From: Punit Agrawal @ 2018-06-13 17:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <87lgbk59gs.fsf@e105922-lin.cambridge.arm.com>

Punit Agrawal <punit.agrawal@arm.com> writes:


[...]

>
> CONFIG_HAVE_MEMORYLESS node is not enabled on arm64 which means we end
> up returning the original node in the fallback path.
>
> Xie, does the below patch help? I can submit a proper patch if this
> fixes the issue for you.
>
> -- >8 --
> Subject: [PATCH] arm64/numa: Enable memoryless numa nodes
>
> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> ---
>  arch/arm64/Kconfig   | 4 ++++
>  arch/arm64/mm/numa.c | 2 ++
>  2 files changed, 6 insertions(+)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index eb2cf4938f6d..5317e9aa93ab 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -756,6 +756,10 @@ config USE_PERCPU_NUMA_NODE_ID
>  	def_bool y
>  	depends on NUMA
>  
> +config HAVE_MEMORYLESS_NODES
> +       def_bool y
> +       depends on NUMA
> +
>  config HAVE_SETUP_PER_CPU_AREA
>  	def_bool y
>  	depends on NUMA
> diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
> index dad128ba98bf..c699dcfe93de 100644
> --- a/arch/arm64/mm/numa.c
> +++ b/arch/arm64/mm/numa.c
> @@ -73,6 +73,8 @@ EXPORT_SYMBOL(cpumask_of_node);
>  static void map_cpu_to_node(unsigned int cpu, int nid)
>  {
>  	set_cpu_numa_node(cpu, nid);
> +	set_numa_mem(local_memory_node(nid));

Argh, this should be

        set_cpu_numa_mem(cpu, local_memory_node(nid));

There is not guarantee that map_cpu_to_node() will be called on the
local cpu.

Hanjun, Xie - can you try with the update please?

Thanks,
Punit

> +
>  	if (nid >= 0)
>  		cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
>  }

^ permalink raw reply

* [PATCH] arm64: dts: hikey960: Define wl1837 power capabilities
From: Ryan Grachek @ 2018-06-13 18:03 UTC (permalink / raw)
  To: linux-arm-kernel

These properties are required for compatibility with runtime PM.
Without these properties, MMC host controller will not be aware
of power capabilities. When the wlcore driver attempts to power
on the device, it will erroneously fail with -EACCES. This fixes
a regression found here: https://lkml.org/lkml/2018/6/12/930

Signed-off-by: Ryan Grachek <ryan@edited.us>
---
 arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
index c6999624ed8a..68c5a6c819ae 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
@@ -585,6 +585,8 @@
 	vmmc-supply = <&wlan_en>;
 	ti,non-removable;
 	non-removable;
+	cap-power-off-card;
+	keep-power-in-suspend;
 	#address-cells = <0x1>;
 	#size-cells = <0x0>;
 	status = "ok";
-- 
2.11.0

^ permalink raw reply related

* [PATCH] arm64: dts: hikey: Define wl1835 power capabilities
From: Ryan Grachek @ 2018-06-13 18:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <521dd38a-29b8-5670-4296-84b300921b3d@arm.com>

Hi Valentin,

On Wed, Jun 13, 2018 at 12:34 PM, Valentin Schneider
<valentin.schneider@arm.com> wrote:
> Hi,
>
> On 13/06/18 16:13, Ryan Grachek wrote:
>> These properties are required for compatibility with runtime PM.
>> Without these properties, MMC host controller will not be aware
>> of power capabilities. When the wlcore driver attempts to power
>> on the device, it will erroneously fail with -EACCES.
>>
>
> I don't have a hikey620 to test this, but the hikey960 is suffering from the
> same issue (see [1]). I added these properties to the dwmmc2 node of the
> hikey960 (see snippet) and wlan0 does show up. Would you mind sending a
> similar patch for that board ?
>
> Thanks !
>
> Valentin
>
> [1]: https://lkml.org/lkml/2018/6/12/930
>
> --->8
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
> index 85aadf1b..c706f70 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
> +++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
> @@ -613,6 +613,8 @@
>         vmmc-supply = <&wlan_en>;
>         ti,non-removable;
>         non-removable;
> +       cap-power-off-card;
> +       keep-power-in-suspend;
>         #address-cells = <0x1>;
>         #size-cells = <0x0>;
>         status = "ok";

Not a problem. The patch can be found here:
https://patchwork.kernel.org/patch/10462767/

Ryan

^ permalink raw reply

* [PATCH] arm64/acpi: Add fixup for HPE m400 quirks
From: Geoff Levand @ 2018-06-13 18:22 UTC (permalink / raw)
  To: linux-arm-kernel

Adds a new ACPI init routine acpi_fixup_m400_quirks that adds
a work-around for HPE ProLiant m400 APEI firmware problems.

The work-around disables APEI when CONFIG_ACPI_APEI is set and
m400 firmware is detected.  Without this fixup m400 systems
experience errors like these on startup:

  [Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 2
  [Hardware Error]: event severity: fatal
  [Hardware Error]:  Error 0, type: fatal
  [Hardware Error]:   section_type: memory error
  [Hardware Error]:   error_status: 0x0000000000001300
  [Hardware Error]:   error_type: 10, invalid address
  Kernel panic - not syncing: Fatal hardware error!

Signed-off-by: Geoff Levand <geoff@infradead.org>
---
Hi,

It seems unlikely there will be any m400 firmware updates to fix
this problem.  APEI support is desired for new ARM64 servers coming
to market.  Distros are now forced to have their own fixes, not
enable APEI, or let m400 users fend for themselves.

Please consider.

-Geoff

 arch/arm64/kernel/acpi.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 7b09487ff8fb..3c315c2c7476 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -31,6 +31,8 @@
 #include <asm/cpu_ops.h>
 #include <asm/smp_plat.h>
 
+#include <acpi/apei.h>
+
 #ifdef CONFIG_ACPI_APEI
 # include <linux/efi.h>
 # include <asm/pgtable.h>
@@ -177,6 +179,33 @@ static int __init acpi_fadt_sanity_check(void)
 	return ret;
 }
 
+/*
+ * acpi_fixup_m400_quirks - Work-around for HPE ProLiant m400 APEI firmware
+ * problems.
+ */
+static void __init acpi_fixup_m400_quirks(void)
+{
+	acpi_status status;
+	struct acpi_table_header *header;
+#if !defined(CONFIG_ACPI_APEI)
+	int hest_disable = HEST_DISABLED;
+#endif
+
+	if (!IS_ENABLED(CONFIG_ACPI_APEI) || hest_disable != HEST_ENABLED)
+		return;
+
+	status = acpi_get_table(ACPI_SIG_HEST, 0, &header);
+
+	if (ACPI_SUCCESS(status) && !strncmp(header->oem_id, "HPE   ", 6) &&
+		!strncmp(header->oem_table_id, "ProLiant", 8) &&
+		MIDR_IMPLEMENTOR(read_cpuid_id()) == ARM_CPU_IMP_APM) {
+		hest_disable = HEST_DISABLED;
+		pr_info("Disabled APEI for m400.\n");
+	}
+
+	acpi_put_table(header);
+}
+
 /*
  * acpi_boot_table_init() called from setup_arch(), always.
  *	1. find RSDP and get its address, and then find XSDT
@@ -232,11 +261,14 @@ void __init acpi_boot_table_init(void)
 	if (acpi_disabled) {
 		if (earlycon_acpi_spcr_enable)
 			early_init_dt_scan_chosen_stdout();
-	} else {
-		acpi_parse_spcr(earlycon_acpi_spcr_enable, true);
-		if (IS_ENABLED(CONFIG_ACPI_BGRT))
-			acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
+		return;
 	}
+
+	acpi_parse_spcr(earlycon_acpi_spcr_enable, true);
+	if (IS_ENABLED(CONFIG_ACPI_BGRT))
+		acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
+
+	acpi_fixup_m400_quirks();
 }
 
 #ifdef CONFIG_ACPI_APEI
-- 
2.14.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox