Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v2 04/11] dt-bindings: clock: qcom: add remaining clocks for IPQ8074
From: Stephen Boyd @ 2017-12-22  0:24 UTC (permalink / raw)
  To: Abhishek Sahu
  Cc: Michael Turquette, Rob Herring, Andy Gross, David Brown,
	Mark Rutland, linux-arm-msm, linux-soc, linux-clk, linux-kernel,
	devicetree
In-Reply-To: <1513175142-3702-5-git-send-email-absahu@codeaurora.org>

On 12/13, Abhishek Sahu wrote:
> This patch adds the DT bindings for following IPQ8074 clocks
> 
>  - General PLL’s, NSS UBI PLL and NSS Crypto PLL.
>  - 2 instances of PCIE, USB, SDCC.
>  - 2 NSS UBI core and common NSS clocks. NSS is network switching
>    system which accelerates the ethernet traffic. IPQ8074
>    NSS has two UBI cores. Some clocks are separate for each UBI core
>    and remaining NSS clocks are common.
>  - NSS ethernet port clocks. IPQ8074 has 6 ethernet ports and
>    each port uses different TX and RX clocks.
>  - Crypto engine clocks.
>  - General purpose clocks which comes over GPIO.
> 
> Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
> ---

Applied to clk-next

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH v2 10/11] dt-bindings: clock: qcom: add misc resets for PCIE and NSS
From: Stephen Boyd @ 2017-12-22  0:24 UTC (permalink / raw)
  To: Abhishek Sahu
  Cc: Michael Turquette, Rob Herring, Andy Gross, David Brown,
	Mark Rutland, linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1513175142-3702-11-git-send-email-absahu-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

On 12/13, Abhishek Sahu wrote:
> PCIE and NSS has MISC reset register in which single register has
> multiple reset bit. The patch adds the DT bindings for these MISC
> resets.
> 
> Signed-off-by: Abhishek Sahu <absahu-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---

Applied to clk-next

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 2/2] PCI: mediatek: Fixup class type for MT7622
From: Bjorn Helgaas @ 2017-12-22  0:27 UTC (permalink / raw)
  To: Honghui Zhang
  Cc: Yong Wu, youlin.pei, devicetree, hongkun.cao, ryder.lee, yu.yu,
	linux-pci, sean.wang, linux-kernel, yt.shen, matthias.bgg,
	lorenzo.pieralisi, linux-mediatek, xinping.qian, bhelgaas,
	yingjoe.chen, eddie.huang, linux-arm-kernel
In-Reply-To: <1513839672.25872.13.camel@mhfsdcap03>

On Thu, Dec 21, 2017 at 03:01:12PM +0800, Honghui Zhang wrote:
> On Thu, 2017-12-21 at 14:41 +0800, Yong Wu wrote:
> > On Thu, 2017-12-21 at 10:11 +0800, honghui.zhang@mediatek.com wrote:
> > > From: Honghui Zhang <honghui.zhang@mediatek.com>
> > > 
> > > The host bridge of MT7622 has hardware code the class code to an
> > > arbitrary, meaningless value, fix that.
> > > 
> > > Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
> > > ---
> > >  drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
> > >  1 file changed, 12 insertions(+)
> > > 
> > > diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
> > > index 3248771..ae8d367 100644
> > > --- a/drivers/pci/host/pcie-mediatek.c
> > > +++ b/drivers/pci/host/pcie-mediatek.c
> > > @@ -1174,3 +1174,15 @@ static struct platform_driver mtk_pcie_driver = {
> > >  	},
> > >  };
> > >  builtin_platform_driver(mtk_pcie_driver);
> > > +
> > > +/* The host bridge of MT7622 advertises the wrong device class. */
> > > +static void mtk_fixup_class(struct pci_dev *dev)
> > > +{
> > > +	dev->class = PCI_CLASS_BRIDGE_PCI << 8;
> > > +}
> > > +
> > > +/*
> > > + * The HW default value of vendor id and device id for mt7622 are 0x0e8d,
> > > + * 0x3258, which are arbitrary, meaningless values.

They may be arbitrary but they are certainly not meaningless.

> > What's the right vendor id and device id? is it possible to fix them
> > too?
> 
> Vendor ID is managed by PCI-SIG, you may get the assigned vendor ID
> from:
> https://pci-ids.ucw.cz/read/PC?restrict=

It's true that Vendor IDs are managed by the PCI-SIG.  The link above
is not managed by the PCI-SIG and is not the official list of assigned
Vendor IDs.

> The vendor ID for Mediatek Corp. should be 14c3.
> Device ID is something like vendor-defined.
> Those values are in the configuration space and are read-only defined by
> spec, it's been stored at the pci_dev, we may change the vendor and
> device values in pci_dev, but I don't think it's necessary to change
> that.
> BTW, Does anyone really cares about the vendor ID and device ID except
> the device's driver?

Yes.  This is a fundamental misunderstanding of how Vendor and Device
IDs work.  The *driver* really doesn't care about the IDs.  The PCI
*core* cares about them because it uses the IDs to select the
appropriate driver to bind to the device.

> > > +DECLARE_PCI_FIXUP_EARLY(0x0e8d, 0x3258, mtk_fixup_class);

This is absolutely not OK.  You cannot take over Vendor ID 0x0e8d
unless it has been assigned to you by the PCI-SIG.

To my knowledge, 0x0e8d has not been assigned to any company yet, but
the PCI-SIG could assign it to some new company X tomorrow.  Company X
may then build a device with Device ID 0x3258.  Now we cannot tell the
difference between the Company X device that is correctly designed and
this Mediatek device that is broken.

This quirk would improperly overwrite dev->class for the Company X
device, and we would not be able to bind the correct driver to either
device.

I am amazed that somebody could build a device that claims to be a PCI
device and get the Vendor ID wrong.  That should be literally the
first thing the hardware designer does.  If you have hardware in the
field that uses the wrong Vendor ID, it is definitely not
PCI-compliant.

Bjorn

^ permalink raw reply

* Re: [PATCH v11 0/6] Add support for Qualcomm A53 CPU clock
From: Stephen Boyd @ 2017-12-22  0:49 UTC (permalink / raw)
  To: Georgi Djakov
  Cc: jassisinghbrar, bjorn.andersson, mturquette, robh, linux-clk,
	linux-kernel, linux-arm-msm, devicetree
In-Reply-To: <20171205154701.27730-1-georgi.djakov@linaro.org>

On 12/05, Georgi Djakov wrote:
> This patchset adds support for the A53 CPU clock on MSM8916 platforms
> and allows scaling of the CPU frequency on msm8916 based platforms.

Ok. I will apply just the clk ones? Patches 3,4, and 6?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH v2 2/2] PCI: mediatek: Fixup class type for MT7622
From: Honghui Zhang @ 2017-12-22  0:56 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: Yong Wu, youlin.pei, devicetree, hongkun.cao, ryder.lee, yu.yu,
	linux-pci, sean.wang, linux-kernel, yt.shen, matthias.bgg,
	lorenzo.pieralisi, linux-mediatek, xinping.qian, bhelgaas,
	yingjoe.chen, eddie.huang, linux-arm-kernel
In-Reply-To: <20171222002712.GE30595@bhelgaas-glaptop.roam.corp.google.com>

On Thu, 2017-12-21 at 18:27 -0600, Bjorn Helgaas wrote:
> On Thu, Dec 21, 2017 at 03:01:12PM +0800, Honghui Zhang wrote:
> > On Thu, 2017-12-21 at 14:41 +0800, Yong Wu wrote:
> > > On Thu, 2017-12-21 at 10:11 +0800, honghui.zhang@mediatek.com wrote:
> > > > From: Honghui Zhang <honghui.zhang@mediatek.com>
> > > > 
> > > > The host bridge of MT7622 has hardware code the class code to an
> > > > arbitrary, meaningless value, fix that.
> > > > 
> > > > Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
> > > > ---
> > > >  drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
> > > >  1 file changed, 12 insertions(+)
> > > > 
> > > > diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
> > > > index 3248771..ae8d367 100644
> > > > --- a/drivers/pci/host/pcie-mediatek.c
> > > > +++ b/drivers/pci/host/pcie-mediatek.c
> > > > @@ -1174,3 +1174,15 @@ static struct platform_driver mtk_pcie_driver = {
> > > >  	},
> > > >  };
> > > >  builtin_platform_driver(mtk_pcie_driver);
> > > > +
> > > > +/* The host bridge of MT7622 advertises the wrong device class. */
> > > > +static void mtk_fixup_class(struct pci_dev *dev)
> > > > +{
> > > > +	dev->class = PCI_CLASS_BRIDGE_PCI << 8;
> > > > +}
> > > > +
> > > > +/*
> > > > + * The HW default value of vendor id and device id for mt7622 are 0x0e8d,
> > > > + * 0x3258, which are arbitrary, meaningless values.
> 
> They may be arbitrary but they are certainly not meaningless.
> 
> > > What's the right vendor id and device id? is it possible to fix them
> > > too?
> > 
> > Vendor ID is managed by PCI-SIG, you may get the assigned vendor ID
> > from:
> > https://pci-ids.ucw.cz/read/PC?restrict=
> 
> It's true that Vendor IDs are managed by the PCI-SIG.  The link above
> is not managed by the PCI-SIG and is not the official list of assigned
> Vendor IDs.
> 
> > The vendor ID for Mediatek Corp. should be 14c3.
> > Device ID is something like vendor-defined.
> > Those values are in the configuration space and are read-only defined by
> > spec, it's been stored at the pci_dev, we may change the vendor and
> > device values in pci_dev, but I don't think it's necessary to change
> > that.
> > BTW, Does anyone really cares about the vendor ID and device ID except
> > the device's driver?
> 
> Yes.  This is a fundamental misunderstanding of how Vendor and Device
> IDs work.  The *driver* really doesn't care about the IDs.  The PCI
> *core* cares about them because it uses the IDs to select the
> appropriate driver to bind to the device.
> 
Thanks, I was wrong about this, I had not seen that the Vendor IDs may
be assigned to another company in the future. I should try another way
to fix this.

> > > > +DECLARE_PCI_FIXUP_EARLY(0x0e8d, 0x3258, mtk_fixup_class);
> 
> This is absolutely not OK.  You cannot take over Vendor ID 0x0e8d
> unless it has been assigned to you by the PCI-SIG.
> 
> To my knowledge, 0x0e8d has not been assigned to any company yet, but
> the PCI-SIG could assign it to some new company X tomorrow.  Company X
> may then build a device with Device ID 0x3258.  Now we cannot tell the
> difference between the Company X device that is correctly designed and
> this Mediatek device that is broken.
> 
> This quirk would improperly overwrite dev->class for the Company X
> device, and we would not be able to bind the correct driver to either
> device.
> 

I will try another way to fix this, thanks very much for your explain.

> I am amazed that somebody could build a device that claims to be a PCI
> device and get the Vendor ID wrong.  That should be literally the
> first thing the hardware designer does.  If you have hardware in the
> field that uses the wrong Vendor ID, it is definitely not
> PCI-compliant.

There's an internal control register that control the Vendor ID and
device ID values, our designer leave the default value un-touched. I
will set these values in that way for the next version of fix.

Thanks.

> 
> Bjorn

^ permalink raw reply

* [PATCH] ASoC: max98373: Added Amplifier Driver
From: Ryan Lee @ 2017-12-22  1:43 UTC (permalink / raw)
  To: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k, arnd-r2nGTMty4D4,
	afd-l0cyMroinI0, robert.jarzmik-GANU6spQydw,
	supercraig0719-Re5JQEeQqe8AvxtiuMwx3w,
	ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR,
	jbrunet-rdvid1DuHRBWk0Htik3J/w, dannenberg-l0cyMroinI0,
	romain.perier-ZGY8ohtN/8qB+jHODAdFcQ,
	bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR,
	kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ,
	m-stecklein-l0cyMroinI0, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: ryan.lee.maxim-Re5JQEeQqe8AvxtiuMwx3w

Signed-off-by: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
---

Created max98373 amplifier driver.

 .../devicetree/bindings/sound/max98373.txt         |  43 +
 sound/soc/codecs/Kconfig                           |   5 +
 sound/soc/codecs/Makefile                          |   2 +
 sound/soc/codecs/max98373.c                        | 996 +++++++++++++++++++++
 sound/soc/codecs/max98373.h                        | 225 +++++
 5 files changed, 1271 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/max98373.txt
 create mode 100644 sound/soc/codecs/max98373.c
 create mode 100644 sound/soc/codecs/max98373.h

diff --git a/Documentation/devicetree/bindings/sound/max98373.txt b/Documentation/devicetree/bindings/sound/max98373.txt
new file mode 100644
index 0000000..22cd259
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/max98373.txt
@@ -0,0 +1,43 @@
+Maxim Integrated MAX98373 Speaker Amplifier
+
+This device supports I2C.
+
+Required properties:
+
+  - compatible : should be one of the following
+    - "maxim,max98373"
+
+  - reg : the I2C address of the device.
+
+Optional properties:
+
+  - maxim,vmon-slot-no : slot number used to send voltage information
+                   or in inteleave mode this will be used as
+                   interleave slot.
+                   slot range : 0 ~ 15,  Default : 0
+
+  - maxim,imon-slot-no : slot number used to send current information
+                   slot range : 0 ~ 15,  Default : 0
+
+  - maxim,spkfb-slot-no : slot number used to send speaker feedback information
+                   slot range : 0 ~ 15,  Default : 0
+
+  - maxim,interleave-mode : When using two MAX98373 in a system it is
+                   possible to create ADC data that that will
+                   overflow the frame size. Digital Audio Interleave
+                   mode provides a means to output VMON and IMON data
+                   from two devices on a single DOUT line when running
+                   smaller frames sizes such as 32 BCLKS per LRCLK or
+                   48 BCLKS per LRCLK.
+                   Range : 0 (off), 1 (on),  Default : 0
+
+Example:
+
+codec: max98373@31 {
+   compatible = "maxim,max98373";
+   reg = <0x31>;
+   maxim,vmon-slot-no = <0>;
+   maxim,imon-slot-no = <1>;
+   maxim,spkfb-slot-no = <2>;
+   maxim,interleave-mode = <0>;
+};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8b02bc8..9af2588 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX98925 if I2C
 	select SND_SOC_MAX98926 if I2C
 	select SND_SOC_MAX98927 if I2C
+	select SND_SOC_MAX98373 if I2C
 	select SND_SOC_MAX9850 if I2C
 	select SND_SOC_MAX9860 if I2C
 	select SND_SOC_MAX9768 if I2C
@@ -626,6 +627,10 @@ config SND_SOC_MAX98927
 	tristate "Maxim Integrated MAX98927 Speaker Amplifier"
 	depends on I2C
 
+config SND_SOC_MAX98373
+	tristate "Maxim Integrated MAX98373 Speaker Amplifier"
+	depends on I2C
+
 config SND_SOC_MAX9850
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0977349..49db8e9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -90,6 +90,7 @@ snd-soc-max9867-objs := max9867.o
 snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
 snd-soc-max98927-objs := max98927.o
+snd-soc-max98373-objs := max98373.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-max9860-objs := max9860.o
 snd-soc-mc13783-objs := mc13783.o
@@ -334,6 +335,7 @@ obj-$(CONFIG_SND_SOC_MAX9867)	+= snd-soc-max9867.o
 obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926)	+= snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX98927)	+= snd-soc-max98927.o
+obj-$(CONFIG_SND_SOC_MAX98373)	+= snd-soc-max98373.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MAX9860)	+= snd-soc-max9860.o
 obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
new file mode 100644
index 0000000..434c367
--- /dev/null
+++ b/sound/soc/codecs/max98373.c
@@ -0,0 +1,996 @@
+/*
+ * max98373.c  --  MAX98373 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static struct reg_default max98373_reg[] = {
+	{MAX98373_R2000_SW_RESET, 0x00},
+	{MAX98373_R2001_INT_RAW1, 0x00},
+	{MAX98373_R2002_INT_RAW2, 0x00},
+	{MAX98373_R2003_INT_RAW3, 0x00},
+	{MAX98373_R2004_INT_STATE1, 0x00},
+	{MAX98373_R2005_INT_STATE2, 0x00},
+	{MAX98373_R2006_INT_STATE3, 0x00},
+	{MAX98373_R2007_INT_FLAG1, 0x00},
+	{MAX98373_R2008_INT_FLAG2, 0x00},
+	{MAX98373_R2009_INT_FLAG3, 0x00},
+	{MAX98373_R200A_INT_EN1, 0x00},
+	{MAX98373_R200B_INT_EN2, 0x00},
+	{MAX98373_R200C_INT_EN3, 0x00},
+	{MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+	{MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+	{MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+	{MAX98373_R2010_IRQ_CTRL, 0x00},
+	{MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+	{MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+	{MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+	{MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+	{MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+	{MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+	{MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+	{MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+	{MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+	{MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+	{MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+	{MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+	{MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+	{MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+	{MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+	{MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+	{MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+	{MAX98373_R202B_PCM_RX_EN, 0x00},
+	{MAX98373_R202C_PCM_TX_EN, 0x00},
+	{MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+	{MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+	{MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+	{MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+	{MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+	{MAX98373_R2034_ICC_TX_CNTL, 0x00},
+	{MAX98373_R2035_ICC_TX_EN, 0x00},
+	{MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+	{MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+	{MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+	{MAX98373_R203F_AMP_DSP_CFG, 0x02},
+	{MAX98373_R2040_TONE_GEN_CFG, 0x00},
+	{MAX98373_R2041_AMP_CFG, 0x03},
+	{MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+	{MAX98373_R2043_AMP_EN, 0x00},
+	{MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+	{MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+	{MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+	{MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+	{MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+	{MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+	{MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+	{MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+	{MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+	{MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+	{MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+	{MAX98373_R2097_BDE_L1_THRESH, 0x00},
+	{MAX98373_R2098_BDE_L2_THRESH, 0x00},
+	{MAX98373_R2099_BDE_L3_THRESH, 0x00},
+	{MAX98373_R209A_BDE_L4_THRESH, 0x00},
+	{MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+	{MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+	{MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+	{MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+	{MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+	{MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+	{MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+	{MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+	{MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+	{MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+	{MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+	{MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+	{MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+	{MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+	{MAX98373_R20B5_BDE_EN, 0x00},
+	{MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+	{MAX98373_R20D1_DHT_CFG, 0x01},
+	{MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+	{MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+	{MAX98373_R20D4_DHT_EN, 0x00},
+	{MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+	{MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+	{MAX98373_R20E2_LIMITER_EN, 0x00},
+	{MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+	{MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+	{MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int format = 0;
+	unsigned int invert = 0;
+
+	dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE;
+		break;
+	default:
+		dev_err(codec->dev, "DAI invert mode unsupported\n");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2026_PCM_CLOCK_RATIO,
+		MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE,
+		invert);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		format = MAX98373_PCM_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		format = MAX98373_PCM_FORMAT_LJ;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		format = MAX98373_PCM_FORMAT_TDM_MODE1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		format = MAX98373_PCM_FORMAT_TDM_MODE0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	max98373->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2024_PCM_DATA_FMT_CFG,
+		MAX98373_PCM_MODE_CFG_FORMAT_MASK,
+		format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT);
+
+	return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+	32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98373_get_bclk_sel(int bclk)
+{
+	int i;
+	/* match BCLKs per LRCLK */
+	for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+		if (bclk_sel_table[i] == bclk)
+			return i + 2;
+	}
+	return 0;
+}
+static int max98373_set_clock(struct max98373_priv *max98373,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_codec *codec = max98373->codec;
+	/* BCLK/LRCLK ratio calculation */
+	int blr_clk_ratio = params_channels(params) * max98373->ch_size;
+	int value;
+
+	if (!max98373->tdm_mode) {
+		/* BCLK configuration */
+		value = max98373_get_bclk_sel(blr_clk_ratio);
+		if (!value) {
+			dev_err(codec->dev, "format unsupported %d\n",
+				params_format(params));
+			return -EINVAL;
+		}
+
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2026_PCM_CLOCK_RATIO,
+			MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+			value);
+	}
+	return 0;
+}
+
+static int max98373_dai_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+	unsigned int sampling_rate = 0;
+	unsigned int chan_sz = 0;
+
+	/* pcm mode configuration */
+	switch (snd_pcm_format_width(params_format(params))) {
+	case 16:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+		break;
+	case 24:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+		break;
+	case 32:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+		break;
+	default:
+		dev_err(codec->dev, "format unsupported %d\n",
+			params_format(params));
+		goto err;
+	}
+
+	max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2024_PCM_DATA_FMT_CFG,
+		MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+	dev_dbg(codec->dev, "format supported %d",
+		params_format(params));
+
+	/* sampling rate configuration */
+	switch (params_rate(params)) {
+	case 8000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+		break;
+	case 11025:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+		break;
+	case 12000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+		break;
+	case 16000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+		break;
+	case 22050:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+		break;
+	case 24000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+		break;
+	case 32000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+		break;
+	case 44100:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+		break;
+	case 48000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+		break;
+	default:
+		dev_err(codec->dev, "rate %d not supported\n",
+			params_rate(params));
+		goto err;
+	}
+	/* set DAI_SR to correct LRCLK frequency */
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2027_PCM_SR_SETUP_1,
+		MAX98373_PCM_SR_SET1_SR_MASK,
+		sampling_rate);
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2028_PCM_SR_SETUP_2,
+		MAX98373_PCM_SR_SET2_SR_MASK,
+		sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+	/* set sampling rate of IV */
+	if (max98373->interleave_mode &&
+	    sampling_rate > MAX98373_PCM_SR_SET1_SR_16000)
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2028_PCM_SR_SETUP_2,
+			MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+			sampling_rate - 3);
+	else
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2028_PCM_SR_SETUP_2,
+			MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+			sampling_rate);
+
+	return max98373_set_clock(max98373, params);
+err:
+	return -EINVAL;
+}
+
+static int max98373_dai_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask,
+	int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+	int bsel = 0;
+	unsigned int chan_sz = 0;
+	unsigned int mask;
+	int x, slot_found;
+
+	max98373->tdm_mode = true;
+
+	/* BCLK configuration */
+	bsel = max98373_get_bclk_sel(slots * slot_width);
+	if (bsel == 0) {
+		dev_err(codec->dev, "BCLK %d not supported\n",
+			slots * slot_width);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2026_PCM_CLOCK_RATIO,
+		MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+		bsel);
+
+	/* Channel size configuration */
+	switch (slot_width) {
+	case 16:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+		break;
+	case 24:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+		break;
+	case 32:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+		break;
+	default:
+		dev_err(codec->dev, "format unsupported %d\n",
+			slot_width);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2024_PCM_DATA_FMT_CFG,
+		MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+	/* Rx slot configuration */
+	slot_found = 0;
+	mask = rx_mask;
+	for (x = 0 ; x < 16 ; x++, mask >>= 1) {
+		if (mask & 0x1) {
+			if (slot_found == 0)
+				regmap_update_bits(max98373->regmap,
+					MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+					MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x);
+			else
+				regmap_write(max98373->regmap,
+					MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+					x);
+			slot_found++;
+			if (slot_found > 1)
+				break;
+		}
+	}
+
+	/* Tx slot Hi-Z configuration */
+	regmap_write(max98373->regmap,
+		MAX98373_R2020_PCM_TX_HIZ_EN_1,
+		~tx_mask & 0xFF);
+	regmap_write(max98373->regmap,
+		MAX98373_R2021_PCM_TX_HIZ_EN_2,
+		(~tx_mask & 0xFF00) >> 8);
+
+	return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98373_dai_set_sysclk(struct snd_soc_dai *dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+
+	max98373->sysclk = freq;
+	return 0;
+}
+
+static const struct snd_soc_dai_ops max98373_dai_ops = {
+	.set_sysclk = max98373_dai_set_sysclk,
+	.set_fmt = max98373_dai_set_fmt,
+	.hw_params = max98373_dai_hw_params,
+	.set_tdm_slot = max98373_dai_tdm_slot,
+};
+
+static int max98373_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R20FF_GLOBAL_SHDN,
+			MAX98373_GLOBAL_EN_MASK, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R20FF_GLOBAL_SHDN,
+			MAX98373_GLOBAL_EN_MASK, 0);
+		max98373->tdm_mode = 0;
+		break;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+static const char * const max98373_switch_text[] = {
+	"Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+	SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+		MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+		3, max98373_switch_text);
+
+static const struct snd_kcontrol_new max98373_dai_controls =
+	SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98373_vi_control =
+	SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_kcontrol_new max98373_spkfb_control =
+	SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = {
+SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+	MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event,
+	SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+	&max98373_dai_controls),
+SND_SOC_DAPM_OUTPUT("BE_OUT"),
+SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+	MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0),
+SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+	MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0),
+SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0,
+	SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+	&max98373_vi_control),
+SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0,
+	&max98373_spkfb_control),
+SND_SOC_DAPM_SIGGEN("VMON"),
+SND_SOC_DAPM_SIGGEN("IMON"),
+SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0);
+static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv,
+	0, 8, TLV_DB_SCALE_ITEM(0, 50, 0),
+	9, 10, TLV_DB_SCALE_ITEM(500, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv,
+	0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv,
+	0, 1, TLV_DB_SCALE_ITEM(25, 25, 0),
+	2, 4, TLV_DB_SCALE_ITEM(100, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv,
+	0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv,
+	0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
+	2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0),
+	8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0),
+	10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0),
+	12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
+	14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv,
+	0, 15, TLV_DB_SCALE_ITEM(0, -100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv,
+	0, 60, TLV_DB_SCALE_ITEM(0, -25, 0),
+);
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3:
+	case MAX98373_R2010_IRQ_CTRL:
+	case MAX98373_R2014_THERM_WARN_THRESH
+		... MAX98373_R2018_THERM_FOLDBACK_EN:
+	case MAX98373_R201E_PIN_DRIVE_STRENGTH
+		... MAX98373_R2036_SOUNDWIRE_CTRL:
+	case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+	case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+		... MAX98373_R2047_IV_SENSE_ADC_EN:
+	case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+		... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+	case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+	case MAX98373_R2097_BDE_L1_THRESH
+		... MAX98373_R209B_BDE_THRESH_HYST:
+	case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+	case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+	case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+	case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+	case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+		... MAX98373_R20FF_GLOBAL_SHDN:
+	case MAX98373_R21FF_REV_ID:
+		return true;
+	default:
+		return false;
+	}
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+	case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+	case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+	case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+	case MAX98373_R21FF_REV_ID:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char * const max98373_output_voltage_lvl_text[] = {
+	"5.43V", "6.09V", "6.83V", "7.67V", "8.60V",
+	"9.65V", "10.83V", "12.15V", "13.63V", "15.29V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum,
+			    MAX98373_R203E_AMP_PATH_GAIN, 0,
+			    max98373_output_voltage_lvl_text);
+
+static const char * const max98373_dht_attack_rate_text[] = {
+	"17.5us", "35us", "70us", "140us",
+	"280us", "560us", "1120us", "2240us"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum,
+			    MAX98373_R20D2_DHT_ATTACK_CFG, 0,
+			    max98373_dht_attack_rate_text);
+
+static const char * const max98373_dht_release_rate_text[] = {
+	"45ms", "225ms", "450ms", "1150ms",
+	"2250ms", "3100ms", "4500ms", "6750ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum,
+			    MAX98373_R20D3_DHT_RELEASE_CFG, 0,
+			    max98373_dht_release_rate_text);
+
+static const char * const max98373_limiter_attack_rate_text[] = {
+	"10us", "20us", "40us", "80us",
+	"160us", "320us", "640us", "1.28ms",
+	"2.56ms", "5.12ms", "10.24ms", "20.48ms",
+	"40.96ms", "81.92ms", "16.384ms", "32.768ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum,
+			    MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4,
+			    max98373_limiter_attack_rate_text);
+
+static const char * const max98373_limiter_release_rate_text[] = {
+	"40us", "80us", "160us", "320us",
+	"640us", "1.28ms", "2.56ms", "5.120ms",
+	"10.24ms", "20.48ms", "40.96ms", "81.92ms",
+	"163.84ms", "327.68ms", "655.36ms", "1310.72ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum,
+			    MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0,
+			    max98373_limiter_release_rate_text);
+
+static const char * const max98373_ADC_samplerate_text[] = {
+	"333kHz", "192kHz", "64kHz", "48kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
+			    MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
+			    max98373_ADC_samplerate_text);
+
+static const struct snd_kcontrol_new max98373_snd_controls[] = {
+SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+	MAX98373_CLOCK_MON_SHIFT, 1, 0),
+SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0),
+SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG,
+	MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL,
+	0, 0x7F, 0, max98373_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN,
+	MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv),
+SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN,
+	MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv),
+SOC_ENUM("Output Voltage", max98373_out_volt_enum),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
+	MAX98373_DHT_EN_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Gain Min", MAX98373_R20D1_DHT_CFG,
+	MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv),
+SOC_SINGLE_TLV("DHT Rot Pnt", MAX98373_R20D1_DHT_CFG,
+	MAX98373_DHT_ROT_PNT_SHIFT, 15, 0, max98373_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Attack Step", MAX98373_R20D2_DHT_ATTACK_CFG,
+	MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_SINGLE_TLV("DHT Release Step", MAX98373_R20D3_DHT_RELEASE_CFG,
+	MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+	MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+	MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+	0, 0x3, 0),
+SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+	0, 0x3, 0),
+SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum),
+/* Brownout Detection Engine */
+SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+	MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+	MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0),
+SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
+SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
+SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
+SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0),
+SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0),
+SOC_SINGLE_TLV("BDE LVL1 Clip Thresh", MAX98373_R20A9_BDE_L1_CFG_2,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Thresh", MAX98373_R20AC_BDE_L2_CFG_2,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Thresh", MAX98373_R20AF_BDE_L3_CFG_2,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Thresh", MAX98373_R20B2_BDE_L4_CFG_2,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Clip Gain Reduct", MAX98373_R20AA_BDE_L1_CFG_3,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Gain Reduct", MAX98373_R20AD_BDE_L2_CFG_3,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Gain Reduct", MAX98373_R20B0_BDE_L3_CFG_3,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Gain Reduct", MAX98373_R20B3_BDE_L4_CFG_3,
+	0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh", MAX98373_R20A8_BDE_L1_CFG_1,
+	0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh", MAX98373_R20AB_BDE_L2_CFG_1,
+	0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh", MAX98373_R20AE_BDE_L3_CFG_1,
+	0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh", MAX98373_R20B1_BDE_L4_CFG_1,
+	0, 0xF, 0, max98373_limiter_thresh_tlv),
+/* Limiter */
+SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
+	MAX98373_LIMITER_EN_SHIFT, 1, 0),
+SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG,
+	MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Limiter Thresh", MAX98373_R20E0_LIMITER_THRESH_CFG,
+	MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv),
+SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum),
+SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum),
+};
+
+static const struct snd_soc_dapm_route max98373_audio_map[] = {
+	/* Plabyack */
+	{"DAI Sel Mux", "Left", "Amp Enable"},
+	{"DAI Sel Mux", "Right", "Amp Enable"},
+	{"DAI Sel Mux", "LeftRight", "Amp Enable"},
+	{"BE_OUT", NULL, "DAI Sel Mux"},
+	/* Capture */
+	{ "VI Sense", "Switch", "VMON" },
+	{ "VI Sense", "Switch", "IMON" },
+	{ "SpkFB Sense", "Switch", "FBMON" },
+	{ "Voltage Sense", NULL, "VI Sense" },
+	{ "Current Sense", NULL, "VI Sense" },
+	{ "Speaker FB Sense", NULL, "SpkFB Sense" },
+};
+
+static struct snd_soc_dai_driver max98373_dai[] = {
+	{
+		.name = "max98373-aif1",
+		.playback = {
+			.stream_name = "HiFi Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98373_RATES,
+			.formats = MAX98373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "HiFi Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98373_RATES,
+			.formats = MAX98373_FORMATS,
+		},
+		.ops = &max98373_dai_ops,
+	}
+};
+
+static int max98373_probe(struct snd_soc_codec *codec)
+{
+	struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+
+	max98373->codec = codec;
+	codec->control_data = max98373->regmap;
+
+	/* Software Reset */
+	regmap_write(max98373->regmap,
+		MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
+
+	/* IV default slot configuration */
+	regmap_write(max98373->regmap,
+		MAX98373_R2020_PCM_TX_HIZ_EN_1,
+		0xFF);
+	regmap_write(max98373->regmap,
+		MAX98373_R2021_PCM_TX_HIZ_EN_2,
+		0xFF);
+	/* L/R mix configuration */
+	regmap_write(max98373->regmap,
+		MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+		0x80);
+	regmap_write(max98373->regmap,
+		MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+		0x1);
+	/* Set inital volume (0dB) */
+	regmap_write(max98373->regmap,
+		MAX98373_R203D_AMP_DIG_VOL_CTRL,
+		0x00);
+	regmap_write(max98373->regmap,
+		MAX98373_R203E_AMP_PATH_GAIN,
+		0x00);
+	/* Enable DC blocker */
+	regmap_write(max98373->regmap,
+		MAX98373_R203F_AMP_DSP_CFG,
+		0x3);
+	/* Enable IMON VMON DC blocker */
+	regmap_write(max98373->regmap,
+		MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+		0x7);
+	/* voltage, current slot configuration */
+	regmap_write(max98373->regmap,
+		MAX98373_R2022_PCM_TX_SRC_1,
+		(max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+		max98373->v_slot) & 0xFF);
+	if (max98373->v_slot < 8)
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2020_PCM_TX_HIZ_EN_1,
+			1 << max98373->v_slot, 0);
+	else
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2021_PCM_TX_HIZ_EN_2,
+			1 << (max98373->v_slot - 8), 0);
+
+	if (max98373->i_slot < 8)
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2020_PCM_TX_HIZ_EN_1,
+			1 << max98373->i_slot, 0);
+	else
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2021_PCM_TX_HIZ_EN_2,
+			1 << (max98373->i_slot - 8), 0);
+
+	/* speaker feedback slot configuration */
+	regmap_write(max98373->regmap,
+		MAX98373_R2023_PCM_TX_SRC_2,
+		max98373->spkfb_slot & 0xFF);
+
+	/* Set interleave mode */
+	if (max98373->interleave_mode)
+		regmap_update_bits(max98373->regmap,
+			MAX98373_R2024_PCM_DATA_FMT_CFG,
+			MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+			MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+	/* Speaker enable */
+	regmap_update_bits(max98373->regmap,
+		MAX98373_R2043_AMP_EN,
+		MAX98373_SPK_EN_MASK, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98373_suspend(struct device *dev)
+{
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+	regcache_cache_only(max98373->regmap, true);
+	regcache_mark_dirty(max98373->regmap);
+	return 0;
+}
+static int max98373_resume(struct device *dev)
+{
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+	regmap_write(max98373->regmap,
+		MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
+	regcache_cache_only(max98373->regmap, false);
+	regcache_sync(max98373->regmap);
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98373_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+};
+
+static const struct snd_soc_codec_driver soc_codec_dev_max98373 = {
+	.probe = max98373_probe,
+	.component_driver = {
+		.controls = max98373_snd_controls,
+		.num_controls = ARRAY_SIZE(max98373_snd_controls),
+		.dapm_widgets = max98373_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+		.dapm_routes = max98373_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+	},
+};
+
+static const struct regmap_config max98373_regmap = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = MAX98373_R21FF_REV_ID,
+	.reg_defaults  = max98373_reg,
+	.num_reg_defaults = ARRAY_SIZE(max98373_reg),
+	.readable_reg = max98373_readable_register,
+	.volatile_reg = max98373_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static void max98373_slot_config(struct i2c_client *i2c,
+	struct max98373_priv *max98373)
+{
+	int value;
+	struct device *dev = &i2c->dev;
+
+	if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+		max98373->v_slot = value & 0xF;
+	else
+		max98373->v_slot = 0;
+
+	if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+		max98373->i_slot = value & 0xF;
+	else
+		max98373->i_slot = 1;
+
+	if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+		max98373->spkfb_slot = value & 0xF;
+	else
+		max98373->spkfb_slot = 2;
+}
+
+static int max98373_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+
+	int ret = 0, value;
+	int reg = 0;
+	struct max98373_priv *max98373 = NULL;
+
+	max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
+
+	if (!max98373) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	i2c_set_clientdata(i2c, max98373);
+
+	/* update interleave mode info */
+	if (!device_property_read_u32(&i2c->dev,
+	    "maxim,interleave_mode", &value)) {
+		if (value > 0)
+			max98373->interleave_mode = 1;
+		else
+			max98373->interleave_mode = 0;
+	} else
+		max98373->interleave_mode = 0;
+
+	/* regmap initialization */
+	max98373->regmap
+		= devm_regmap_init_i2c(i2c, &max98373_regmap);
+	if (IS_ERR(max98373->regmap)) {
+		ret = PTR_ERR(max98373->regmap);
+		dev_err(&i2c->dev,
+			"Failed to allocate regmap: %d\n", ret);
+		return ret;
+	}
+
+	/* Check Revision ID */
+	ret = regmap_read(max98373->regmap,
+		MAX98373_R21FF_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c->dev,
+			"Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID);
+		return ret;
+	}
+	dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg);
+
+	/* voltage/current slot configuration */
+	max98373_slot_config(i2c, max98373);
+
+	/* codec registeration */
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98373,
+		max98373_dai, ARRAY_SIZE(max98373_dai));
+	if (ret < 0)
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+	return ret;
+}
+
+static int max98373_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id max98373_i2c_id[] = {
+	{ "max98373", 0},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98373_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+	{ .compatible = "maxim,max98373", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+	{ "MX98373", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static struct i2c_driver max98373_i2c_driver = {
+	.driver = {
+		.name = "max98373",
+		.of_match_table = of_match_ptr(max98373_of_match),
+		.acpi_match_table = ACPI_PTR(max98373_acpi_match),
+		.pm = &max98373_pm,
+	},
+	.probe = max98373_i2c_probe,
+	.remove = max98373_i2c_remove,
+	.id_table = max98373_i2c_id,
+};
+
+module_i2c_driver(max98373_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
new file mode 100644
index 0000000..bbb2271
--- /dev/null
+++ b/sound/soc/codecs/max98373.h
@@ -0,0 +1,225 @@
+/*
+ * max98373.h  --  MAX98373 ALSA Soc Audio driver
+ *
+ * Copyright 2017 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+#ifndef _MAX98373_H
+#define _MAX98373_H
+
+#define MAX98373_R2000_SW_RESET 0x2000
+#define MAX98373_R2001_INT_RAW1 0x2001
+#define MAX98373_R2002_INT_RAW2 0x2002
+#define MAX98373_R2003_INT_RAW3 0x2003
+#define MAX98373_R2004_INT_STATE1 0x2004
+#define MAX98373_R2005_INT_STATE2 0x2005
+#define MAX98373_R2006_INT_STATE3 0x2006
+#define MAX98373_R2007_INT_FLAG1 0x2007
+#define MAX98373_R2008_INT_FLAG2 0x2008
+#define MAX98373_R2009_INT_FLAG3 0x2009
+#define MAX98373_R200A_INT_EN1 0x200A
+#define MAX98373_R200B_INT_EN2 0x200B
+#define MAX98373_R200C_INT_EN3 0x200C
+#define MAX98373_R200D_INT_FLAG_CLR1 0x200D
+#define MAX98373_R200E_INT_FLAG_CLR2 0x200E
+#define MAX98373_R200F_INT_FLAG_CLR3 0x200F
+#define MAX98373_R2010_IRQ_CTRL 0x2010
+#define MAX98373_R2014_THERM_WARN_THRESH 0x2014
+#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015
+#define MAX98373_R2016_THERM_HYSTERESIS 0x2016
+#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017
+#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018
+#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E
+#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020
+#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021
+#define MAX98373_R2022_PCM_TX_SRC_1 0x2022
+#define MAX98373_R2023_PCM_TX_SRC_2 0x2023
+#define MAX98373_R2024_PCM_DATA_FMT_CFG	0x2024
+#define MAX98373_R2025_AUDIO_IF_MODE 0x2025
+#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026
+#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027
+#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028
+#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029
+#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A
+#define MAX98373_R202B_PCM_RX_EN 0x202B
+#define MAX98373_R202C_PCM_TX_EN 0x202C
+#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E
+#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F
+#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030
+#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031
+#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032
+#define MAX98373_R2034_ICC_TX_CNTL 0x2034
+#define MAX98373_R2035_ICC_TX_EN 0x2035
+#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036
+#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D
+#define MAX98373_R203E_AMP_PATH_GAIN 0x203E
+#define MAX98373_R203F_AMP_DSP_CFG 0x203F
+#define MAX98373_R2040_TONE_GEN_CFG 0x2040
+#define MAX98373_R2041_AMP_CFG 0x2041
+#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042
+#define MAX98373_R2043_AMP_EN 0x2043
+#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046
+#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047
+#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051
+#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052
+#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053
+#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054
+#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055
+#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056
+#define MAX98373_R2090_BDE_LVL_HOLD 0x2090
+#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091
+#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092
+#define MAX98373_R2097_BDE_L1_THRESH 0x2097
+#define MAX98373_R2098_BDE_L2_THRESH 0x2098
+#define MAX98373_R2099_BDE_L3_THRESH 0x2099
+#define MAX98373_R209A_BDE_L4_THRESH 0x209A
+#define MAX98373_R209B_BDE_THRESH_HYST 0x209B
+#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8
+#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9
+#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA
+#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB
+#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC
+#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD
+#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE
+#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF
+#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0
+#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1
+#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2
+#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3
+#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4
+#define MAX98373_R20B5_BDE_EN 0x20B5
+#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6
+#define MAX98373_R20D1_DHT_CFG 0x20D1
+#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2
+#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3
+#define MAX98373_R20D4_DHT_EN 0x20D4
+#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0
+#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1
+#define MAX98373_R20E2_LIMITER_EN 0x20E2
+#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE
+#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF
+#define MAX98373_R21FF_REV_ID 0x21FF
+
+/* MAX98373_R2022_PCM_TX_SRC_1 */
+#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98373_R2024_PCM_DATA_FMT_CFG */
+#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98373_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98373_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98373_R2026_PCM_CLOCK_RATIO */
+#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98373_R2027_PCM_SR_SETUP_1 */
+#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98373_R2028_PCM_SR_SETUP_2 */
+#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98373_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0)
+
+/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+
+/* MAX98373_R203E_AMP_PATH_GAIN */
+#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98373_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98373_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98373_R203F_AMP_DSP_CFG */
+#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0)
+#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1)
+#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2)
+#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3)
+#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5)
+#define MAX98373_AMP_VOL_SEL_SHIFT (7)
+
+/* MAX98373_R2043_AMP_EN */
+#define MAX98373_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98373_SPK_EN_MASK (0x1 << 0)
+#define MAX98373_SPKFB_EN_SHIFT (1)
+
+/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98373_FLT_EN_SHIFT (4)
+
+/* MAX98373_R20B2_BDE_L4_CFG_2 */
+#define MAX98373_LVL4_MUTE_EN_SHIFT (7)
+#define MAX98373_LVL4_HOLD_EN_SHIFT (6)
+
+/* MAX98373_R20B5_BDE_EN */
+#define MAX98373_BDE_EN_SHIFT (0)
+
+/* MAX98373_R20D1_DHT_CFG */
+#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT	(4)
+#define MAX98373_DHT_ROT_PNT_SHIFT	(0)
+
+/* MAX98373_R20D2_DHT_ATTACK_CFG */
+#define MAX98373_DHT_ATTACK_STEP_SHIFT (3)
+#define MAX98373_DHT_ATTACK_RATE_SHIFT (0)
+
+/* MAX98373_R20D3_DHT_RELEASE_CFG */
+#define MAX98373_DHT_RELEASE_STEP_SHIFT (3)
+#define MAX98373_DHT_RELEASE_RATE_SHIFT (0)
+
+/* MAX98373_R20D4_DHT_EN */
+#define MAX98373_DHT_EN_SHIFT (0)
+
+/* MAX98373_R20E0_LIMITER_THRESH_CFG */
+#define MAX98373_LIMITER_THRESH_SHIFT (2)
+#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0)
+
+/* MAX98373_R20E2_LIMITER_EN */
+#define MAX98373_LIMITER_EN_SHIFT (0)
+
+/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */
+#define MAX98373_CLOCK_MON_SHIFT (0)
+
+/* MAX98373_R20FF_GLOBAL_SHDN */
+#define MAX98373_GLOBAL_EN_MASK (0x1 << 0)
+
+/* MAX98373_R2000_SW_RESET */
+#define MAX98373_SOFT_RESET (0x1 << 0)
+
+struct max98373_priv {
+	struct regmap *regmap;
+	struct snd_soc_codec *codec;
+	unsigned int sysclk;
+	unsigned int v_slot;
+	unsigned int i_slot;
+	unsigned int spkfb_slot;
+	bool interleave_mode;
+	unsigned int ch_size;
+	unsigned int iface;
+	bool tdm_mode;
+};
+#endif
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH 01/10] soc: qcom: Separate kryo l2 accessors from PMU driver
From: Stephen Boyd @ 2017-12-22  2:06 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Ilia Lin, linux-clk, linux-arm-kernel, linux-arm-msm, devicetree,
	will.deacon, rnayak, qualcomm-lt, celster, tfinkel
In-Reply-To: <20171212140302.au4wart4uhwm7lfq@lakrids.cambridge.arm.com>

On 12/12, Mark Rutland wrote:
> On Tue, Dec 12, 2017 at 02:31:28PM +0200, Ilia Lin wrote:
> > + * accesses, and system registers with respect to device memory
> > + */
> > +void set_l2_indirect_reg(u64 reg, u64 val)
> > +{
> > +	unsigned long flags;
> > +	mb();
> 
> We didn't need this for the PMU driver, so it's unfortuante that it now
> has to pay the cost.
> 
> Can we please factor this mb() into the callers that need it?

+1

> 
> > +	raw_spin_lock_irqsave(&l2_access_lock, flags);
> > +	write_sysreg_s(reg, L2CPUSRSELR_EL1);
> > +	isb();
> > +	write_sysreg_s(val, L2CPUSRDR_EL1);
> > +	isb();
> > +	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
> > +}
> > +EXPORT_SYMBOL(set_l2_indirect_reg);
> 
> [...]
> 
> > +#ifdef CONFIG_ARCH_QCOM
> > +void set_l2_indirect_reg(u64 reg_addr, u64 val);
> > +u64 get_l2_indirect_reg(u64 reg_addr);
> > +#else
> > +static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
> > +static inline u32 get_l2_indirect_reg(u32 reg_addr)
> > +{
> > +	return 0;
> > +}
> > +#endif
> > +#endif
> 
> Are there any drivers that will bne built for !CONFIG_ARCH_QCOM that
> reference this?
> 
> It might be better to not have the stub versions, so that we get a
> build-error if they are erroneously used.
> 

Does that approach make COMPILE_TEST break?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH] ASoC: max98373: Added Amplifier Driver
From: Kuninori Morimoto @ 2017-12-22  2:24 UTC (permalink / raw)
  To: Ryan Lee
  Cc: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k, arnd-r2nGTMty4D4,
	afd-l0cyMroinI0, robert.jarzmik-GANU6spQydw,
	supercraig0719-Re5JQEeQqe8AvxtiuMwx3w,
	jbrunet-rdvid1DuHRBWk0Htik3J/w, dannenberg-l0cyMroinI0,
	romain.perier-ZGY8ohtN/8qB+jHODAdFcQ,
	bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR,
	m-stecklein-l0cyMroinI0, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	ryan.lee.maxim-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <1513907030-18441-1-git-send-email-ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>


Hi Ryan

> Signed-off-by: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
> ---
> 
> Created max98373 amplifier driver.
> 
>  .../devicetree/bindings/sound/max98373.txt         |  43 +
>  sound/soc/codecs/Kconfig                           |   5 +
>  sound/soc/codecs/Makefile                          |   2 +
>  sound/soc/codecs/max98373.c                        | 996 +++++++++++++++++++++
>  sound/soc/codecs/max98373.h                        | 225 +++++
>  5 files changed, 1271 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/max98373.txt
>  create mode 100644 sound/soc/codecs/max98373.c
>  create mode 100644 sound/soc/codecs/max98373.h
(snip)
> +struct max98373_priv {
> +	struct regmap *regmap;
> +	struct snd_soc_codec *codec;
> +	unsigned int sysclk;
> +	unsigned int v_slot;
> +	unsigned int i_slot;
> +	unsigned int spkfb_slot;
> +	bool interleave_mode;
> +	unsigned int ch_size;
> +	unsigned int iface;
> +	bool tdm_mode;
> +};

About this max98373->codec.
This user is only max98373_set_clock(), and it is called from
max98373_dai_hw_params().
You are getting *codec from dai->codec in this function,
and max98373 is came from it.
This means, we can remove max98373->codec ?

About max98373->regmap.
You are using devm_regmap_init_i2c(), and keeping it on max98373.
Can you check snd_soc_component_add() which is called from
snd_soc_add_component().
It will set component->regmap if you called devm_regmap_init_i2c().
I think you can use snd_soc_component_read/write instead of regmap_read/write.
Then, we can remove max98373->regmap too ?
Ahh, you want to check Revision ID.
then, snd_soc_component_init_regmap() can help you ?

Best regards
---
Kuninori Morimoto
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 2/2] PCI: mediatek: Fixup class type for MT7622
From: Bjorn Helgaas @ 2017-12-22  3:15 UTC (permalink / raw)
  To: Honghui Zhang
  Cc: Yong Wu, youlin.pei-NuS5LvNUpcJWk0Htik3J/w,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	hongkun.cao-NuS5LvNUpcJWk0Htik3J/w,
	ryder.lee-NuS5LvNUpcJWk0Htik3J/w, yu.yu-NuS5LvNUpcJWk0Htik3J/w,
	linux-pci-u79uwXL29TY76Z2rM5mHXA,
	sean.wang-NuS5LvNUpcJWk0Htik3J/w,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	yt.shen-NuS5LvNUpcJWk0Htik3J/w,
	matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w,
	lorenzo.pieralisi-5wv7dgnIgG8,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	xinping.qian-NuS5LvNUpcJWk0Htik3J/w,
	bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
	yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w,
	eddie.huang-NuS5LvNUpcJWk0Htik3J/w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1513904194.25872.23.camel@mhfsdcap03>

On Fri, Dec 22, 2017 at 08:56:34AM +0800, Honghui Zhang wrote:
> There's an internal control register that control the Vendor ID and
> device ID values, our designer leave the default value un-touched. I
> will set these values in that way for the next version of fix.

Then there's no problem.  The mtk_pcie driver is a platform driver
that claims the host controller based on an of_device_id from a device
tree.

Apparently the bridge is also materialized in PCI config space, which
is typical for x86 host bridges, and makes the bridge appear in
"lspci".  But drivers generally don't claim bridges that way.

You can just program the Vendor and Device IDs by writing the internal
control registers somewhere in the mtk_pci_probe() path.

Then you can set the class code the same way, using an internal
control register (if that's possible), or using a quirk with the
correct Mediatek Vendor ID (if there is no internal writable
register).

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/2] [media] Add Rockchip RK1608 driver
From: Jacob Chen @ 2017-12-22  4:33 UTC (permalink / raw)
  To: Leo Wen
  Cc: Mauro Carvalho Chehab, robh+dt, mark.rutland, davem, gregkh,
	rdunlap, Linux Media Mailing List, devicetree, linux-kernel,
	open list:ARM/Rockchip SoC..., Eddie Cai
In-Reply-To: <1513060095-29588-2-git-send-email-leo.wen@rock-chips.com>

Hi leo,


2017-12-12 14:28 GMT+08:00 Leo Wen <leo.wen@rock-chips.com>:
> Rk1608 is used as a PreISP to link on Soc, which mainly has two functions.
> One is to download the firmware of RK1608, and the other is to match the
> extra sensor such as camera and enable sensor by calling sensor's s_power.
>
> use below v4l2-ctl command to capture frames.
>
>     v4l2-ctl --verbose -d /dev/video1 --stream-mmap=2
>     --stream-to=/tmp/stream.out --stream-count=60 --stream-poll
>
> use below command to playback the video on your PC.
>
>     mplayer ./stream.out -loop 0 -demuxer rawvideo -rawvideo
>     w=640:h=480:size=$((640*480*3/2)):format=NV12
>
> Signed-off-by: Leo Wen <leo.wen@rock-chips.com>
> ---
>  MAINTAINERS                |    6 +
>  drivers/media/spi/Makefile |    1 +
>  drivers/media/spi/rk1608.c | 1165 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/media/spi/rk1608.h |  366 ++++++++++++++
>  4 files changed, 1538 insertions(+)
>  create mode 100644 drivers/media/spi/rk1608.c
>  create mode 100644 drivers/media/spi/rk1608.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 82ad0ea..48235d8 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -128,6 +128,12 @@ Maintainers List (try to look for most precise areas first)
>
>                 -----------------------------------
>
> +ROCKCHIP RK1608 DRIVER
> +M:     Leo Wen <leo.wen@rock-chips.com>
> +S:     Maintained
> +F:     drivers/media/platform/spi/rk1608.c
> +F:     drivers/media/platform/spi/rk1608.h
> +
>  3C59X NETWORK DRIVER
>  M:     Steffen Klassert <klassert@mathematik.tu-chemnitz.de>
>  L:     netdev@vger.kernel.org
> diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
> index ea64013..9d9d9ec 100644
> --- a/drivers/media/spi/Makefile
> +++ b/drivers/media/spi/Makefile
> @@ -1 +1,2 @@
>  obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
> +obj-$(CONFIG_ROCKCHIP_RK1608) += rk1608.o
> diff --git a/drivers/media/spi/rk1608.c b/drivers/media/spi/rk1608.c
> new file mode 100644
> index 0000000..e646204
> --- /dev/null
> +++ b/drivers/media/spi/rk1608.c
> @@ -0,0 +1,1165 @@
> +/**
> + * Rockchip rk1608 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses.  You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + *     Redistribution and use in source and binary forms, with or
> + *     without modification, are permitted provided that the following
> + *     conditions are met:
> + *
> + *      - Redistributions of source code must retain the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer.
> + *
> + *      - Redistributions in binary form must reproduce the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer in the documentation and/or other materials
> + *        provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +#include <linux/delay.h>
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-image-sizes.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/v4l2-of.h>
> +#include "rk1608.h"
> +
> +/**
> + * Rk1608 is used as the Pre-ISP to link on Soc, which mainly has two
> + * functions. One is to download the firmware of RK1608, and the other
> + * is to match the extra sensor such as camera and enable sensor by
> + * calling sensor's s_power.
> + *     |-----------------------|
> + *     |     Sensor Camera     |
> + *     |-----------------------|
> + *     |-----------||----------|
> + *     |-----------||----------|
> + *     |-----------\/----------|
> + *     |     Pre-ISP RK1608    |
> + *     |-----------------------|
> + *     |-----------||----------|
> + *     |-----------||----------|
> + *     |-----------\/----------|
> + *     |      Rockchip Soc     |
> + *     |-----------------------|
> + * Data Transfer As shown above. In RK1608, the data received from the
> + * extra sensor,and it is passed to the Soc through ISP.
> + */
> +struct rk1608_state {
> +       struct v4l2_subdev      sd;
> +       struct v4l2_subdev      *sensor_sd;
> +       struct device           *dev;
> +       struct spi_device       *spi;
> +       struct media_pad        pad;
> +       struct clk                      *mclk;
> +       struct mutex            lock;           /* protect resource */
> +       struct mutex            sensor_lock;    /* protect sensor */
> +       struct mutex            send_msg_lock;  /* protect msg */
> +       int power_count;
> +       int reset_gpio;
> +       int reset_active;
> +       int irq_gpio;
> +       int irq;
> +       int sleepst_gpio;
> +       int sleepst_irq;
> +       int wakeup_gpio;
> +       int wakeup_active;
> +       int powerdown_gpio;
> +       int powerdown_active;
> +       int msg_num;
> +       u32 sensor_cnt;
> +       u32 sensor_nums;
> +       u32 max_speed_hz;
> +       u32 min_speed_hz;
> +       atomic_t                        msg_done[8];
> +       wait_queue_head_t       msg_wait;
> +       struct v4l2_ctrl        *link_freq;
> +       struct v4l2_ctrl_handler ctrl_handler;
> +};
> +
> +static const s64 link_freq_menu_items[] = {
> +       1000000000
> +};
> +
> +static inline struct rk1608_state *to_state(struct v4l2_subdev *sd)
> +{
> +       return container_of(sd, struct rk1608_state, sd);
> +}
> +
> +/**
> + * rk1608_operation_query - RK1608 last operation state query
> + *
> + * @spi: device from which data will be read
> + * @state: last operation state [out]
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_operation_query(struct spi_device *spi, s32 *state)
> +{
> +       s32 query_cmd = RK1608_CMD_QUERY;
> +       struct spi_transfer query_cmd_packet = {
> +               .tx_buf = &query_cmd,
> +               .len    = sizeof(query_cmd),
> +       };
> +       struct spi_transfer state_packet = {
> +               .rx_buf = state,
> +               .len    = sizeof(*state),
> +       };
> +       struct spi_message  m;
> +
> +       spi_message_init(&m);
> +       spi_message_add_tail(&query_cmd_packet, &m);
> +       spi_message_add_tail(&state_packet, &m);
> +       spi_sync(spi, &m);
> +
> +       return ((*state & RK1608_STATE_ID_MASK) == RK1608_STATE_ID) ? 0 : -1;
> +}
> +
> +int rk1608_write(struct spi_device *spi,
> +                s32 addr, const s32 *data, size_t data_len)
> +{
> +       s32 write_cmd = RK1608_CMD_WRITE;
> +       struct spi_transfer write_cmd_packet = {
> +               .tx_buf = &write_cmd,
> +               .len    = sizeof(write_cmd),
> +       };
> +       struct spi_transfer addr_packet = {
> +               .tx_buf = &addr,
> +               .len    = sizeof(addr),
> +       };
> +       struct spi_transfer data_packet = {
> +               .tx_buf = data,
> +               .len    = data_len,
> +       };
> +       struct spi_message  m;
> +
> +       spi_message_init(&m);
> +       spi_message_add_tail(&write_cmd_packet, &m);
> +       spi_message_add_tail(&addr_packet, &m);
> +       spi_message_add_tail(&data_packet, &m);
> +       return spi_sync(spi, &m);
> +}
> +
> +/**
> + * rk1608_safe_write - RK1608 synchronous write with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_write(struct spi_device *spi,
> +                     s32 addr, const s32 *data, size_t data_len)
> +{
> +       int ret = 0;
> +       s32 state, retry = 0;
> +
> +       while (data_len > 0) {
> +               size_t slen = MIN(data_len, RK1608_MAX_OP_BYTES);
> +
> +               do {
> +                       rk1608_write(spi, addr, data, data_len);
> +                       if (rk1608_operation_query(spi, &state) != 0)
> +                               return -1;
> +                       if ((state & RK1608_STATE_MASK) == 0)
> +                               break;
> +
> +                       udelay(RK1608_OP_TRY_DELAY);
> +               } while (retry++ != RK1608_OP_TRY_MAX);
> +
> +               data_len = data_len - slen;
> +               data = (s32 *)((s8 *)data + slen);
> +               addr += slen;
> +       }
> +       return ret;
> +}
> +
> +void rk1608_hw_init(struct spi_device *spi)
> +{
> +       s32 write_data = SPI0_PLL_SEL_APLL;
> +
> +       /* modify rk1608 spi slave clk to 300M */
> +       rk1608_safe_write(spi, CRUPMU_CLKSEL14_CON, &write_data, 4);
> +
> +       /* modify rk1608 spi io driver strength to 8mA */
> +       write_data = BIT7_6_SEL_8MA;
> +       rk1608_safe_write(spi, PMUGRF_GPIO1A_E, &write_data, 4);
> +       write_data = BIT1_0_SEL_8MA;
> +       rk1608_safe_write(spi, PMUGRF_GPIO1B_E, &write_data, 4);
> +}
> +
> +/**
> + * rk1608_read - RK1608 synchronous read
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_read(struct spi_device *spi,
> +               s32 addr, s32 *data, size_t data_len)
> +{
> +       s32 real_len = MIN(data_len, RK1608_MAX_OP_BYTES);
> +       s32 read_cmd = RK1608_CMD_READ | (real_len << 14 &
> +                                          RK1608_STATE_ID_MASK);
> +       s32 read_begin_cmd = RK1608_CMD_READ_BEGIN;
> +       s32 dummy = 0;
> +       struct spi_transfer read_cmd_packet = {
> +               .tx_buf = &read_cmd,
> +               .len    = sizeof(read_cmd),
> +       };
> +       struct spi_transfer addr_packet = {
> +               .tx_buf = &addr,
> +               .len    = sizeof(addr),
> +       };
> +       struct spi_transfer read_dummy_packet = {
> +               .tx_buf = &dummy,
> +               .len    = sizeof(dummy),
> +       };
> +       struct spi_transfer read_begin_cmd_packet = {
> +               .tx_buf = &read_begin_cmd,
> +               .len    = sizeof(read_begin_cmd),
> +       };
> +       struct spi_transfer data_packet = {
> +               .rx_buf = data,
> +               .len    = data_len,
> +       };
> +       struct spi_message  m;
> +
> +       spi_message_init(&m);
> +       spi_message_add_tail(&read_cmd_packet, &m);
> +       spi_message_add_tail(&addr_packet, &m);
> +       spi_message_add_tail(&read_dummy_packet, &m);
> +       spi_message_add_tail(&read_begin_cmd_packet, &m);
> +       spi_message_add_tail(&data_packet, &m);
> +       return spi_sync(spi, &m);
> +}
> +
> +/**
> + * rk1608_safe_read - RK1608 synchronous read with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_read(struct spi_device *spi,
> +                    s32 addr, s32 *data, size_t data_len)
> +{
> +       s32 state = 0;
> +       s32 retry = 0;
> +
> +       do {
> +               rk1608_read(spi, addr, data, data_len);
> +               if (rk1608_operation_query(spi, &state) != 0)
> +                       return -1;
> +               if ((state & RK1608_STATE_MASK) == 0)
> +                       break;
> +               udelay(RK1608_OP_TRY_DELAY);
> +       } while (retry++ != RK1608_OP_TRY_MAX);
> +
> +       return (state & RK1608_STATE_MASK);
> +}
> +
> +static int rk1608_read_wait(struct spi_device *spi,
> +                           const struct rk1608_section *sec)
> +{
> +       s32 value = 0;
> +       int retry = 0;
> +       int ret = 0;
> +
> +       do {
> +               ret = rk1608_safe_read(spi, sec->wait_addr, &value, 4);
> +               if (!ret && value == sec->wait_value)
> +                       break;
> +
> +               if (retry++ == sec->timeout) {
> +                       ret = -1;
> +                       dev_err(&spi->dev, "read 0x%x is %x != %x timeout\n",
> +                               sec->wait_addr, value, sec->wait_value);
> +                       break;
> +               }
> +               mdelay(sec->wait_time);
> +       } while (1);
> +
> +       return ret;
> +}
> +
> +static int rk1608_boot_request(struct spi_device *spi,
> +                              const struct rk1608_section *sec)
> +{
> +       struct rk1608_boot_req boot_req;
> +       int retry = 0;
> +       int ret = 0;
> +
> +       /*send boot request to rk1608 for ddr init*/
> +       boot_req.flag = sec->flag;
> +       boot_req.load_addr = sec->load_addr;
> +       boot_req.boot_len = sec->size;
> +       boot_req.status = 1;
> +       boot_req.cmd = 2;
> +
> +       ret = rk1608_safe_write(spi, BOOT_REQUEST_ADDR,
> +                               (s32 *)&boot_req, sizeof(boot_req));
> +       if (ret)
> +               return ret;
> +
> +       if (sec->flag & BOOT_FLAG_READ_WAIT) {
> +       /*waitting for rk1608 init ddr done*/
> +               do {
> +                       ret = rk1608_safe_read(spi, BOOT_REQUEST_ADDR,
> +                                              (s32 *)&boot_req,
> +                                              sizeof(boot_req));
> +
> +                       if (!ret && boot_req.status == 0)
> +                               break;
> +
> +                       if (retry++ == sec->timeout) {
> +                               ret = -1;
> +                               dev_err(&spi->dev, "boot_request timeout\n");
> +                               break;
> +                       }
> +                       mdelay(sec->wait_time);
> +               } while (1);
> +       }
> +
> +       return ret;
> +}
> +
> +static int rk1608_download_section(struct spi_device *spi, const u8 *data,
> +                                  const struct rk1608_section *sec)
> +{
> +       int ret = 0;
> +
> +       dev_info(&spi->dev, "offset:%x,size:%x,addr:%x,wait_time:%x",
> +                sec->offset, sec->size, sec->load_addr, sec->wait_time);
> +       dev_info(&spi->dev, "timeout:%x,crc:%x,flag:%x,type:%x",
> +                sec->timeout, sec->crc_16, sec->flag, sec->type);
> +
> +       if (sec->size > 0) {
> +               ret = rk1608_safe_write(spi, sec->load_addr,
> +                                       (s32 *)(data + sec->offset),
> +                                       sec->size);
> +               if (ret) {
> +                       dev_err(&spi->dev, "rk1608_safe_write err =%d\n", ret);
> +                       return ret;
> +               }
> +       }
> +
> +       if (sec->flag & BOOT_FLAG_BOOT_REQUEST)
> +               ret = rk1608_boot_request(spi, sec);
> +       else if (sec->flag & BOOT_FLAG_READ_WAIT)
> +               ret = rk1608_read_wait(spi, sec);
> +
> +       return ret;
> +}
> +
> +/**
> + * rk1608_download_fw: - rk1608 firmware download through spi
> + *
> + * @spi: spi device
> + * @fw_name: name of firmware file, NULL for default firmware name
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + **/
> +int rk1608_download_fw(struct spi_device *spi, const char *fw_name)
> +{
> +       const struct rk1608_header *head;
> +       const struct firmware *fw;
> +       int i = 0;
> +       int ret = 0;
> +
> +       if (!fw_name)
> +               fw_name = RK1608_FW_NAME;
> +
> +       dev_info(&spi->dev, "before request firmware");
> +       ret = request_firmware(&fw, fw_name, &spi->dev);
> +       if (ret) {
> +               dev_err(&spi->dev, "request firmware %s failed!", fw_name);
> +               return ret;
> +       }
> +
> +       head = (const struct rk1608_header *)fw->data;
> +
> +       dev_info(&spi->dev, "request firmware %s (version:%s) success!",
> +                fw_name, head->version);
> +
> +       for (i = 0; i < head->section_count; i++) {
> +               ret = rk1608_download_section(spi, fw->data,
> +                                             &head->sections[i]);
> +               if (ret)
> +                       break;
> +       }
> +
> +       release_firmware(fw);
> +       return ret;
> +}
> +
> +int rk1608_lsb_w32(struct spi_device *spi, s32 addr, s32 data)
> +{
> +       s32 write_cmd = RK1608_CMD_WRITE;
> +       struct spi_transfer write_cmd_packet = {
> +               .tx_buf = &write_cmd,
> +               .len    = sizeof(write_cmd),
> +       };
> +       struct spi_transfer addr_packet = {
> +               .tx_buf = &addr,
> +               .len    = sizeof(addr),
> +       };
> +       struct spi_transfer data_packet = {
> +               .tx_buf = &data,
> +               .len    = sizeof(data),
> +       };
> +       struct spi_message  m;
> +
> +       write_cmd = MSB2LSB32(write_cmd);
> +       addr = MSB2LSB32(addr);
> +       data = MSB2LSB32(data);
> +       spi_message_init(&m);
> +       spi_message_add_tail(&write_cmd_packet, &m);
> +       spi_message_add_tail(&addr_packet, &m);
> +       spi_message_add_tail(&data_packet, &m);
> +       return spi_sync(spi, &m);
> +}
> +
> +void rk1608_cs_set_value(struct rk1608_state *pdata, int value)
> +{
> +       s8 null_cmd = 0;
> +       struct spi_transfer null_cmd_packet = {
> +               .tx_buf = &null_cmd,
> +               .len    = sizeof(null_cmd),
> +               .cs_change = !value,
> +       };
> +       struct spi_message  m;
> +
> +       spi_message_init(&m);
> +       spi_message_add_tail(&null_cmd_packet, &m);
> +       spi_sync(pdata->spi, &m);
> +}
> +
> +void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz)
> +{
> +       pdata->spi->max_speed_hz = hz;
> +}
> +
> +static int rk1608_sensor_power(struct v4l2_subdev *sd, int on)
> +{
> +       int ret = 0;
> +       struct rk1608_state *pdata = to_state(sd);
> +       struct spi_device *spi = pdata->spi;
> +
> +       mutex_lock(&pdata->lock);
> +       /*start Sensor power on/off*/
> +       if (pdata->sensor_sd)
> +               pdata->sensor_sd->ops->core->s_power(pdata->sensor_sd, on);
> +       if (on && !pdata->power_count)  {
> +               clk_prepare_enable(pdata->mclk);
> +               clk_set_rate(pdata->mclk, RK1608_MCLK_RATE);
> +               /*request rk1608 enter slave mode*/
> +               rk1608_cs_set_value(pdata, 0);
> +               if (pdata->powerdown_gpio > 0) {
> +                       gpio_set_value(pdata->powerdown_gpio,
> +                                      pdata->powerdown_active);
> +               }
> +               if (pdata->wakeup_gpio > 0) {
> +                       gpio_set_value(pdata->wakeup_gpio,
> +                                      pdata->wakeup_active);
> +               }
> +               mdelay(3);
> +               if (pdata->reset_gpio > 0)
> +                       gpio_set_value(pdata->reset_gpio, pdata->reset_active);
> +               mdelay(5);
> +               rk1608_cs_set_value(pdata, 1);
> +               rk1608_set_spi_speed(pdata, pdata->min_speed_hz);
> +               rk1608_lsb_w32(spi, SPI_ENR, 0);
> +               rk1608_lsb_w32(spi, SPI_CTRL0,
> +                              OPM_SLAVE_MODE | RSD_SEL_2CYC | DFS_SEL_16BIT);
> +               rk1608_hw_init(pdata->spi);
> +               rk1608_set_spi_speed(pdata, pdata->max_speed_hz);
> +               /*download system firmware*/
> +               ret = rk1608_download_fw(pdata->spi, NULL);
> +               if (ret)
> +                       dev_err(pdata->dev, "Download firmware failed!");
> +               else
> +                       dev_info(pdata->dev, "Download firmware success!");
> +               enable_irq(pdata->irq);
> +               if (pdata->sleepst_irq > 0)
> +                       enable_irq(pdata->sleepst_irq);
> +
> +       } else if (!on && pdata->power_count == 1) {
> +               disable_irq(pdata->irq);
> +               if (pdata->sleepst_irq > 0)
> +                       disable_irq(pdata->sleepst_irq);
> +               if (pdata->powerdown_gpio > 0)
> +                       gpio_set_value(pdata->powerdown_gpio,
> +                                      !pdata->powerdown_active);
> +
> +               if (pdata->wakeup_gpio > 0)
> +                       gpio_set_value(pdata->wakeup_gpio,
> +                                      !pdata->wakeup_active);
> +
> +               if (pdata->reset_gpio > 0)
> +                       gpio_set_value(pdata->reset_gpio, !pdata->reset_active);
> +
> +               rk1608_cs_set_value(pdata, 0);
> +               clk_disable_unprepare(pdata->mclk);
> +       }
> +       /* Update the power count. */
> +       pdata->power_count += on ? 1 : -1;
> +       WARN_ON(pdata->power_count < 0);
> +       mutex_unlock(&pdata->lock);
> +
> +       return ret;
> +}
> +
> +static int rk1608_stream_on(struct rk1608_state *pdata)
> +{
> +       int  cnt = 0;
> +
> +       /*Waiting for the sensor to be ready*/
> +       while (pdata->sensor_cnt < pdata->sensor_nums) {
> +               /* TIMEOUT 10s break*/
> +               if (cnt++ > SENSOR_TIMEOUT) {
> +                       dev_err(pdata->dev, "Sensor%d is ready to timeout!",
> +                               pdata->sensor_cnt);
> +                       break;
> +               }
> +       mdelay(10);
> +       }
> +
> +       if (pdata->sensor_nums) {
> +               if (pdata->sensor_cnt == pdata->sensor_nums)
> +                       dev_info(pdata->dev, "Sensor(num %d) is ready!",
> +                                pdata->sensor_cnt);
> +       } else {
> +               dev_warn(pdata->dev, "No sensor is found!");
> +       }
> +
> +       return 0;
> +}
> +
> +static int rk1608_stream_off(struct rk1608_state *pdata)
> +{
> +       mutex_lock(&pdata->sensor_lock);
> +       pdata->sensor_cnt = 0;
> +       mutex_unlock(&pdata->sensor_lock);
> +       return 0;
> +}
> +
> +static int rk1608_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +       struct rk1608_state *pdata = to_state(sd);
> +
> +       if (enable)
> +               return rk1608_stream_on(pdata);
> +       else
> +               return rk1608_stream_off(pdata);
> +}
> +
> +static int rk1608_enum_mbus_code(struct v4l2_subdev *sd,
> +                                struct v4l2_subdev_pad_config *cfg,
> +                                struct v4l2_subdev_mbus_code_enum *code)
> +{
> +       if (code->index > 0)
> +               return -EINVAL;
> +
> +       code->code = MEDIA_BUS_FMT_SGRBG8_1X8;
> +
> +       return 0;
> +}
> +
> +static int rk1608_get_fmt(struct v4l2_subdev *sd,
> +                         struct v4l2_subdev_pad_config *cfg,
> +                         struct v4l2_subdev_format *fmt)
> +{
> +       struct v4l2_mbus_framefmt *mf = &fmt->format;
> +
> +       mf->code = MEDIA_BUS_FMT_SGRBG8_1X8;
> +       mf->width = RK1608_WINDOW_WIDTH_DEF;
> +       mf->height = RK1608_WINDOW_HEIGHT_DEF;
> +       mf->field = V4L2_FIELD_NONE;
> +       mf->colorspace = V4L2_COLORSPACE_SRGB;
> +
> +       return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops rk1608_subdev_internal_ops = {
> +       .open   = NULL,
> +};
> +
> +static const struct v4l2_subdev_video_ops rk1608_subdev_video_ops = {
> +       .s_stream       = rk1608_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops rk1608_subdev_pad_ops = {
> +       .enum_mbus_code = rk1608_enum_mbus_code,
> +       .get_fmt        = rk1608_get_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops rk1608_core_ops = {
> +       .s_power        = rk1608_sensor_power,
> +};
> +
> +static const struct v4l2_subdev_ops rk1608_subdev_ops = {
> +       .core   = &rk1608_core_ops,
> +       .video  = &rk1608_subdev_video_ops,
> +       .pad    = &rk1608_subdev_pad_ops,
> +};
> +
> +/**
> + * rk1608_msq_read_head - read rk1608 msg queue head
> + *
> + * @spi: spi device
> + * @addr: msg queue head addr
> + * @m: msg queue pointer
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_read_head(struct spi_device *spi,
> +                        u32 addr, struct rk1608_msg_queue *q)
> +{
> +       int err = 0;
> +       s32 reg;
> +
> +       err = rk1608_safe_read(spi, RK1608_PMU_SYS_REG0, &reg, 4);
> +
> +       if (err || ((reg & RK1608_MSG_QUEUE_OK_MASK) !=
> +                        RK1608_MSG_QUEUE_OK_TAG))
> +               return -1;
> +
> +       err = rk1608_safe_read(spi, addr, (s32 *)q, sizeof(*q));
> +
> +       return err;
> +}
> +
> +/**
> + * rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue
> + *
> + * @q: msg queue
> + * @m: a msg pointer buf [out]
> + *
> + * need call rk1608_msq_recv_msg_free to free msg after msg use done
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m)
> +{
> +       struct rk1608_msg_queue queue;
> +       struct rk1608_msg_queue *q = &queue;
> +       u32 size = 0, msg_size = 0;
> +       u32 recv_addr = 0;
> +       u32 next_recv_addr = 0;
> +       int err = 0;
> +
> +       *m = NULL;
> +       err = rk1608_msq_read_head(spi, RK1608_S_MSG_QUEUE_ADDR, q);
> +       if (err)
> +               return err;
> +
> +       if (q->cur_send == q->cur_recv)
> +               return -1;
> +       /*skip to head when size is 0*/
> +       err = rk1608_safe_read(spi, (s32)q->cur_recv, (s32 *)&size, 4);
> +       if (err)
> +               return err;
> +       if (size == 0) {
> +               err = rk1608_safe_read(spi, (s32)q->buf_head, (s32 *)&size, 4);
> +               if (err)
> +                       return err;
> +
> +               msg_size = size * sizeof(u32);
> +               recv_addr = q->buf_head;
> +               next_recv_addr = q->buf_head + msg_size;
> +       } else {
> +               msg_size = size * sizeof(u32);
> +               recv_addr = q->cur_recv;
> +               next_recv_addr = q->cur_recv + msg_size;
> +               if (next_recv_addr == q->buf_tail)
> +                       next_recv_addr = q->buf_head;
> +       }
> +
> +       if (msg_size > (q->buf_tail - q->buf_head))
> +               return -2;
> +
> +       *m = kmalloc(msg_size, GFP_KERNEL);
> +       err = rk1608_safe_read(spi, recv_addr, (s32 *)*m, msg_size);
> +       if (err == 0) {
> +               err = rk1608_safe_write(spi, RK1608_S_MSG_QUEUE_ADDR +
> +                                      (u8 *)&q->cur_recv - (u8 *)q,
> +                                      &next_recv_addr, 4);
> +       }
> +       if (err)
> +               kfree(*m);
> +
> +       return err;
> +}
> +
> +static void print_rk1608_log(struct rk1608_state *pdata,
> +                            struct msg_rk1608_log_t *log)
> +{
> +       char *str = (char *)(log);
> +
> +       str[log->size * sizeof(s32) - 1] = 0;
> +       str += sizeof(struct msg_rk1608_log_t);
> +       dev_info(pdata->dev, "RK1608%d: %s", log->core_id, str);
> +}
> +
> +void int32_hexdump(const char *prefix, int32_t *data, int len)
> +{
> +       pr_err("%s\n", prefix);
> +       print_hex_dump(KERN_ERR, "offset ", DUMP_PREFIX_OFFSET,
> +                      16, 4, data, len, false);
> +       pr_err("\n");
> +}
> +
> +static void dispatch_received_msg(struct rk1608_state *pdata,
> +                                 struct msg *msg)
> +{
> +       #if DEBUG_DUMP_ALL_SEND_RECV_MSG == 1
> +       int32_hexdump("recv msg:", (s32 *)msg, msg->size * 4);
> +       #endif
> +
> +       if (msg->type == id_msg_set_stream_out_on_ret_t) {
> +               mutex_lock(&pdata->sensor_lock);
> +               pdata->sensor_cnt++;
> +               mutex_unlock(&pdata->sensor_lock);
> +       }
> +
> +       if (msg->type == id_msg_rk1608_log_t)
> +               print_rk1608_log(pdata, (struct msg_rk1608_log_t *)msg);
> +}
> +
> +static irqreturn_t rk1608_threaded_isr(int irq, void *dev_id)
> +{
> +       struct rk1608_state *pdata = dev_id;
> +       struct msg *msg;
> +
> +       WARN_ON(irq != pdata->irq);
> +       while (!rk1608_msq_recv_msg(pdata->spi, &msg) && NULL != msg) {
> +               dispatch_received_msg(pdata, msg);
> +               /* for kernel msg sync */
> +               if (pdata->msg_num != 0 && msg->sync) {
> +                       dev_info(pdata->dev, "rk1608 kernel sync\n");
> +                       mutex_lock(&pdata->send_msg_lock);
> +                       pdata->msg_num--;
> +                       atomic_set(&pdata->msg_done[pdata->msg_num], 1);
> +                       mutex_unlock(&pdata->send_msg_lock);
> +                       wake_up(&pdata->msg_wait);
> +               }
> +               kfree(msg);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rk1608_sleep_isr(int irq, void *dev_id)
> +{
> +       struct rk1608_state *pdata = dev_id;
> +
> +       WARN_ON(irq != pdata->sleepst_irq);
> +       if (pdata->powerdown_gpio > 0)
> +               gpio_set_value(pdata->powerdown_gpio, !pdata->powerdown_active);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int rk1608_parse_dt_property(struct rk1608_state *pdata)
> +{
> +       int ret = 0;
> +       int i;
> +       struct device *dev = pdata->dev;
> +       struct device_node *node = dev->of_node;
> +       enum of_gpio_flags flags;
> +
> +       if (!node)
> +               return 1;
> +
> +       ret = of_property_read_u32(node, "spi-max-frequency",
> +                                  &pdata->max_speed_hz);
> +       if (ret <= 0) {
> +               dev_warn(dev, "can not get spi-max-frequency!");
> +               pdata->max_speed_hz = RK1608_MCLK_RATE;
> +       }
> +
> +       ret = of_property_read_u32(node, "spi-min-frequency",
> +                                  &pdata->min_speed_hz);
> +       if (ret <= 0) {
> +               dev_warn(dev, "can not get spi-min-frequency!");
> +               pdata->min_speed_hz = pdata->max_speed_hz / 2;
> +       }
> +
> +       pdata->mclk = devm_clk_get(dev, "mclk");
> +       if (IS_ERR(pdata->mclk)) {
> +               dev_err(dev, "can not get mclk, error %ld\n",
> +                       PTR_ERR(pdata->mclk));
> +               pdata->mclk = NULL;
> +               return -1;
> +       }
> +
> +       ret = of_get_named_gpio_flags(node, "reset-gpio", 0, &flags);
> +       if (ret <= 0) {
> +               dev_warn(dev, "can not find reset-gpio, error %d\n", ret);
> +               return ret;
> +       }
> +       pdata->reset_gpio = ret;
> +       pdata->reset_active = 1;
> +       if (flags == OF_GPIO_ACTIVE_LOW)
> +               pdata->reset_active = 0;
> +
> +       if (pdata->reset_gpio > 0) {
> +               ret = devm_gpio_request(dev, pdata->reset_gpio, "rk1608-reset");
> +               if (ret) {
> +                       dev_err(dev, "gpio %d request error %d\n",
> +                               pdata->reset_gpio, ret);
> +                       return ret;
> +               }
> +
> +               ret = gpio_direction_output(pdata->reset_gpio,
> +                                           !pdata->reset_active);
> +               if (ret) {
> +                       dev_err(dev, "gpio %d direction output error %d\n",
> +                               pdata->reset_gpio, ret);
> +                       return ret;
> +               }
> +       }
> +
> +       ret = of_get_named_gpio_flags(node, "irq-gpio", 0, NULL);
> +       if (ret <= 0) {
> +               dev_warn(dev, "can not find irq-gpio, error %d\n", ret);
> +               return ret;
> +       }
> +
> +       pdata->irq_gpio = ret;
> +
> +       ret = devm_gpio_request(dev, pdata->irq_gpio, "rk1608-irq");
> +       if (ret) {
> +               dev_err(dev, "gpio %d request error %d\n", pdata->irq_gpio,
> +                       ret);
> +               return ret;
> +       }
> +
> +       ret = gpio_direction_input(pdata->irq_gpio);
> +       if (ret) {
> +               dev_err(dev, "gpio %d direction input error %d\n",
> +                       pdata->irq_gpio, ret);
> +               return ret;
> +       }
> +
> +       ret = gpio_to_irq(pdata->irq_gpio);
> +       if (ret < 0) {
> +               dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
> +                       pdata->irq_gpio, ret);
> +               return ret;
> +       }
> +       pdata->irq = ret;
> +       ret = request_threaded_irq(pdata->irq, NULL, rk1608_threaded_isr,
> +                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +                                  "rk1608-irq", pdata);
> +       if (ret) {
> +               dev_err(dev, "cannot request thread irq: %d\n", ret);
> +               return ret;
> +       }
> +
> +       disable_irq(pdata->irq);
> +
> +       ret = of_get_named_gpio_flags(node, "powerdown-gpio", 0, &flags);
> +       if (ret <= 0)
> +               dev_warn(dev, "can not find  powerdown-gpio, error %d\n", ret);
> +
> +       pdata->powerdown_gpio = ret;
> +       pdata->powerdown_active = 1;
> +       if (flags == OF_GPIO_ACTIVE_LOW)
> +               pdata->powerdown_active = 0;
> +
> +       if (pdata->powerdown_gpio > 0) {
> +               ret = devm_gpio_request(dev, pdata->powerdown_gpio,
> +                                       "rk1608-powerdown");
> +               if (ret) {
> +                       dev_err(dev, "gpio %d request error %d\n",
> +                               pdata->powerdown_gpio, ret);
> +                       return ret;
> +               }
> +
> +               ret = gpio_direction_output(pdata->powerdown_gpio,
> +                                           !pdata->powerdown_active);
> +               if (ret) {
> +                       dev_err(dev, "gpio %d direction output error %d\n",
> +                               pdata->powerdown_gpio, ret);
> +                       return ret;
> +               }
> +       }
> +
> +       pdata->sleepst_gpio = -1;
> +       pdata->sleepst_irq = -1;
> +       pdata->wakeup_gpio = -1;
> +
> +       ret = of_get_named_gpio_flags(node, "sleepst-gpio", 0, NULL);
> +       if (ret <= 0) {
> +               dev_warn(dev, "can not find property sleepst-gpio, error %d\n",
> +                        ret);
> +               return ret;
> +       }
> +
> +       pdata->sleepst_gpio = ret;
> +
> +       ret = devm_gpio_request(dev, pdata->sleepst_gpio, "rk1608-sleep-irq");
> +       if (ret) {
> +               dev_err(dev, "gpio %d request error %d\n",
> +                       pdata->sleepst_gpio, ret);
> +               return ret;
> +       }
> +
> +       ret = gpio_direction_input(pdata->sleepst_gpio);
> +       if (ret) {
> +               dev_err(dev, "gpio %d direction input error %d\n",
> +                       pdata->sleepst_gpio, ret);
> +               return ret;
> +       }
> +
> +       ret = gpio_to_irq(pdata->sleepst_gpio);
> +       if (ret < 0) {
> +               dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
> +                       pdata->sleepst_gpio, ret);
> +               return ret;
> +       }
> +       pdata->sleepst_irq = ret;
> +       ret = request_any_context_irq(pdata->sleepst_irq,
> +                                     rk1608_sleep_isr,
> +                                     IRQF_TRIGGER_RISING,
> +                                     "rk1608-sleepst", pdata);
> +       disable_irq(pdata->sleepst_irq);
> +
> +       ret = of_get_named_gpio_flags(node, "wakeup-gpio", 0, &flags);
> +       if (ret <= 0)
> +               dev_warn(dev, "can not find wakeup-gpio error %d\n", ret);
> +
> +       pdata->wakeup_gpio = ret;
> +       pdata->wakeup_active = 1;
> +       if (flags == OF_GPIO_ACTIVE_LOW)
> +               pdata->wakeup_active = 0;
> +
> +       if (pdata->wakeup_gpio > 0) {
> +               ret = devm_gpio_request(dev, pdata->wakeup_gpio,
> +                                       "rk1608-wakeup");
> +               if (ret) {
> +                       dev_err(dev, "gpio %d request error %d\n",
> +                               pdata->wakeup_gpio, ret);
> +                       return ret;
> +               }
> +
> +               ret = gpio_direction_output(pdata->wakeup_gpio,
> +                                           !pdata->wakeup_active);
> +               if (ret) {
> +                       dev_err(dev, "gpio %d direction output error %d\n",
> +                               pdata->wakeup_gpio, ret);
> +                       return ret;
> +               }
> +       }
> +       pdata->msg_num = 0;
> +       init_waitqueue_head(&pdata->msg_wait);
> +       for (i = 0; i < 8; i++)
> +               atomic_set(&pdata->msg_done[i], 0);
> +
> +       return ret;
> +}
> +
> +static int get_remote_node_dev(struct rk1608_state *pdev)
> +{
> +       struct platform_device *sensor_pdev = NULL;
> +       struct device *dev = pdev->dev;
> +       struct device_node *parent = dev->of_node;
> +       struct device_node *node, *pre_node = NULL;
> +       struct device_node  *remote = NULL;
> +       int ret, sensor_nums = 0;
> +
> +       node = of_graph_get_next_endpoint(parent, pre_node);
> +       if (node) {
> +               of_node_put(pre_node);
> +               pre_node = node;
> +       } else {
> +               dev_err(dev, "fieled to get endpoint\n");
> +               return -EINVAL;
> +       }
> +       while ((node = of_graph_get_next_endpoint(parent, pre_node)) != NULL) {
> +               of_node_put(pre_node);
> +               pre_node = node;
> +               remote = of_graph_get_remote_port_parent(node);
> +               if (!remote) {
> +                       dev_err(dev, "%s: no valid device\n", __func__);
> +                       of_node_put(remote);
> +                       ret = -EINVAL;
> +               }
> +
> +               sensor_pdev = of_find_device_by_node(remote);
> +               of_node_put(remote);
> +
> +               if (!sensor_pdev) {
> +                       dev_err(dev, "fieled to get Sensor device\n");
> +                       ret = -EINVAL;
> +               } else {
> +                       pdev->sensor_sd = platform_get_drvdata(sensor_pdev);
> +                       if (pdev->sensor_sd)
> +                               sensor_nums++;
> +                       else
> +                               dev_err(dev, "fieled to get Sensor drvdata\n");
> +                       ret = 0;
> +               }
> +       }
> +       pdev->sensor_nums = sensor_nums;
> +       if (pdev->sensor_nums)
> +               dev_info(dev, "get Sensor (nums=%d) dev is OK!\n",
> +                        pdev->sensor_nums);
> +
> +       return ret;
> +}
> +
> +static int rk1608_probe(struct spi_device *spi)
> +{
> +       struct rk1608_state             *rk1608;
> +       struct v4l2_subdev              *sd;
> +       struct v4l2_ctrl_handler        *handler;
> +       int ret;
> +
> +       rk1608 = devm_kzalloc(&spi->dev, sizeof(*rk1608), GFP_KERNEL);
> +       if (!rk1608)
> +               return -ENOMEM;
> +       rk1608->dev = &spi->dev;
> +       rk1608->spi = spi;
> +       spi_set_drvdata(spi, rk1608);
> +       ret = rk1608_parse_dt_property(rk1608);
> +       if (ret) {
> +               dev_err(rk1608->dev, "rk1608 parse dt property err(%x)\n", ret);
> +               goto parse_err;
> +       }
> +       ret = get_remote_node_dev(rk1608);
> +       if (ret)
> +               dev_warn(rk1608->dev, "get remote node dev err(%x)\n", ret);
> +       rk1608->sensor_cnt = 0;
> +       mutex_init(&rk1608->sensor_lock);
> +       mutex_init(&rk1608->send_msg_lock);
> +       mutex_init(&rk1608->lock);
> +       sd = &rk1608->sd;
> +       v4l2_spi_subdev_init(sd, spi, &rk1608_subdev_ops);
> +
> +       handler = &rk1608->ctrl_handler;
> +       ret = v4l2_ctrl_handler_init(handler, 1);
> +       if (ret)
> +               goto handler_init_err;
> +
> +       rk1608->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
> +                                                  V4L2_CID_LINK_FREQ,
> +                                                  0, 0, link_freq_menu_items);
> +       if (rk1608->link_freq)
> +               rk1608->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +       if (handler->error)
> +               goto handler_err;
> +
> +       sd->ctrl_handler = handler;
> +       sd->internal_ops = &rk1608_subdev_internal_ops;
> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +       rk1608->pad.flags = MEDIA_PAD_FL_SOURCE;
> +       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
> +
> +       ret = media_entity_init(&sd->entity, 1, &rk1608->pad, 0);
> +       if (ret < 0)
> +               goto handler_err;
> +
> +       ret = v4l2_async_register_subdev(sd);
> +       if (ret < 0)
> +               goto register_err;
> +       dev_info(rk1608->dev, "DSP rk1608 Driver probe is OK!\n");
> +
> +       return 0;
> +register_err:
> +       media_entity_cleanup(&sd->entity);
> +handler_err:
> +       v4l2_ctrl_handler_free(handler);
> +handler_init_err:
> +       v4l2_device_unregister_subdev(&rk1608->sd);
> +       mutex_destroy(&rk1608->lock);
> +       mutex_destroy(&rk1608->send_msg_lock);
> +       mutex_destroy(&rk1608->sensor_lock);
> +parse_err:
> +       kfree(rk1608);
> +       return ret;
> +}
> +
> +static int rk1608_remove(struct spi_device *spi)
> +{
> +       struct rk1608_state *rk1608 = spi_get_drvdata(spi);
> +
> +       v4l2_async_unregister_subdev(&rk1608->sd);
> +       media_entity_cleanup(&rk1608->sd.entity);
> +       v4l2_ctrl_handler_free(&rk1608->ctrl_handler);
> +       v4l2_device_unregister_subdev(&rk1608->sd);
> +       mutex_destroy(&rk1608->lock);
> +       mutex_destroy(&rk1608->send_msg_lock);
> +       mutex_destroy(&rk1608->sensor_lock);
> +       kfree(rk1608);
> +
> +       return 0;
> +}
> +
> +static const struct spi_device_id rk1608_id[] = {
> +       { "RK1608", 0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(spi, rk1608_id);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id rk1608_of_match[] = {
> +       { .compatible = "rockchip,rk1608" },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rk1608_of_match);
> +#endif
> +
> +static struct spi_driver rk1608_driver = {
> +       .driver = {
> +               .of_match_table = of_match_ptr(rk1608_of_match),
> +               .name   = "RK1608",
> +       },
> +       .probe          = rk1608_probe,
> +       .remove         = rk1608_remove,
> +       .id_table       = rk1608_id,
> +};
> +
> +module_spi_driver(rk1608_driver);
> +
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("A DSP driver for rk1608 chip");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/spi/rk1608.h b/drivers/media/spi/rk1608.h
> new file mode 100644
> index 0000000..bf0c5ec
> --- /dev/null
> +++ b/drivers/media/spi/rk1608.h
> @@ -0,0 +1,366 @@
> +/**
> + * Rockchip rk1608 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses.  You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + *     Redistribution and use in source and binary forms, with or
> + *     without modification, are permitted provided that the following
> + *     conditions are met:
> + *
> + *      - Redistributions of source code must retain the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer.
> + *
> + *      - Redistributions in binary form must reproduce the above
> + *        copyright notice, this list of conditions and the following
> + *        disclaimer in the documentation and/or other materials
> + *        provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
> +#ifndef __RK1608_H__
> +#define __RK1608_H__
> +
> +#include <linux/types.h>
> +#include <linux/string.h>
> +#include <linux/spi/spi.h>
> +#include "linux/i2c.h"
> +
> +#define RK1608_OP_TRY_MAX                      3
> +#define RK1608_OP_TRY_DELAY                    10
> +#define RK1608_CMD_WRITE                       0x00000011
> +#define RK1608_CMD_WRITE_REG0          0X00010011
> +#define RK1608_CMD_WRITE_REG1          0X00020011
> +#define RK1608_CMD_READ                                0x00000077
> +#define RK1608_CMD_READ_BEGIN          0x000000aa
> +#define RK1608_CMD_QUERY                       0x000000ff
> +#define RK1608_CMD_QUERY_REG2          0x000001ff
> +#define RK1608_STATE_ID_MASK           (0xffff0000)
> +#define RK1608_STATE_ID                                (0X16080000)
> +#define RK1608_STATE_MASK                      (0x0000ffff)
> +
> +#define BOOT_REQUEST_ADDR                      0x18000010
> +#define RK1608_HEAD_ADDR                       0x60000000
> +#define RK1608_FW_NAME                         "rk1608.rkl"
> +#define RK1608_S_MSG_QUEUE_ADDR                0x60050010
> +#define RK1608_PMU_SYS_REG0                    0x120000f0
> +#define RK1608_MSG_QUEUE_OK_MASK       0xffff0001
> +#define RK1608_MSG_QUEUE_OK_TAG                0x16080001
> +#define RK1608_MAX_OP_BYTES                    60000
> +
> +#define RK1608_WINDOW_HEIGHT_DEF       480
> +#define RK1608_WINDOW_WIDTH_DEF                640
> +
> +#define BOOT_FLAG_CRC                          (0x01 << 0)
> +#define BOOT_FLAG_EXE                          (0x01 << 1)
> +#define BOOT_FLAG_LOAD_PMEM                    (0x01 << 2)
> +#define BOOT_FLAG_ACK                          (0x01 << 3)
> +#define BOOT_FLAG_READ_WAIT                    (0x01 << 4)
> +#define BOOT_FLAG_BOOT_REQUEST         (0x01 << 5)
> +
> +#define DEBUG_DUMP_ALL_SEND_RECV_MSG   0
> +#define RK1608_MCLK_RATE                       (24 * 1000 * 1000ul)
> +#define SENSOR_TIMEOUT                         1000
> +#define GRF_BASE_ADDR                          0xff770000
> +#define GRF_GPIO2B_IOMUX                       0x0014
> +#define GRF_IO_VSEL                                    0x0380
> +#define        OPM_SLAVE_MODE                          0X100000
> +#define        RSD_SEL_2CYC                            0X008000
> +#define        DFS_SEL_16BIT                           0X000002
> +#define SPI_CTRL0                                      0x11060000
> +#define SPI_ENR                                                0x11060008
> +#define CRUPMU_CLKSEL14_CON                    0x12008098
> +#define PMUGRF_GPIO1A_E                                0x12030040
> +#define PMUGRF_GPIO1B_E                                0x12030044
> +#define BIT7_6_SEL_8MA                         0xf000a000
> +#define BIT1_0_SEL_8MA                         0x000f000a
> +#define SPI0_PLL_SEL_APLL                      0xff004000
> +#define INVALID_ID                                     -1
> +#define RK1608_MAX_SEC_NUM                     10
> +
> +#ifndef MIN
> +#define MIN(a, b) ((a) < (b) ? (a) : (b))
> +#endif
> +
> +#ifndef MSB2LSB32
> +#define MSB2LSB32(x)   ((((u32)x & 0x80808080) >> 7) | \
> +                       (((u32)x & 0x40404040) >> 5) | \
> +                       (((u32)x & 0x20202020) >> 3) | \
> +                       (((u32)x & 0x10101010) >> 1) | \
> +                       (((u32)x & 0x08080808) << 1) | \
> +                       (((u32)x & 0x04040404) << 3) | \
> +                       (((u32)x & 0x02020202) << 5) | \
> +                       (((u32)x & 0x01010101) << 7))
> +#endif
> +
> +struct rk1608_section {
> +       union {
> +               u32     offset;
> +               u32     wait_value;
> +       };
> +       u32 size;
> +       union {
> +               u32     load_addr;
> +               u32     wait_addr;
> +       };
> +       u16     wait_time;
> +       u16     timeout;
> +       u16     crc_16;
> +       u8      flag;
> +       u8      type;
> +};
> +
> +struct rk1608_header {
> +       char version[32];
> +       u32 header_size;
> +       u32 section_count;
> +       struct rk1608_section sections[RK1608_MAX_SEC_NUM];
> +};
> +
> +struct rk1608_boot_req {
> +       u32 flag;
> +       u32 load_addr;
> +       u32 boot_len;
> +       u8 status;
> +       u8 dummy[2];
> +       u8 cmd;
> +};
> +
> +struct rk1608_msg_queue {
> +       u32 buf_head; /* msg buffer head */
> +       u32 buf_tail; /* msg buffer tail */
> +       u32 cur_send; /* current msg send postition */
> +       u32 cur_recv; /* current msg receive position */
> +};
> +
> +struct msg {
> +       u32 size; /* unit 4 bytes */
> +       u16 type; /* msg identification */
> +       s8  camera_id;
> +       s8  sync;
> +};
> +
> +enum {
> +       /** AP -> RK1608
> +        *   1 msg of sensor
> +        */
> +       id_msg_init_sensor_t =                  0x0001,
> +       id_msg_set_input_size_t,
> +       id_msg_set_output_size_t,
> +       id_msg_set_stream_in_on_t,
> +       id_msg_set_stream_in_off_t,
> +       id_msg_set_stream_out_on_t,
> +       id_msg_set_stream_out_off_t,
> +
> +       /** AP -> RK1608
> +        *   2 msg of take picture
> +        */
> +       id_msg_take_picture_t =                 0x0021,
> +       id_msg_take_picture_done_t,
> +
> +       /** AP -> RK1608
> +        *   3 msg of realtime parameter
> +        */
> +       id_msg_rt_args_t =                              0x0031,
> +
> +       /** AP -> RK1608
> +        *   4 msg of power manager
> +        */
> +       id_msg_set_sys_mode_bypass_t =  0x0200,
> +       id_msg_set_sys_mode_standby_t,
> +       id_msg_set_sys_mode_idle_enable_t,
> +       id_msg_set_sys_mode_idle_disable_t,
> +       id_msg_set_sys_mode_slave_rk1608_on_t,
> +       id_msg_set_sys_mode_slave_rk1608_off_t,
> +
> +       /** AP -> RK1608
> +        *   5 msg of debug config
> +        */
> +       id_msg_set_log_level_t =                0x0250,
> +
> +       /** RK1608 -> AP
> +        *   6 response of sensor msg
> +        */
> +       id_msg_init_sensor_ret_t =              0x0301,
> +       id_msg_set_input_size_ret_t,
> +       id_msg_set_output_size_ret_t,
> +       id_msg_set_stream_in_on_ret_t,
> +       id_msg_set_stream_in_off_ret_t,
> +       id_msg_set_stream_out_on_ret_t,
> +       id_msg_set_stream_out_off_ret_t,
> +
> +       /** RK1608 -> AP
> +        *   7 response of take picture msg
> +        */
> +       id_msg_take_picture_ret_t =             0x0320,
> +       id_msg_take_picture_done_ret_t,
> +
> +       /** RK1608 -> AP
> +        *   8 response of realtime parameter msg
> +        */
> +       id_msg_rt_args_ret_t =                  0x0330,
> +
> +       /*rk1608 -> ap*/
> +       id_msg_do_i2c_t =                               0x0390,
> +       /*ap -> rk1608*/
> +       id_msg_do_i2c_ret_t,
> +
> +       /** RK1608 -> AP
> +        *   9 msg of print log
> +        */
> +       id_msg_rk1608_log_t =                   0x0400,
> +
> +       /* dsi2csi dump */
> +       id_msg_dsi2sci_rgb_dump_t =             0x6000,
> +       id_msg_dsi2sci_nv12_dump_t =    0x6001,
> +
> +       /** RK1608 -> AP
> +        *      10  msg of xfile
> +        */
> +       id_msg_xfile_import_t =         0x8000 + 0x0600,
> +       id_msg_xfile_export_t,
> +       id_msg_xfile_mkdir_t
> +};
> +
> +struct msg_rk1608_log_t {
> +       u32     size;
> +       u16     type;
> +       s8      core_id;
> +       s8      log_level;
> +};
> +
> +/**
> + * rk1608_write - RK1608 synchronous write
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_write(struct spi_device *spi, s32 addr,
> +                const s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_safe_write - RK1608 synchronous write with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_write(struct spi_device *spi,
> +                     s32 addr, const s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_read - RK1608 synchronous read
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_read(struct spi_device *spi, s32 addr,
> +               s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_safe_read - RK1608 synchronous read with state check
> + *
> + * @spi: spi device
> + * @addr: resource address
> + * @data: data buffer [out]
> + * @data_len: data buffer size, in bytes
> + * Context: can sleep
> + *
> + * It returns zero on success, else operation state code.
> + */
> +int rk1608_safe_read(struct spi_device *spi,
> +                    s32 addr, s32 *data, size_t data_len);
> +
> +/**
> + * rk1608_operation_query - RK1608 last operation state query
> + *
> + * @spi: spi device
> + * @state: last operation state [out]
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_operation_query(struct spi_device *spi, s32 *state);
> +
> +/**
> + * rk1608_interrupt_request - RK1608 request a rk1608 interrupt
> + *
> + * @spi: spi device
> + * @interrupt_num: interrupt identification
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_interrupt_request(struct spi_device *spi,
> +                            s32 interrupt_num);
> +
> +static int rk1608_read_wait(struct spi_device *spi,
> +                           const struct rk1608_section *sec);
> +
> +static int rk1608_boot_request(struct spi_device *spi,
> +                              const struct rk1608_section *sec);
> +
> +static int rk1608_download_section(struct spi_device *spi, const u8 *data,
> +                                  const struct rk1608_section *sec);
> +/**
> + * rk1608_download_fw: - rk1608 firmware download through spi
> + *
> + * @spi: spi device
> + * @fw_name: name of firmware file, NULL for default firmware name
> + * Context: can sleep
> + *
> + * It returns zero on success, else a negative error code.
> + **/
> +int rk1608_download_fw(struct spi_device *spi, const char *fw_name);
> +
> +/**
> + * rk1608_msq_read_head - read rk1608 msg queue head
> + *
> + * @spi: spi device
> + * @addr: msg queue head addr
> + * @m: msg queue pointer
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_read_head(struct spi_device *spi,
> +                        u32 addr, struct rk1608_msg_queue *q);
> +
> +/**
> + * rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue
> + *
> + * @q: msg queue
> + * @m: a msg pointer buf [out]
> + *
> + * need call rk1608_msq_free_received_msg to free msg after msg use done
> + *
> + * It returns zero on success, else a negative error code.
> + */
> +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m);
> +#endif
> --
> 2.7.4
>
>

Please rebase your kernel tree to linux-next or media_tree, not
rockchip bsp kernel.

https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/
https://git.linuxtv.org/

^ permalink raw reply

* Re: [PATCH V8 2/3] OPP: Introduce "required-opp" property
From: Viresh Kumar @ 2017-12-22  5:28 UTC (permalink / raw)
  To: ulf.hansson, Kevin Hilman, robh+dt, Viresh Kumar, Nishanth Menon,
	Stephen Boyd, Rafael J. Wysocki
  Cc: linux-pm, Vincent Guittot, rnayak, sudeep.holla, devicetree,
	linux-kernel
In-Reply-To: <6615035f294a64a4c17e5b44ac6690d1c2ac127c.1513591822.git.viresh.kumar@linaro.org>

On 18-12-17, 15:51, Viresh Kumar wrote:
> Devices have inter-dependencies some times. For example a device that
> needs to run at 800 MHz, needs another device (e.g. Its power domain) to
> be configured at a particular operating performance point.
> 
> This patch introduces a new property "required-opp" which can be present
> directly in a device's node (if it doesn't need to change its OPPs), or
> in device's OPP nodes. More details on the property can be seen in the
> binding itself.

This is slightly updated based on Rob and Ulf's comments. Will apply it to the
OPP tree with below diff.

-- 
viresh

-------------------------8<-------------------------
Subject: [PATCH] OPP: Introduce "required-opp" property

Devices have inter-dependencies some times. For example a device that
needs to run at 800 MHz, needs another device (e.g. Its power domain) to
be configured at a particular operating performance point.

This patch introduces a new property "required-opp" which can be present
directly in a device's node (if it doesn't need to change its OPPs), or
in device's OPP nodes. More details on the property can be seen in the
binding itself.

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
Changes:
- s/opp_table/opp-table for node names.
- s/parent/power for domain alias.

 Documentation/devicetree/bindings/opp/opp.txt      |  8 +++
 .../devicetree/bindings/power/power_domain.txt     | 59 ++++++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
index a3953a1bb1a1..4e4f30288c8b 100644
--- a/Documentation/devicetree/bindings/opp/opp.txt
+++ b/Documentation/devicetree/bindings/opp/opp.txt
@@ -159,6 +159,14 @@ properties.
 
 - status: Marks the node enabled/disabled.
 
+- required-opp: This contains phandle to an OPP node in another device's OPP
+  table. It may contain an array of phandles, where each phandle points to an
+  OPP of a different device. It should not contain multiple phandles to the OPP
+  nodes in the same OPP table. This specifies the minimum required OPP of the
+  device(s), whose OPP's phandle is present in this property, for the
+  functioning of the current device at the current OPP (where this property is
+  present).
+
 Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
 
 / {
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
index 61549840ab3b..f3355313c020 100644
--- a/Documentation/devicetree/bindings/power/power_domain.txt
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -126,4 +126,63 @@ The node above defines a typical PM domain consumer device, which is located
 inside a PM domain with index 0 of a power controller represented by a node
 with the label "power".
 
+Optional properties:
+- required-opp: This contains phandle to an OPP node in another device's OPP
+  table. It may contain an array of phandles, where each phandle points to an
+  OPP of a different device. It should not contain multiple phandles to the OPP
+  nodes in the same OPP table. This specifies the minimum required OPP of the
+  device(s), whose OPP's phandle is present in this property, for the
+  functioning of the current device at the current OPP (where this property is
+  present).
+
+Example:
+- OPP table for domain provider that provides two domains.
+
+	domain0_opp_table: opp-table0 {
+		compatible = "operating-points-v2";
+
+		domain0_opp_0: opp-1000000000 {
+			opp-hz = /bits/ 64 <1000000000>;
+			opp-microvolt = <975000 970000 985000>;
+		};
+		domain0_opp_1: opp-1100000000 {
+			opp-hz = /bits/ 64 <1100000000>;
+			opp-microvolt = <1000000 980000 1010000>;
+		};
+	};
+
+	domain1_opp_table: opp-table1 {
+		compatible = "operating-points-v2";
+
+		domain1_opp_0: opp-1200000000 {
+			opp-hz = /bits/ 64 <1200000000>;
+			opp-microvolt = <975000 970000 985000>;
+		};
+		domain1_opp_1: opp-1300000000 {
+			opp-hz = /bits/ 64 <1300000000>;
+			opp-microvolt = <1000000 980000 1010000>;
+		};
+	};
+
+	power: power-controller@12340000 {
+		compatible = "foo,power-controller";
+		reg = <0x12340000 0x1000>;
+		#power-domain-cells = <1>;
+		operating-points-v2 = <&domain0_opp_table>, <&domain1_opp_table>;
+	};
+
+	leaky-device0@12350000 {
+		compatible = "foo,i-leak-current";
+		reg = <0x12350000 0x1000>;
+		power-domains = <&power 0>;
+		required-opp = <&domain0_opp_0>;
+	};
+
+	leaky-device1@12350000 {
+		compatible = "foo,i-leak-current";
+		reg = <0x12350000 0x1000>;
+		power-domains = <&power 1>;
+		required-opp = <&domain1_opp_1>;
+	};
+
 [1]. Documentation/devicetree/bindings/power/domain-idle-state.txt

^ permalink raw reply related

* Re: [PATCH V7 11/12] arm64: dts: add syscon for whale2 platform
From: Chunyan Zhang @ 2017-12-22  5:30 UTC (permalink / raw)
  To: Stephen Boyd, arm-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
  Cc: Chunyan Zhang, Michael Turquette, Rob Herring, Mark Rutland,
	Catalin Marinas, Will Deacon, linux-clk,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	Xiaolong Zhang, Ben Li, Orson Zhai, Arnd Bergmann
In-Reply-To: <20171221230316.GY7997-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

On 22 December 2017 at 07:03, Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org> wrote:
> On 12/07, Chunyan Zhang wrote:
>> Some clocks on SC9860 are in the same address area with syscon
>> devices, the proper syscon node will be quoted under the
>> definitions of those clocks in DT.
>>
>> Signed-off-by: Chunyan Zhang <chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
>> ---
>
> These last two can go via arm-soc?

Thanks Stephen!

Hi Arnd, Olof

Could you please take the patch 11, 12 through arm-soc?

Thanks,
Chunyan

>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH V7 12/12] arm64: dts: add clocks for SC9860
From: Chunyan Zhang @ 2017-12-22  5:31 UTC (permalink / raw)
  To: Chunyan Zhang, arm@kernel.org
  Cc: Stephen Boyd, Michael Turquette, Rob Herring, Mark Rutland,
	Catalin Marinas, Will Deacon, linux-clk, linux-kernel, devicetree,
	linux-arm-kernel, Arnd Bergmann, Mark Brown, Xiaolong Zhang,
	Ben Li, Orson Zhai
In-Reply-To: <20171207125715.16160-13-chunyan.zhang@spreadtrum.com>

+ arm@kernel.org

On 7 December 2017 at 20:57, Chunyan Zhang <chunyan.zhang@spreadtrum.com> wrote:
> Some clocks on SC9860 are in the same address area with syscon devices,
> those are what have a property of 'sprd,syscon' which would refer to
> syscon devices, others would have a reg property indicated their address
> ranges.
>
> Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
> ---
>  arch/arm64/boot/dts/sprd/sc9860.dtsi | 115 +++++++++++++++++++++++++++++++++++
>  arch/arm64/boot/dts/sprd/whale2.dtsi |  18 +++++-
>  2 files changed, 131 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/sprd/sc9860.dtsi b/arch/arm64/boot/dts/sprd/sc9860.dtsi
> index 7b7d8ce..bf03da4 100644
> --- a/arch/arm64/boot/dts/sprd/sc9860.dtsi
> +++ b/arch/arm64/boot/dts/sprd/sc9860.dtsi
> @@ -7,6 +7,7 @@
>   */
>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/clock/sprd,sc9860-clk.h>
>  #include "whale2.dtsi"
>
>  / {
> @@ -183,6 +184,120 @@
>         };
>
>         soc {
> +               pmu_gate: pmu-gate {
> +                       compatible = "sprd,sc9860-pmu-gate";
> +                       sprd,syscon = <&pmu_regs>; /* 0x402b0000 */
> +                       clocks = <&ext_26m>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               pll: pll {
> +                       compatible = "sprd,sc9860-pll";
> +                       sprd,syscon = <&ana_regs>; /* 0x40400000 */
> +                       clocks = <&pmu_gate 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               ap_clk: clock-controller@20000000 {
> +                       compatible = "sprd,sc9860-ap-clk";
> +                       reg = <0 0x20000000 0 0x400>;
> +                       clocks = <&ext_26m>, <&pll 0>,
> +                                <&pmu_gate 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               aon_prediv: aon-prediv {
> +                       compatible = "sprd,sc9860-aon-prediv";
> +                       reg = <0 0x402d0000 0 0x400>;
> +                       clocks = <&ext_26m>, <&pll 0>,
> +                                <&pmu_gate 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               apahb_gate: apahb-gate {
> +                       compatible = "sprd,sc9860-apahb-gate";
> +                       sprd,syscon = <&ap_ahb_regs>; /* 0x20210000 */
> +                       clocks = <&aon_prediv 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               aon_gate: aon-gate {
> +                       compatible = "sprd,sc9860-aon-gate";
> +                       sprd,syscon = <&aon_regs>; /* 0x402e0000 */
> +                       clocks = <&aon_prediv 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               aonsecure_clk: clock-controller@40880000 {
> +                       compatible = "sprd,sc9860-aonsecure-clk";
> +                       reg = <0 0x40880000 0 0x400>;
> +                       clocks = <&ext_26m>, <&pll 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               agcp_gate: agcp-gate {
> +                       compatible = "sprd,sc9860-agcp-gate";
> +                       sprd,syscon = <&agcp_regs>; /* 0x415e0000 */
> +                       clocks = <&aon_prediv 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               gpu_clk: clock-controller@60200000 {
> +                       compatible = "sprd,sc9860-gpu-clk";
> +                       reg = <0 0x60200000 0 0x400>;
> +                       clocks = <&pll 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               vsp_clk: clock-controller@61000000 {
> +                       compatible = "sprd,sc9860-vsp-clk";
> +                       reg = <0 0x61000000 0 0x400>;
> +                       clocks = <&ext_26m>, <&pll 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               vsp_gate: vsp-gate {
> +                       compatible = "sprd,sc9860-vsp-gate";
> +                       sprd,syscon = <&vsp_regs>; /* 0x61100000 */
> +                       clocks = <&vsp_clk 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               cam_clk: clock-controller@62000000 {
> +                       compatible = "sprd,sc9860-cam-clk";
> +                       reg = <0 0x62000000 0 0x4000>;
> +                       clocks = <&ext_26m>, <&pll 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               cam_gate: cam-gate {
> +                       compatible = "sprd,sc9860-cam-gate";
> +                       sprd,syscon = <&cam_regs>; /* 0x62100000 */
> +                       clocks = <&cam_clk 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               disp_clk: clock-controller@63000000 {
> +                       compatible = "sprd,sc9860-disp-clk";
> +                       reg = <0 0x63000000 0 0x400>;
> +                       clocks = <&ext_26m>, <&pll 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               disp_gate: disp-gate {
> +                       compatible = "sprd,sc9860-disp-gate";
> +                       sprd,syscon = <&disp_regs>; /* 0x63100000 */
> +                       clocks = <&disp_clk 0>;
> +                       #clock-cells = <1>;
> +               };
> +
> +               apapb_gate: apapb-gate {
> +                       compatible = "sprd,sc9860-apapb-gate";
> +                       sprd,syscon = <&ap_apb_regs>; /* 0x70b00000 */
> +                       clocks = <&ap_clk 0>;
> +                       #clock-cells = <1>;
> +               };
> +
>                 funnel@10001000 { /* SoC Funnel */
>                         compatible = "arm,coresight-funnel", "arm,primecell";
>                         reg = <0 0x10001000 0 0x1000>;
> diff --git a/arch/arm64/boot/dts/sprd/whale2.dtsi b/arch/arm64/boot/dts/sprd/whale2.dtsi
> index 6ea3a75..328009c 100644
> --- a/arch/arm64/boot/dts/sprd/whale2.dtsi
> +++ b/arch/arm64/boot/dts/sprd/whale2.dtsi
> @@ -106,10 +106,24 @@
>                 };
>         };
>
> -       ext_26m: ext-26m {
> +       ext_32k: ext_32k {
> +               compatible = "fixed-clock";
> +               #clock-cells = <0>;
> +               clock-frequency = <32768>;
> +               clock-output-names = "ext-32k";
> +       };
> +
> +       ext_26m: ext_26m {
>                 compatible = "fixed-clock";
>                 #clock-cells = <0>;
>                 clock-frequency = <26000000>;
> -               clock-output-names = "ext_26m";
> +               clock-output-names = "ext-26m";
> +       };
> +
> +       ext_rco_100m: ext_rco_100m {
> +               compatible = "fixed-clock";
> +               #clock-cells = <0>;
> +               clock-frequency = <100000000>;
> +               clock-output-names = "ext-rco-100m";
>         };
>  };
> --
> 2.7.4
>

^ permalink raw reply

* [PATCH v4 0/2] PCI: mediatek: Fixups for the IRQ handle routine and MT7622's class code
From: honghui.zhang-NuS5LvNUpcJWk0Htik3J/w @ 2017-12-22  5:39 UTC (permalink / raw)
  To: bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
	matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-pci-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w,
	eddie.huang-NuS5LvNUpcJWk0Htik3J/w,
	ryder.lee-NuS5LvNUpcJWk0Htik3J/w, lorenzo.pieralisi-5wv7dgnIgG8
  Cc: honghui.zhang-NuS5LvNUpcJWk0Htik3J/w,
	hongkun.cao-NuS5LvNUpcJWk0Htik3J/w,
	youlin.pei-NuS5LvNUpcJWk0Htik3J/w, yong.wu-NuS5LvNUpcJWk0Htik3J/w,
	yt.shen-NuS5LvNUpcJWk0Htik3J/w, sean.wang-NuS5LvNUpcJWk0Htik3J/w,
	xinping.qian-NuS5LvNUpcJWk0Htik3J/w

From: Honghui Zhang <honghui.zhang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

Two fixups for mediatek's host bridge:
The first patch fixup the IRQ handle routine to avoid IRQ reentry which
may exist for both MT2712 and MT7622.
The second patch fixup class type for MT7622.

Change since v3:
 - Setup the class type and vendor ID at the beginning of startup instead
   of in a quirk.
 - Add mediatek's vendor ID, it could be found in:
   https://pcisig.com/membership/member-companies?combine=&page=4

Change since v2:
 - Move the initialize of the iterate before the loop to fix an
   INTx IRQ issue in the first patch

Change since v1:
 - Add the second patch.
 - Make the first patch's commit message more standard.

Honghui Zhang (2):
  PCI: mediatek: Clear IRQ status after IRQ dispatched to avoid reentry
  PCI: mediatek: Set up class type and vendor ID for MT7622

 drivers/pci/host/pcie-mediatek.c | 23 ++++++++++++++++++-----
 include/linux/pci_ids.h          |  3 +++
 2 files changed, 21 insertions(+), 5 deletions(-)

-- 
2.6.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v4 1/2] PCI: mediatek: Clear IRQ status after IRQ dispatched to avoid reentry
From: honghui.zhang @ 2017-12-22  5:39 UTC (permalink / raw)
  To: bhelgaas, matthias.bgg, linux-arm-kernel, linux-mediatek,
	linux-pci, linux-kernel, devicetree, yingjoe.chen, eddie.huang,
	ryder.lee, lorenzo.pieralisi
  Cc: honghui.zhang, hongkun.cao, youlin.pei, yong.wu, yt.shen,
	sean.wang, xinping.qian
In-Reply-To: <1513921178-16148-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang@mediatek.com>

There maybe a same IRQ reentry scenario after IRQ received in current
IRQ handle flow:
	EP device		PCIe host driver	EP driver
1. issue an IRQ
			2. received IRQ
			3. clear IRQ status
			4. dispatch IRQ
						5. clear IRQ source
The IRQ status was not successfully cleared at step 2 since the IRQ
source was not cleared yet. So the PCIe host driver may receive the
same IRQ after step 5. Then there's an IRQ reentry occurred.
Even worse, if the reentry IRQ was not an IRQ that EP driver expected,
it may not handle the IRQ. Then we may run into the infinite loop from
step 2 to step 4.
Clear the IRQ status after IRQ have been dispatched to avoid the IRQ
reentry.
This patch also fix another INTx IRQ issue by initialize the iterate
before the loop. If an INTx IRQ re-occurred while we are dispatching
the INTx IRQ, then iterate may start from PCI_NUM_INTX + INTX_SHIFT
instead of INTX_SHIFT for the second time entering the
for_each_set_bit_from() loop.

Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
Acked-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/pci/host/pcie-mediatek.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
index db93efd..fc29a9a 100644
--- a/drivers/pci/host/pcie-mediatek.c
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -601,15 +601,16 @@ static irqreturn_t mtk_pcie_intr_handler(int irq, void *data)
 	struct mtk_pcie_port *port = (struct mtk_pcie_port *)data;
 	unsigned long status;
 	u32 virq;
-	u32 bit = INTX_SHIFT;
+	u32 bit;
 
 	while ((status = readl(port->base + PCIE_INT_STATUS)) & INTX_MASK) {
+		bit = INTX_SHIFT;
 		for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) {
-			/* Clear the INTx */
-			writel(1 << bit, port->base + PCIE_INT_STATUS);
 			virq = irq_find_mapping(port->irq_domain,
 						bit - INTX_SHIFT);
 			generic_handle_irq(virq);
+			/* Clear the INTx */
+			writel(1 << bit, port->base + PCIE_INT_STATUS);
 		}
 	}
 
@@ -619,10 +620,10 @@ static irqreturn_t mtk_pcie_intr_handler(int irq, void *data)
 
 			while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
 				for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
-					/* Clear the MSI */
-					writel(1 << bit, port->base + PCIE_IMSI_STATUS);
 					virq = irq_find_mapping(port->msi_domain, bit);
 					generic_handle_irq(virq);
+					/* Clear the MSI */
+					writel(1 << bit, port->base + PCIE_IMSI_STATUS);
 				}
 			}
 			/* Clear MSI interrupt status */
-- 
2.6.4

^ permalink raw reply related

* [PATCH v4 2/2] PCI: mediatek: Set up class type and vendor ID for MT7622
From: honghui.zhang @ 2017-12-22  5:39 UTC (permalink / raw)
  To: bhelgaas, matthias.bgg, linux-arm-kernel, linux-mediatek,
	linux-pci, linux-kernel, devicetree, yingjoe.chen, eddie.huang,
	ryder.lee, lorenzo.pieralisi
  Cc: youlin.pei, hongkun.cao, sean.wang, xinping.qian, honghui.zhang,
	yt.shen, yong.wu
In-Reply-To: <1513921178-16148-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang@mediatek.com>

The hardware default value of IDs and class type is not correct,
fix that by setup the correct values before start up.

Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
---
 drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
 include/linux/pci_ids.h          |  3 +++
 2 files changed, 15 insertions(+)

diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
index fc29a9a..0ef33e4 100644
--- a/drivers/pci/host/pcie-mediatek.c
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -74,6 +74,10 @@
 
 /* PCIe V2 per-port registers */
 #define PCIE_MSI_VECTOR		0x0c0
+
+#define PCIE_CONF_ID		0x100
+#define PCIE_CONF_CLASS		0x104
+
 #define PCIE_INT_MASK		0x420
 #define INTX_MASK		GENMASK(19, 16)
 #define INTX_SHIFT		16
@@ -393,6 +397,14 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
 		val |= PCIE_CSR_LTSSM_EN(port->slot) |
 		       PCIE_CSR_ASPM_L1_EN(port->slot);
 		writel(val, pcie->base + PCIE_SYS_CFG_V2);
+
+		/* Set up vendor ID and device ID for MT7622*/
+		val = PCI_VENDOR_ID_MEDIATEK | (PCI_DEVICE_ID_MT7622 << 16);
+		writel(val, port->base + PCIE_CONF_ID);
+
+		/* Set up class code for MT7622 */
+		val = PCI_CLASS_BRIDGE_PCI << 16;
+		writel(val, port->base + PCIE_CONF_CLASS);
 	}
 
 	/* Assert all reset signals */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index ab20dc5..000c5df 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2113,6 +2113,9 @@
 
 #define PCI_VENDOR_ID_MYRICOM		0x14c1
 
+#define PCI_VENDOR_ID_MEDIATEK		0x14c3
+#define PCI_DEVICE_ID_MT7622		0x5396
+
 #define PCI_VENDOR_ID_TITAN		0x14D2
 #define PCI_DEVICE_ID_TITAN_010L	0x8001
 #define PCI_DEVICE_ID_TITAN_100L	0x8010
-- 
2.6.4

^ permalink raw reply related

* Re: [PATCH v2 2/3] dt-bindings: PCI: dra7xx: Add properties to enable x2 lane in dra7
From: Kishon Vijay Abraham I @ 2017-12-22  6:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: Lorenzo Pieralisi, Bjorn Helgaas, Mark Rutland, linux-omap,
	devicetree, linux-pci, linux-kernel, nsekhar
In-Reply-To: <20171220185715.j5h7dlricom6kuiz@rob-hp-laptop>

Hi Rob,

On Thursday 21 December 2017 12:27 AM, Rob Herring wrote:
> On Tue, Dec 19, 2017 at 02:28:22PM +0530, Kishon Vijay Abraham I wrote:
>> Add syscon properties required for configuring PCIe in x2 lane mode.
>>
>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>> Signed-off-by: Sekhar Nori <nsekhar@ti.com>
>> ---
>>  Documentation/devicetree/bindings/pci/ti-pci.txt | 6 ++++++
>>  1 file changed, 6 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt
>> index 82cb875e4cec..bfbc77ac7355 100644
>> --- a/Documentation/devicetree/bindings/pci/ti-pci.txt
>> +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt
>> @@ -13,6 +13,12 @@ PCIe DesignWare Controller
>>   - ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>",
>>  	       where <X> is the instance number of the pcie from the HW spec.
>>   - num-lanes as specified in ../designware-pcie.txt
>> + - ti,syscon-lane-conf : phandle/offset pair. Phandle to the system control
>> +			 module and the register offset to specify 1 lane or
>> +			 2 lane.
>> + - ti,syscon-lane-sel : phandle/offset pair. Phandle to the system control
>> +			module and the register offset to specify lane
>> +			selection.
> 
> Adding a property for every syscon register doesn't really scale and 
> doesn't work if the register layout changes.

The register layout doesn't really change between silicon revisions and for new
SoCs, the phandle and the register offset for that SoC will have to be
populated again.

Having said that, I'm not aware of any other alternative here.

Thanks
Kishon

^ permalink raw reply

* Re: [PATCH 1/5] bindings: regulator: added support for suspend states
From: Chunyan Zhang @ 2017-12-22  6:05 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Chunyan Zhang
In-Reply-To: <20171221232614.rhrgrpyl2w2lc6bw@rob-hp-laptop>

Hi Rob,

On 22 December 2017 at 07:26, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Thu, Dec 21, 2017 at 02:25:02PM +0800, Chunyan Zhang wrote:
>> Documented a few new added properties which are used for supporting
>> regulator suspend states.
>
> Your commit message should answer why you need this. What problem do you
> have that this solves?
>
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  Documentation/devicetree/bindings/regulator/regulator.txt | 11 +++++++++--
>>  1 file changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt
>> index 378f6dc..618a322 100644
>> --- a/Documentation/devicetree/bindings/regulator/regulator.txt
>> +++ b/Documentation/devicetree/bindings/regulator/regulator.txt
>> @@ -42,8 +42,15 @@ Optional properties:
>>  - regulator-state-[mem/disk] node has following common properties:
>>       - regulator-on-in-suspend: regulator should be on in suspend state.
>>       - regulator-off-in-suspend: regulator should be off in suspend state.
>> -     - regulator-suspend-microvolt: regulator should be set to this voltage
>> -       in suspend.
>> +     - regulator-suspend-min-microvolt: minimum voltage may be set in
>> +       suspend state.
>> +     - regulator-suspend-max-microvolt: maximum voltage may be set in
>> +       suspend state.
>> +     - regulator-suspend-microvolt: the default voltage which regulator
>> +       should be set in suspend, this can be adjusted among
>> +       <regulator-suspend-min-microvolt, regulator-suspend-max-microvolt>
>
> Perhaps this should stay a single property with: <target> <min> <max>

Do you mean that change the property name from "regulator-suspend-microvolt" to
"regulator-suspend-target-microvolt"?

"regulator-suspend-microvolt" is the one that some SoC is using. My
intention was just to keep that configuration still working.

> Though why would you ever not try to set to the minimum voltage within
> the range of <min> to <max>?

IIUC, you mean just removing "regulator-suspend-microvolt", and use
"regulator-suspend-min-microvolt" as the default value for suspend
voltage?
I think that can work, but would it be better to not remove that right
now, since some one is using that?

Thanks for your review!

Chunyan

>
>> +     - regulator-changeable-in-suspend: whether the default voltage and
>> +       the regulator on/off in suspend can be changed in runtime.
>>       - regulator-mode: operating mode in the given suspend state.
>>         The set of possible operating modes depends on the capabilities of
>>         every hardware so the valid modes are documented on each regulator
>> --
>> 2.7.4
>>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] arm: dts: mt7623: enable all four available UARTs on bananapi-r2
From: sean.wang-NuS5LvNUpcJWk0Htik3J/w @ 2017-12-22  6:06 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sean Wang

From: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

On bpi-r2 board, totally there're four uarts which we usually called
uart[0-3] helpful to extend slow I/O devices. Among those ones, uart2 has
dedicated pin slot which is used to conolse log. uart[0-1] appear at the
40-pins connector and uart3 has no pinout, but just has test points (TP47
for TX and TP48 for RX, respectively) nearby uart2. Also, some missing
pinctrl is being complemented for those devices.

Signed-off-by: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
index 7bf5aa2..64bf5db 100644
--- a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
+++ b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
@@ -409,6 +409,20 @@
 				 <MT7623_PIN_82_UTXD1_FUNC_UTXD1>;
 		};
 	};
+
+	uart2_pins_a: uart@2 {
+		pins_dat {
+			pinmux = <MT7623_PIN_14_GPIO14_FUNC_URXD2>,
+				 <MT7623_PIN_15_GPIO15_FUNC_UTXD2>;
+		};
+	};
+
+	uart3_pins_a: uart@3 {
+		pins_dat {
+			pinmux = <MT7623_PIN_242_URTS2_FUNC_URTS2>,
+				 <MT7623_PIN_243_UCTS2_FUNC_UTXD3>;
+		};
+	};
 };
 
 &pwm {
@@ -454,16 +468,24 @@
 &uart0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart0_pins_a>;
-	status = "disabled";
+	status = "okay";
 };
 
 &uart1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart1_pins_a>;
-	status = "disabled";
+	status = "okay";
 };
 
 &uart2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2_pins_a>;
+	status = "okay";
+};
+
+&uart3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart3_pins_a>;
 	status = "okay";
 };
 
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [PATCH] arm: dts: mt7623: enable all four available UARTs on bananapi-r2
From: Sean Wang @ 2017-12-22  6:24 UTC (permalink / raw)
  To: matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w
  Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <b4e84a68da76c1ca538150a730777ff530c1db5a.1513922513.git.sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

On Fri, 2017-12-22 at 14:06 +0800, sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org wrote:
> From: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> 
> On bpi-r2 board, totally there're four uarts which we usually called
> uart[0-3] helpful to extend slow I/O devices. Among those ones, uart2 has
> dedicated pin slot which is used to conolse log. uart[0-1] appear at the

Hi, Matthias

Could you help to fix the misspelling "conolse" with "console" when you
merge the patch ?

	Sean

> 40-pins connector and uart3 has no pinout, but just has test points (TP47
> for TX and TP48 for RX, respectively) nearby uart2. Also, some missing
> pinctrl is being complemented for those devices.
> 
> Signed-off-by: Sean Wang <sean.wang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> ---
>  arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts | 26 ++++++++++++++++++++++++--
>  1 file changed, 24 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
> index 7bf5aa2..64bf5db 100644
> --- a/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
> +++ b/arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
> @@ -409,6 +409,20 @@
>  				 <MT7623_PIN_82_UTXD1_FUNC_UTXD1>;
>  		};
>  	};
> +
> +	uart2_pins_a: uart@2 {
> +		pins_dat {
> +			pinmux = <MT7623_PIN_14_GPIO14_FUNC_URXD2>,
> +				 <MT7623_PIN_15_GPIO15_FUNC_UTXD2>;
> +		};
> +	};
> +
> +	uart3_pins_a: uart@3 {
> +		pins_dat {
> +			pinmux = <MT7623_PIN_242_URTS2_FUNC_URTS2>,
> +				 <MT7623_PIN_243_UCTS2_FUNC_UTXD3>;
> +		};
> +	};
>  };
>  
>  &pwm {
> @@ -454,16 +468,24 @@
>  &uart0 {
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&uart0_pins_a>;
> -	status = "disabled";
> +	status = "okay";
>  };
>  
>  &uart1 {
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&uart1_pins_a>;
> -	status = "disabled";
> +	status = "okay";
>  };
>  
>  &uart2 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&uart2_pins_a>;
> +	status = "okay";
> +};
> +
> +&uart3 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&uart3_pins_a>;
>  	status = "okay";
>  };
>  


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] ARM: realview: remove eb-mp clcd IRQ
From: Arnd Bergmann @ 2017-12-22  7:12 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Rob Herring, Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux ARM, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CACRpkdbCWZB1ZkrdGe0s0GfNVWEYY+NThy4ce-+SBtU-s=WNbQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Thu, Dec 21, 2017 at 11:08 PM, Linus Walleij
<linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On Thu, Dec 21, 2017 at 10:31 PM, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
>
>> We get a dtc warning about the CLCD interrupt being invalid:
>>
>> arch/arm/boot/dts/arm-realview-eb-11mp-ctrevb.dtb: Warning (interrupts_property): interrupts size is (8), expected multiple of 12 in /fpga/charlcd@10008000
>>
>> According to the datasheet I found and the old board file, this
>> line is not connected, so I'm removing the respective properties here.
>>
>> Link: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0411d/index.html
>> Signed-off-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
>
> There is some confusion here. There is CLCD "Color LCD"
> which is just a code name for PrimeCell PL111 and there is the actual
> character LCD which is a hardware thin to talk to a character LCD with
> some characters on.
>
>> diff --git a/arch/arm/boot/dts/arm-realview-eb-mp.dtsi b/arch/arm/boot/dts/arm-realview-eb-mp.dtsi
>
> So this DTS is for the ARM 11 MP core tile which is described in
> DUI0318F. It doesn't even list an IRQ for the character LCD.
>
>>  &charlcd {
>> -       interrupt-parent = <&intc>;
>> -       interrupts = <0  IRQ_TYPE_LEVEL_HIGH>;
>
> This was probably me thinking to go back and fill in the right
> IRQ and forgetting to actually do it. Sorry :(
>
>> +       /* CLCD is not connected here */
>
> Call it character LCD instead to avoid confusion please.
>
>> +       /delete-property/interrupt-parent;
>> +       /delete-property/interrupts;
>
> I don't understand this delete-property business (first time
> I see it, but the top level
> DTSI (arm-realview-eb.dtsi) does not define any interrupt
> so can't you just delete this whole &charlcd?
>
> I do think the reference design has a character LCD, and I
> do think it has an interrupt, it's just undocumented so
> someone with this board would have to test it manually
> to figure out which line it is. Whoever uses this design
> will get to it if ever.

Sounds good, can you prepare a patch for this?

We are getting very close to zero warnings now with the
latest arm-soc/for-next branch:

arch/arm/boot/dts/arm-realview-eb-11mp.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/arm-realview-eb-11mp-bbrevd.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/arm-realview-eb-11mp-ctrevb.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/arm-realview-eb-a9mp.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/arm-realview-eb-11mp-bbrevd-ctrevb.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/arm-realview-eb-a9mp-bbrevd.dtb: Warning
(interrupts_property): interrupts size is (8), expected multiple of 12
in /fpga/charlcd@10008000
arch/arm/boot/dts/s5pv210-smdkv210.dtb: Warning (interrupts_property):
Missing interrupt-parent for /soc/ohci@ec300000
arch/arm/boot/dts/s5pv210-smdkc110.dtb: Warning (interrupts_property):
arch/arm/boot/dts/s5pv210-goni.dtb: Warning (interrupts_property):
Missing interrupt-parent for /soc/ohci@ec300000
arch/arm/boot/dts/s5pv210-aquila.dtb: Warning (interrupts_property):
Missing interrupt-parent for /soc/ohci@ec300000
Missing interrupt-parent for /soc/ohci@ec300000
arch/arm/boot/dts/s5pv210-torbreck.dtb: Warning (interrupts_property):
Missing interrupt-parent for /soc/ohci@ec300000
arch/arm/boot/dts/spear1310-evb.dtb: Warning (gpios_property):
Property 'cs-gpios', cell 6 is not a phandle reference in
/ahb/apb/spi@e0100000
arch/arm/boot/dts/spear1310-evb.dtb: Warning (gpios_property): Missing
property '#gpio-cells' in node /interrupt-controller@ec801000 or bad
phandle (referred from /ahb/apb/spi@e0100000:cs-gpios[6])
arch/arm/boot/dts/spear1340-evb.dtb: Warning (dmas_property): Property
'dmas', cell 4 is not a phandle reference in /ahb/apb/serial@b4100000
arch/arm/boot/dts/spear1340-evb.dtb: Warning (dmas_property): Missing
property '#dma-cells' in node /interrupt-controller@ec801000 or bad
phandle (referred from /ahb/apb/serial@b4100000:dmas[4])
arch/arm/boot/dts/stih407-b2120.dtb: Warning (gpios_property):
hdmi,hpd-gpio property size (8) too small for cell size 2 in
/soc/sti-display-subsystem/sti-hdmi@8d04000
arch/arm/boot/dts/stih410-b2120.dtb: Warning (gpios_property):
hdmi,hpd-gpio property size (8) too small for cell size 2 in
/soc/sti-display-subsystem/sti-hdmi@8d04000
arch/arm/boot/dts/stih410-b2260.dtb: Warning (gpios_property):
hdmi,hpd-gpio property size (8) too small for cell size 2 in
/soc/sti-display-subsystem/sti-hdmi@8d04000
arch/arm/boot/dts/lpc3250-ea3250.dtb: Warning (gpios_property):
power-gpio property size (12) too small for cell size 3 in
/ahb/apb/i2c@400A0000/uda1380@18
arch/arm/boot/dts/lpc3250-ea3250.dtb: Warning (gpios_property):
reset-gpio property size (12) too small for cell size 3 in
/ahb/apb/i2c@400A0000/uda1380@18
arch/arm/boot/dts/lpc3250-phy3250.dtb: Warning (gpios_property):
power-gpio property size (12) too small for cell size 3 in
/ahb/apb/i2c@400A0000/uda1380@18
arch/arm/boot/dts/lpc3250-phy3250.dtb: Warning (gpios_property):
reset-gpio property size (12) too small for cell size 3 in
/ahb/apb/i2c@400A0000/uda1380@18
arch/arm/boot/dts/ste-nomadik-s8815.dtb: Warning
(interrupts_property): Missing interrupt-parent for
/amba/clcd@10120000
arch/arm/boot/dts/ste-nomadik-nhk15.dtb: Warning
(interrupts_property): Missing interrupt-parent for
/amba/clcd@10120000
arch/arm/boot/dts/spear600-evb.dtb: Warning (interrupts_property):
Missing interrupt-parent for /ahb/apb/rtc@fc900000

Can you also look at the nomadik warnings and possibly some of the others
if you have time? Unfortunately I'm heading out for my Christmas vacation
and can't do any others of these any more.

      Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 4/6] ARM: dts: imx6: Add support for phxBOARD-Mira i.MX 6 DualLight/Solo RDK
From: Stefan Riedmüller @ 2017-12-22  7:33 UTC (permalink / raw)
  To: Lothar Waßmann
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	fabio.estevam-3arQi8VN3Tc, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	Christian Hemp, Stefan Christ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20171220151008.4aa4b5a5-AvR2QvxeiV7DiMYJYoSAnRvVK+yQ3ZXh@public.gmane.org>

Hi,

On 20.12.2017 15:10, Lothar Waßmann wrote:
> Hi,
>
> On Wed, 20 Dec 2017 14:29:25 +0100 Stefan Riedmueller wrote:
>> From: Christian Hemp <c.hemp-guT5V/WYfQezQB+pC5nmwQ@public.gmane.org>
>>
>> Add support for the PHYTEC phyBOARD-Mira Low-Cost Rapid Development Kit
>> with i.MX 6DualLight/Solo with NAND.
>>
> s/phxBOARD/phyBOARD/ in the subject line.
>
>
> Lothar Waßmann
I'll fix it, thanks.

Stefan

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [RFC 2/2] pci: dwc: pci-exynos: add the codes to support the exynos5433
From: Jingoo Han @ 2017-12-22  7:49 UTC (permalink / raw)
  To: 'Jaehoon Chung', linux-pci-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	kgene-DgEjT+Ai2ygdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8
In-Reply-To: <21cb6abf-5428-03cf-77f1-be762d96d156-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

On Thursday, December 21, 2017 9:21 PM, Jaehoon Chung wrote:
> On 12/22/2017 01:12 AM, Jingoo Han wrote:
> > On Thursday, December 21, 2017 7:14 AM, Jaehoon Chung wrote:
> >>
> >> Exynos5433 has the PCIe for WiFi.
> >> Added the codes relevant to PCIe for supporting the exynos5433.
> >> Also changed the binding documentation name to
> >> 'samsung,exynos-pcie.txt'.
> >> (It's not only exynos5440 anymore.)
> >>
> >
> > I have no objection.
> > However, I added some comments about Exynos5440.
> >
> >> Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> >> ---
> >>  ...exynos5440-pcie.txt => samsung,exynos-pcie.txt} |   2 +-
> >>  drivers/pci/dwc/pci-exynos.c                       | 183
> > ++++++++++++++++-----
> >>  2 files changed, 144 insertions(+), 41 deletions(-)
> >>  rename Documentation/devicetree/bindings/pci/{samsung,exynos5440-
> pcie.txt
> >> => samsung,exynos-pcie.txt} (97%)
> >>
> >> diff --git a/Documentation/devicetree/bindings/pci/samsung,exynos5440-
> >> pcie.txt b/Documentation/devicetree/bindings/pci/samsung,exynos-
> pcie.txt
> >> similarity index 97%
> >> rename from Documentation/devicetree/bindings/pci/samsung,exynos5440-
> >> pcie.txt
> >> rename to Documentation/devicetree/bindings/pci/samsung,exynos-pcie.txt
> >> index 34a11bfbfb60..958dcc150505 100644
> >> --- a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
> >> +++ b/Documentation/devicetree/bindings/pci/samsung,exynos-pcie.txt
> >> @@ -4,7 +4,7 @@ This PCIe host controller is based on the Synopsys
> >> DesignWare PCIe IP
> >>  and thus inherits all the common properties defined in designware-
> >> pcie.txt.
> >>
> >>  Required properties:
> >> -- compatible: "samsung,exynos5440-pcie"
> >> +- compatible: "samsung,exynos5440-pcie" or "samsung,exynos5433-pcie"
> >>  - reg: base addresses and lengths of the PCIe controller,
> >>  	the PHY controller, additional register for the PHY controller.
> >>  	(Registers for the PHY controller are DEPRECATED.
> >> diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-
> exynos.c
> >> index 5596fdedbb94..8dee2e90347e 100644
> >> --- a/drivers/pci/dwc/pci-exynos.c
> >> +++ b/drivers/pci/dwc/pci-exynos.c
> >> @@ -40,6 +40,8 @@
> >>  #define PCIE_IRQ_SPECIAL		0x008
> >>  #define PCIE_IRQ_EN_PULSE		0x00c
> >>  #define PCIE_IRQ_EN_LEVEL		0x010
> >> +#define PCIE_SW_WAKE			0x018
> >> +#define PCIE_BUS_EN			BIT(1)
> >>  #define IRQ_MSI_ENABLE			BIT(2)
> >>  #define PCIE_IRQ_EN_SPECIAL		0x014
> >>  #define PCIE_PWR_RESET			0x018
> >> @@ -49,7 +51,8 @@
> >>  #define PCIE_NONSTICKY_RESET		0x024
> >>  #define PCIE_APP_INIT_RESET		0x028
> >>  #define PCIE_APP_LTSSM_ENABLE		0x02c
> >> -#define PCIE_ELBI_RDLH_LINKUP		0x064
> >> +#define PCIE_ELBI_RDLH_LINKUP		0x074
> >
> > The address of this register should be 0x064 for exynos5440.
> > Howe about the following?
> >
> > +#define EXYNOS5440_PCIE_ELBI_RDLH_LINKUP	0x064
> > +#define PCIE_ELBI_RDLH_LINKUP		0x074
> >
> > Or you can add the following.
> >
> > /* Exynos5440 PCIe ELBI registers */
> > #define EXYNOS5440_PCIE_ELBI_RDLH_LINKUP	0x064
> 
> Maybe, you're right. Because i didn't have Exynos5440 TRM, it's problem to
> me about updating other SoCs.
> I have checked almost all variants Exynos. They are using the LINKUP
> register as 0x74.
> 
> If i can get the exynos5440 TRM, it's much helpful to me. Is it possible?

I don't have Exynos5440 TRM.
Please ask other guys.

Best regards,
Jingoo Han

> 
> >
> >> +#define PCIE_ELBI_XMLH_LINKUP		BIT(4)
> >>  #define PCIE_ELBI_LTSSM_ENABLE		0x1
> >>  #define PCIE_ELBI_SLV_AWMISC		0x11c
> >>  #define PCIE_ELBI_SLV_ARMISC		0x120
> >> @@ -94,6 +97,10 @@
> >>  #define PCIE_PHY_TRSV3_PD_TSV		BIT(7)
> >>  #define PCIE_PHY_TRSV3_LVCC		0x31c
> >>
> >> +/* DBI register */
> >> +#define PCIE_MISC_CONTROL_1_OFF		0x8BC
> >> +#define DBI_RO_WR_EN			BIT(0)
> >> +
> >>  struct exynos_pcie_mem_res {
> >>  	void __iomem *elbi_base;   /* DT 0th resource: PCIe CTRL */
> >>  	void __iomem *phy_base;    /* DT 1st resource: PHY CTRL */
> >> @@ -221,6 +228,96 @@ static const struct exynos_pcie_ops
> >> exynos5440_pcie_ops = {
> >>  	.deinit_clk_resources	= exynos5440_pcie_deinit_clk_resources,
> >>  };
> >>
> >> +static int exynos5433_pcie_get_mem_resources(struct platform_device
> > *pdev,
> >> +					     struct exynos_pcie *ep)
> >> +{
> >> +	struct dw_pcie *pci = ep->pci;
> >> +	struct device *dev = pci->dev;
> >> +	struct resource *res;
> >> +
> >> +	ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
> >> +	if (!ep->mem_res)
> >> +		return -ENOMEM;
> >> +
> >> +	/* External Local Bus interface(ELBI) Register */
> >> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
> >> +	ep->mem_res->elbi_base = devm_ioremap_resource(&pdev->dev, res);
> >> +	if (IS_ERR(ep->mem_res->elbi_base))
> >> +		return PTR_ERR(ep->mem_res->elbi_base);
> >> +
> >> +	/* Data Bus Interface(DBI) Register */
> >> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
> >> +	pci->dbi_base = devm_ioremap_resource(&pdev->dev, res);
> >> +	if (IS_ERR(pci->dbi_base))
> >> +		return PTR_ERR(pci->dbi_base);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int exynos5433_pcie_get_clk_resources(struct exynos_pcie *ep)
> >> +{
> >> +	struct dw_pcie *pci = ep->pci;
> >> +	struct device *dev = pci->dev;
> >> +
> >> +	ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
> >> +	if (!ep->clk_res)
> >> +		return -ENOMEM;
> >> +
> >> +	ep->clk_res->clk = devm_clk_get(dev, "pcie");
> >> +	if (IS_ERR(ep->clk_res->clk)) {
> >> +		dev_err(dev, "Failed to get pcie rc clock\n");
> >> +		return PTR_ERR(ep->clk_res->clk);
> >> +	}
> >> +
> >> +	ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
> >> +	if (IS_ERR(ep->clk_res->bus_clk)) {
> >> +		dev_err(dev, "Failed to get pcie bus clock\n");
> >> +		return PTR_ERR(ep->clk_res->bus_clk);
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static void exynos5433_pcie_deinit_clk_resources(struct exynos_pcie
> *ep)
> >> +{
> >> +	clk_disable_unprepare(ep->clk_res->bus_clk);
> >> +	clk_disable_unprepare(ep->clk_res->clk);
> >> +}
> >> +
> >> +
> >> +static int exynos5433_pcie_init_clk_resources(struct exynos_pcie *ep)
> >> +{
> >> +	struct dw_pcie *pci = ep->pci;
> >> +	struct device *dev = pci->dev;
> >> +	int ret;
> >> +
> >> +	ret = clk_prepare_enable(ep->clk_res->clk);
> >> +	if (ret) {
> >> +		dev_err(dev, "cannot enable pcie rc clock");
> >> +		return ret;
> >> +	}
> >> +
> >> +	ret = clk_prepare_enable(ep->clk_res->bus_clk);
> >> +	if (ret) {
> >> +		dev_err(dev, "cannot enable pcie bus clock");
> >> +		goto err_bus_clk;
> >> +	}
> >> +
> >> +	return 0;
> >> +
> >> +err_bus_clk:
> >> +	clk_disable_unprepare(ep->clk_res->clk);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static const struct exynos_pcie_ops exynos5433_pcie_ops = {
> >> +	.get_mem_resources	= exynos5433_pcie_get_mem_resources,
> >> +	.get_clk_resources	= exynos5433_pcie_get_clk_resources,
> >> +	.init_clk_resources	= exynos5433_pcie_init_clk_resources,
> >> +	.deinit_clk_resources	= exynos5433_pcie_deinit_clk_resources,
> >> +};
> >> +
> >>  static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
> >>  {
> >>  	writel(val, base + reg);
> >> @@ -279,7 +376,9 @@ static void exynos_pcie_deassert_core_reset(struct
> >> exynos_pcie *ep)
> >>  	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
> >>  	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
> >>  	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
> >> -	exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
> >> +	if (ep->mem_res->block_base)
> >> +		exynos_pcie_writel(ep->mem_res->block_base, 1,
> >> +				PCIE_PHY_MAC_RESET);
> >
> > Good.
> >
> >>  }
> >>
> >>  static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
> >> @@ -413,9 +512,6 @@ static int exynos_pcie_establish_link(struct
> >> exynos_pcie *ep)
> >>  	if (ep->using_phy) {
> >>  		phy_reset(ep->phy);
> >>
> >> -		exynos_pcie_writel(ep->mem_res->elbi_base, 1,
> >> -				PCIE_PWR_RESET);
> >> -
> >>  		phy_power_on(ep->phy);
> >>  		phy_init(ep->phy);
> >>  	} else {
> >> @@ -430,14 +526,16 @@ static int exynos_pcie_establish_link(struct
> >> exynos_pcie *ep)
> >>  		udelay(500);
> >>  		exynos_pcie_writel(ep->mem_res->block_base, 0,
> >>  					PCIE_PHY_COMMON_RESET);
> >> +		exynos_pcie_deassert_core_reset(ep);
> >>  	}
> >>
> >> -	/* pulse for common reset */
> >> -	exynos_pcie_writel(ep->mem_res->block_base, 1,
> >> PCIE_PHY_COMMON_RESET);
> >> -	udelay(500);
> >> -	exynos_pcie_writel(ep->mem_res->block_base, 0,
> >> PCIE_PHY_COMMON_RESET);
> >
> > These codes are also necessary for Exyno5440.
> > How about moving these codes instead of removing them?
> >
> > @@ -430,14 +526,16 @@ static int exynos_pcie_establish_link(struct
> > exynos_pcie *ep)
> >  		udelay(500);
> >  		exynos_pcie_writel(ep->mem_res->block_base, 0,
> >  					PCIE_PHY_COMMON_RESET);
> > +		/* pulse for common reset */
> > +		exynos_pcie_writel(ep->mem_res->block_base, 1,
> > +					PCIE_PHY_COMMON_RESET);
> > +		udelay(500);
> > +		exynos_pcie_writel(ep->mem_res->block_base, 0,
> > +					PCIE_PHY_COMMON_RESET);
> > +		exynos_pcie_deassert_core_reset(ep);
> >  	}
> >
> >
> >> +	/*
> >> +	 * Enable DBI_RO_WR_EN bit.
> >> +	 * - When set to 1, some RO and HWinit bits are wriatble from
> >> +	 *   the local application through the DBI.
> >> +	 */
> >> +	dw_pcie_writel_dbi(pci, PCIE_MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
> >>
> >> -	exynos_pcie_deassert_core_reset(ep);
> >>  	dw_pcie_setup_rc(pp);
> >>  	exynos_pcie_assert_reset(ep);
> >>
> >> @@ -472,16 +570,6 @@ static void exynos_pcie_clear_irq_pulse(struct
> >> exynos_pcie *ep)
> >>  	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
> >>  }
> >>
> >> -static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> >> -{
> >> -	u32 val;
> >> -
> >> -	/* enable INTX interrupt */
> >> -	val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
> >> -		IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
> >> -	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
> >> -}
> >> -
> >>  static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
> >>  {
> >>  	struct exynos_pcie *ep = arg;
> >> @@ -513,9 +601,16 @@ static void exynos_pcie_msi_init(struct
> exynos_pcie
> >> *ep)
> >>  	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
> >>  }
> >>
> >> -static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep)
> >> +static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> >>  {
> >> -	exynos_pcie_enable_irq_pulse(ep);
> >> +	u32 val;
> >> +
> >> +	val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
> >> +		IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
> >> +	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
> >> +
> >> +	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
> >> +	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
> >
> > Good.
> >
> >>
> >>  	if (IS_ENABLED(CONFIG_PCI_MSI))
> >>  		exynos_pcie_msi_init(ep);
> >> @@ -575,10 +670,8 @@ static int exynos_pcie_link_up(struct dw_pcie *pci)
> >>  	u32 val;
> >>
> >>  	val = exynos_pcie_readl(ep->mem_res->elbi_base,
> >> PCIE_ELBI_RDLH_LINKUP);
> >> -	if (val == PCIE_ELBI_LTSSM_ENABLE)
> >> -		return 1;
> >
> > Exynos5440 uses 'PCIE_ELBI_LTSSM_ENABLE'.
> > Can you keep this code for Exyno5440?
> 
> It's possible to keep, but if it has to keep, then it needs to distinguish
> between exynos5440 and other exynos.
> Although I already mentioned, i needs to get Exynos5440 TRM. :)
> 
> Best Regards,
> Jaehoon Chung
> 
> >
> > This register can be added as below.
> >
> > /* Exynos5440 PCIe ELBI registers */
> > #define EXYNOS5440_PCIE_ELBI_RDLH_LINKUP	0x064
> > #define EXYNOS5440_PCIE_ELBI_LTSSM_ENABLE	BIT(0)
> >
> > Best regards,
> > Jingoo Han
> >
> >>
> >> -	return 0;
> >> +	return (val & PCIE_ELBI_XMLH_LINKUP);
> >>  }
> >>
> >>  static int exynos_pcie_host_init(struct pcie_port *pp)
> >> @@ -587,7 +680,7 @@ static int exynos_pcie_host_init(struct pcie_port
> *pp)
> >>  	struct exynos_pcie *ep = to_exynos_pcie(pci);
> >>
> >>  	exynos_pcie_establish_link(ep);
> >> -	exynos_pcie_enable_interrupts(ep);
> >> +	exynos_pcie_enable_irq_pulse(ep);
> >>
> >>  	return 0;
> >>  }
> >> @@ -608,8 +701,11 @@ static int __init exynos_add_pcie_port(struct
> >> exynos_pcie *ep,
> >>
> >>  	pp->irq = platform_get_irq(pdev, 1);
> >>  	if (pp->irq < 0) {
> >> -		dev_err(dev, "failed to get irq\n");
> >> -		return pp->irq;
> >> +		pp->irq = platform_get_irq_byname(pdev, "intr");
> >> +		if (pp->irq < 0) {
> >> +			dev_err(dev, "failed to get irq\n");
> >> +			return pp->irq;
> >> +		}
> >>  	}
> >>  	ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
> >>  				IRQF_SHARED, "exynos-pcie", ep);
> >> @@ -678,13 +774,23 @@ static int __init exynos_pcie_probe(struct
> >> platform_device *pdev)
> >>
> >>  	ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
> >>
> >> -	/* Assume that controller doesn't use the PHY framework */
> >> -	ep->using_phy = false;
> >> +	/*
> >> +	 * In case of Exynos5440,
> >> +	 * Assume that controller doesn't use the PHY frameork.
> >> +	 * Other SoCs might be used the PHY framework.
> >> +	 */
> >> +
> >> +	if (of_device_is_compatible(np, "samsung,exynos5440-pcie"))
> >> +		ep->using_phy = false;
> >>
> >> -	ep->phy = devm_of_phy_get(dev, np, NULL);
> >> +	ep->phy = devm_of_phy_get(dev, np, "pcie-phy");
> >>  	if (IS_ERR(ep->phy)) {
> >>  		if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
> >>  			return PTR_ERR(ep->phy);
> >> +		if (!of_device_is_compatible(np, "samsung,exynos5440-pcie"))
> >> {
> >> +			dev_err(dev, "Can't find the pcie-phy\n");
> >> +			return PTR_ERR(ep->phy);
> >> +		}
> >>  		dev_warn(dev, "Use the 'phy' property. Current DT of pci-
> >> exynos was deprecated!!\n");
> >>  	} else
> >>  		ep->using_phy = true;
> >> @@ -734,23 +840,20 @@ static int __exit exynos_pcie_remove(struct
> >> platform_device *pdev)
> >>  static const struct of_device_id exynos_pcie_of_match[] = {
> >>  	{
> >>  		.compatible = "samsung,exynos5440-pcie",
> >> -		.data = &exynos5440_pcie_ops
> >> +		.data = &exynos5440_pcie_ops,
> >> +	}, {
> >> +		.compatible = "samsung,exynos5433-pcie",
> >> +		.data = &exynos5433_pcie_ops,
> >>  	},
> >>  	{},
> >>  };
> >>
> >>  static struct platform_driver exynos_pcie_driver = {
> >> +	.probe		= exynos_pcie_probe,
> >>  	.remove		= __exit_p(exynos_pcie_remove),
> >>  	.driver = {
> >>  		.name	= "exynos-pcie",
> >>  		.of_match_table = exynos_pcie_of_match,
> >>  	},
> >>  };
> >> -
> >> -/* Exynos PCIe driver does not allow module unload */
> >> -
> >> -static int __init exynos_pcie_init(void)
> >> -{
> >> -	return platform_driver_probe(&exynos_pcie_driver,
> >> exynos_pcie_probe);
> >> -}
> >> -subsys_initcall(exynos_pcie_init);
> >> +builtin_platform_driver(exynos_pcie_driver);
> >> --
> >> 2.15.1
> >
> >
> >
> >
> >


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v5 2/3] PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver
From: Subrahmanya Lingappa @ 2017-12-22  7:59 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: linux-pci, Bjorn Helgaas, Marc Zyngier, robh, devicetree,
	Mingkai Hu, Peter W Newton, M.h. Lian, Raj Raina, Rajan Kapoor,
	Prabhjot Singh
In-Reply-To: <20171220170307.GC1709@red-moon>

Lorenzo,

On Wed, Dec 20, 2017 at 10:33 PM, Lorenzo Pieralisi
<lorenzo.pieralisi@arm.com> wrote:
>
> On Wed, Dec 13, 2017 at 11:08:37AM -0500, Subrahmanya Lingappa wrote:
> > Adds driver for Mobiveil AXI PCIe Host Bridge Soft IP -
> > GPEX 4.0, a PCIe gen4 IP. This IP has upto 8
> > outbound and inbound windows for the address translation.
> >
> > Signed-off-by: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
> > Cc: Bjorn Helgaas <bhelgaas@google.com>
> > Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > Cc: Marc Zyngier <marc.zyngier@arm.com>
> > Cc: linux-pci@vger.kernel.org
> > Cc: devicetree@vger.kernel.org
> > ---
> >  drivers/pci/host/Kconfig         |   8 +
> >  drivers/pci/host/Makefile        |   1 +
> >  drivers/pci/host/pcie-mobiveil.c | 653 +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 662 insertions(+)
> >  create mode 100644 drivers/pci/host/pcie-mobiveil.c
> >
> > diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> > index 38d1298..09bf1d9 100644
> > --- a/drivers/pci/host/Kconfig
> > +++ b/drivers/pci/host/Kconfig
> > @@ -27,6 +27,14 @@ config PCIE_XILINX_NWL
> >        or End Point. The current option selection will only
> >        support root port enabling.
> >
> > +config PCIE_MOBIVEIL
> > +        bool "Mobiveil AXI PCIe host bridge support"
> > +        depends on ARCH_ZYNQMP || COMPILE_TEST
> > +        depends on PCI_MSI_IRQ_DOMAIN
>
> There is no PCI_MSI_IRQ_DOMAIN dependency in this patch.
>
> > +        help
> > +          Say 'Y' here if you want kernel to support the Mobiveil AXI PCIe
> > +          Host Bridge driver.
> > +
> >  config PCI_FTPCI100
> >       bool "Faraday Technology FTPCI100 PCI controller"
> >       depends on OF
> > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> > index 34ec1d8..d745d68 100644
> > --- a/drivers/pci/host/Makefile
> > +++ b/drivers/pci/host/Makefile
> > @@ -23,6 +23,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
> >  obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
> >  obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
> >  obj-$(CONFIG_VMD) += vmd.o
> > +obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
> >
> >  # The following drivers are for devices that use the generic ACPI
> >  # pci_root.c driver but don't support standard ECAM config access.
> > diff --git a/drivers/pci/host/pcie-mobiveil.c b/drivers/pci/host/pcie-mobiveil.c
> > new file mode 100644
> > index 0000000..8611aaa
> > --- /dev/null
> > +++ b/drivers/pci/host/pcie-mobiveil.c
> > @@ -0,0 +1,653 @@
> > +/*
> > + * PCIe host controller driver for Mobiveil PCIe Host controller
> > + *
> > + * SPDX-License-Identifier: GPL-2.0
> > + * Copyright (c) 2017 Mobiveil Inc.
> > + * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/init.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/of_pci.h>
> > +#include <linux/pci.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +
> > +/* register offsets and bit positions */
> > +
> > +/*
> > + * translation tables are grouped into windows, each window registers are
> > + * grouped into blocks of 4 or 16 registers each
> > + */
> > +#define PAB_REG_BLOCK_SIZE_4 4
> > +#define PAB_REG_BLOCK_SIZE_16        16
>
> What I wanted to say is that you can tag the block with a name
> instead of using a number.
>
> I can say that block size 4 is only for PAB_EXT_*, you can tag
> it as such.
>
> eg PAB_EXT_REG_BLOCK_SIZE

Ok, I'll rename them as below, and use them accordingly.

#define PAB_REG_BLOCK_SIZE 16
#define PAB_EXT_REG_BLOCK_SIZE 4

>
>
> > +#define PAB_REG_ADDR_4(offset, win) (offset + (win * PAB_REG_BLOCK_SIZE_4))
> > +#define PAB_REG_ADDR_16(offset, win) (offset + (win * PAB_REG_BLOCK_SIZE_16))
> > +
> > +#define LTSSM_STATUS         0x0404
> > +#define  LTSSM_STATUS_L0_MASK        0x3f
> > +#define  LTSSM_STATUS_L0     0x2d
> > +
> > +#define PAB_CTRL             0x0808
> > +#define  AMBA_PIO_ENABLE_SHIFT       0
> > +#define  PEX_PIO_ENABLE_SHIFT        1
> > +#define  PAGE_SEL_SHIFT              13
> > +#define  PAGE_SEL_MASK               0x3f
> > +#define  PAGE_LO_MASK                0x3ff
> > +#define  PAGE_SEL_EN         0xc00
> > +#define  PAGE_SEL_OFFSET_SHIFT       10
> > +
> > +#define PAB_AXI_PIO_CTRL     0x0840
> > +#define  APIO_EN_MASK                0xf
> > +
> > +#define PAB_PEX_PIO_CTRL     0x08c0
> > +#define  PIO_ENABLE_SHIFT    0
> > +
> > +#define PAB_INTP_AMBA_MISC_ENB       0x0b0c
> > +#define PAB_INTP_AMBA_MISC_STAT      0x0b1c
> > +#define  PAB_INTP_INTX_MASK  0x1e0
> > +
> > +#define PAB_AXI_AMAP_CTRL(win)       PAB_REG_ADDR_16(0x0ba0, win)
> > +#define  WIN_ENABLE_SHIFT    0
> > +#define  WIN_TYPE_SHIFT              1
> > +#define  WIN_SIZE_SHIFT              10
> > +
> > +#define PAB_EXT_AXI_AMAP_SIZE(win)   PAB_REG_ADDR_4(0xbaf0, win)
> > +
> > +#define PAB_AXI_AMAP_AXI_WIN(win)    PAB_REG_ADDR_16(0x0ba4, win)
> > +#define  AXI_WINDOW_BASE_SHIFT               2
> > +
> > +#define PAB_AXI_AMAP_PEX_WIN_L(win)  PAB_REG_ADDR_16(0x0ba8, win)
> > +#define  PAB_BUS_SHIFT                       24
> > +#define  PAB_DEVICE_SHIFT            19
> > +#define  PAB_FUNCTION_SHIFT          16
> > +
> > +#define PAB_AXI_AMAP_PEX_WIN_H(win)  PAB_REG_ADDR_16(0x0bac, win)
> > +#define PAB_INTP_AXI_PIO_CLASS               0x474
>
> This one is a bit odd here, move it up in the file.
>
> > +#define PAB_PEX_AMAP_CTRL(win)               PAB_REG_ADDR_16(0x4ba0, win)
> > +#define  AMAP_CTRL_EN_SHIFT          0
> > +#define  AMAP_CTRL_TYPE_SHIFT                1
> > +
> > +#define PAB_EXT_PEX_AMAP_SIZEN(win)  PAB_REG_ADDR_4(0xbef0, win)
> > +#define PAB_PEX_AMAP_AXI_WIN(win)    PAB_REG_ADDR_16(0x4ba4, win)
> > +#define PAB_PEX_AMAP_PEX_WIN_L(win)  PAB_REG_ADDR_16(0x4ba8, win)
> > +#define PAB_PEX_AMAP_PEX_WIN_H(win)  PAB_REG_ADDR_16(0x4bac, win)
>
> Here you have three base offsets:
>
> 0xb00
> 0x4000
> 0xb000
>
> you can create a macro for each of them, according to what they
> represent and then add an offset into each.
>
> > +
> > +/* supported number of interrupts */
> > +#define PCI_NUM_INTX         4
>
> It is already defined in linux/pci.h
>
> > +#define PAB_INTA_POS         5
> > +
> > +/* outbound and inbound window definitions */
> > +#define WIN_NUM_0            0
> > +#define WIN_NUM_1            1
> > +#define CFG_WINDOW_TYPE              0
> > +#define IO_WINDOW_TYPE               1
> > +#define MEM_WINDOW_TYPE              2
> > +#define IB_WIN_SIZE          (256 * 1024 * 1024)
> > +#define MAX_PIO_WINDOWS              8
> > +
> > +/* Parameters for the waiting for link up routine */
> > +#define LINK_WAIT_MAX_RETRIES        10
> > +#define LINK_WAIT_MIN                90000
> > +#define LINK_WAIT_MAX                100000
> > +
> > +#ifndef UINT64_MAX
> > +#define UINT64_MAX           (u64)(~((u64)0))
> > +#endif
> > +
> > +struct mobiveil_pcie {
> > +     struct platform_device *pdev;
> > +     struct list_head resources;
> > +     void __iomem *config_axi_slave_base;    /* endpoint config base */
> > +     void __iomem *csr_axi_slave_base;       /* root port config base */
> > +     struct irq_domain *intx_domain;
> > +     int irq;
> > +     int apio_wins;
> > +     int ppio_wins;
> > +     int ob_wins_configured; /*  configured outbound windows */
> > +     int ib_wins_configured; /*  configured inbound windows */
> > +     struct resource *ob_io_res;
> > +     char root_bus_nr;
> > +};
> > +
> > +static inline void csr_writel(struct mobiveil_pcie *pcie, const u32 value,
> > +             const u32 reg)
> > +{
> > +     writel_relaxed(value, pcie->csr_axi_slave_base + reg);
> > +}
> > +
> > +static inline u32 csr_readl(struct mobiveil_pcie *pcie,      const u32 reg)
> > +{
> > +     return readl_relaxed(pcie->csr_axi_slave_base + reg);
> > +}
> > +
> > +static bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie)
> > +{
> > +     return (csr_readl(pcie, LTSSM_STATUS) &
> > +             LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0;
> > +}
>
> https://marc.info/?l=linux-pci&m=151329230814614&w=2
>
> Bjorn would like to remove it.

I do not have a solid explanation for this, as I followed the earlier
driver framework. I'll remove it for now.
>
>
> > +static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
> > +{
> > +     struct mobiveil_pcie *pcie = bus->sysdata;
> > +
> > +     /* Check if link is up when trying to access downstream ports */
> > +     if (bus->number != pcie->root_bus_nr)
> > +             if (!mobiveil_pcie_link_up(pcie))
> > +                     return false;
> > +
> > +     /* Only one device down on each root port */
> > +     if ((bus->number == pcie->root_bus_nr) && (devfn > 0))
> > +             return false;
> > +
> > +     /*
> > +      * Do not read more than one device on the bus directly
> > +      * attached to RC
> > +      */
> > +     if ((bus->primary == pcie->root_bus_nr) && (devfn > 0))
> > +             return false;
> > +
> > +     return true;
> > +}
> > +
> > +/*
> > + * mobiveil_pcie_map_bus - routine to get the configuration base of either
> > + * root port or endpoint
> > + */
> > +static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus,
> > +                                     unsigned int devfn, int where)
> > +{
> > +     struct mobiveil_pcie *pcie = bus->sysdata;
> > +
> > +     if (!mobiveil_pcie_valid_device(bus, devfn))
> > +             return NULL;
> > +
> > +     if (bus->number == pcie->root_bus_nr) {
> > +             /* RC config access */
> > +             return pcie->csr_axi_slave_base + where;
> > +     } else {
> > +             /*
> > +              * EP config access (in Config/APIO space)
> > +              * Program PEX Address base (31..16 bits) with appropriate value
> > +              * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register.
> > +              * Relies on pci_lock serialization
> > +              */
> > +             csr_writel(pcie,
> > +                     bus->number << PAB_BUS_SHIFT |
> > +                     PCI_SLOT(devfn) << PAB_DEVICE_SHIFT |
> > +                     PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT,
> > +                     PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0));
> > +             return pcie->config_axi_slave_base + where;
> > +     }
> > +}
> > +
> > +static struct pci_ops mobiveil_pcie_ops = {
> > +     .map_bus = mobiveil_pcie_map_bus,
> > +     .read = pci_generic_config_read,
> > +     .write = pci_generic_config_write,
> > +};
> > +
> > +static void mobiveil_pcie_isr(struct irq_desc *desc)
> > +{
> > +     struct irq_chip *chip = irq_desc_get_chip(desc);
> > +     struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc);
> > +     struct device *dev = &pcie->pdev->dev;
> > +     u32 intr_status;
> > +     unsigned long shifted_status;
>
> Why not u32 ?
>
for_each_set_bit() api warns about the variable not being unsigned
long. So used the same to take out the warning.


> > +     u32 bit, virq;
> > +     u32 val, mask;
> > +
> > +     chained_irq_enter(chip, desc);
> > +
> > +     /* read INTx status */
> > +     val = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT);
> > +     mask = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
> > +     intr_status = val & mask;
> > +
> > +     /*
> Handle INTx */
> > +     if (intr_status & PAB_INTP_INTX_MASK) {
> > +             shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT);
>
> Why can't you reuse val to read the status and create shifted_status
> from it ?
>
> eg
>
> shifted_status = val << PAB_INTA_POS;
> csr_writel(pcie, shifted_status, PAB_INTP_AMBA_MISC_STAT);
>
> Just a hint to make the loop more readable. BTW, I do not think that
> writing shifted_status into that register is OK, see below.
>
> > +             do {
> > +                     for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) {
> > +
> > +                             /* clear interrupts */
> > +                             csr_writel(pcie, shifted_status <<
> PAB_INTA_POS,
> > +
> PAB_INTP_AMBA_MISC_STAT);
>
> Should not you clear just the interrupt you are about to handle ?

PAB_INTP_AMBA_MISC_STAT register has INTA, INTB, INTC, INTD status
bits at positions 5,6,7 and 8,
they are RW1C bits, so writing 1 to this bit clears the status.
So here the status read was written back to it to clear.
>
>
> > +                             virq = irq_find_mapping(pcie->intx_domain,
> > +                                     bit + 1);
> > +                             if (virq)
> > +
> generic_handle_irq(virq);
> > +                             else
> > +                                     dev_err_ratelimited(dev,
> > +                                             "unexpected IRQ, INT%d\n",
> > +                                             bit);
> > +
> > +                     }
> > +
> > +                     shifted_status = csr_readl(pcie,
> > +                                             PAB_INTP_AMBA_MISC_STAT);
> > +
> > +             } while ((shifted_status >>  PAB_INTA_POS) != 0);
>
> I do not understand this. Can you explain to me how the
> register at:
>
> PAB_INTP_AMBA_MISC_STAT
>
> works ?
>
just repeating what explained before, to ease the readablility.
PAB_INTP_AMBA_MISC_STAT register has INTA, INTB, INTC, INTD status
bits at positions 5,6,7 and 8,
they are RW1C bits, so writing 1 to this bit clears the status.

>
> A patch for mediatek has been posted:
>
> https://patchwork.ozlabs.org/patch/851181/
>
> It looks like this loop may suffer from the same issue, so please do
> have a look.
>
I will clear the the interrupt after

its hadled by generic_handle_irq()
.
right ?
>
> On top of that, most of the operations you are carrying out here
> can be done automatically by making them part of the struct irq_chip
> methods (ie acking IRQs, masking IRQs, etc, see below).
>
Any side effects of keeping this code as is ?

>
> > +     }
> > +
> > +     csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT);
>
> > +     chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie)
> > +{
> > +     struct device *dev = &pcie->pdev->dev;
> > +     struct platform_device *pdev = pcie->pdev;
> > +     struct device_node *node = dev->of_node;
> > +     struct resource *res;
> > +     const char *type;
> > +
> > +     type = of_get_property(node, "device_type", NULL);
> > +     if (!type || strcmp(type, "pci")) {
> > +             dev_err(dev, "invalid \"device_type\" %s\n", type);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* map config resource */
> > +     res = platform_get_resource_byname(pdev,
> > +             IORESOURCE_MEM, "config_axi_slave");
> > +     pcie->config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
> > +     if (IS_ERR(pcie->config_axi_slave_base))
> > +             return PTR_ERR(pcie->config_axi_slave_base);
> > +     pcie->ob_io_res = res;
> > +
> > +     /* map csr resource */
> > +     res = platform_get_resource_byname(pdev,
> > +             IORESOURCE_MEM, "csr_axi_slave");
> > +     pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
> > +     if (IS_ERR(pcie->csr_axi_slave_base))
> > +             return PTR_ERR(pcie->csr_axi_slave_base);
> > +
> > +     /* read the number of windows requested */
> > +     if (!pcie->apio_wins &&
> > +             of_property_read_u32(node, "apio-wins", &pcie->apio_wins)) {
> > +             pcie->apio_wins = MAX_PIO_WINDOWS;
> > +     }
> > +
> > +     if (!pcie->ppio_wins &&
>
> What I wanted to say in my previous review is that apio_wins and
> ppio_wins are zero here why would they be any different and why
> do you need to check that.

Understood. I will remove this redundant check.

>
>
> > +             of_property_read_u32(node, "ppio-wins", &pcie->ppio_wins)) {
> > +             pcie->ppio_wins = MAX_PIO_WINDOWS;
> > +     }
> > +
> > +     pcie->irq = platform_get_irq(pdev, 0);
> > +     if (pcie->irq <= 0) {
> > +             dev_err(dev, "failed to map IRQ: %d\n", pcie->irq);
> > +             return -ENODEV;
> > +     }
> > +
> > +     irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie);
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * select_paged_register - routine to access paged register of root complex
> > + *
> > + * registers of RC are paged, for this scheme to work
> > + * extracted higher 6 bits of the offset will be written to pg_sel
> > + * field of PAB_CTRL register and rest of the lower 10 bits enabled with
> > + * PAGE_SEL_EN are used as offset of the register.
> > + *
> > + */
> > +static void select_paged_register(struct mobiveil_pcie *pcie, u32 offset)
> > +{
> > +     int pab_ctrl_dw, pg_sel;
> > +
> > +     /* clear pg_sel field */
> > +     pab_ctrl_dw = csr_readl(pcie, PAB_CTRL);
> > +     pab_ctrl_dw = (pab_ctrl_dw & ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT));
> > +
> > +     /* set pg_sel field */
> > +     pg_sel = (offset >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK;
> > +     pab_ctrl_dw |= ((pg_sel << PAGE_SEL_SHIFT));
> > +     csr_writel(pcie, pab_ctrl_dw, PAB_CTRL);
> > +}
> > +
> > +static void write_paged_register(struct mobiveil_pcie *pcie,
> > +             u32 val, u32 offset)
> > +{
> > +     u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN;
> > +
> > +     select_paged_register(pcie, offset);
> > +     csr_writel(pcie, val, off);
> > +}
> > +
> > +static u32 read_paged_register(struct mobiveil_pcie *pcie, u32 offset)
> > +{
> > +     u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN;
> > +
> > +     select_paged_register(pcie, offset);
> > +     return csr_readl(pcie, off);
> > +}
> > +
> > +static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num,
> > +             int pci_addr, int type, int size_kb)
> > +{
> > +     int pio_ctrl_val;
> > +     int amap_ctrl_dw;
> > +     u64 size64 = (UINT64_MAX << (WIN_SIZE_SHIFT + ilog2(size_kb)));
> > +
> > +     if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) {
> > +             dev_err(&pcie->pdev->dev,
> > +                     "ERROR: max inbound windows reached !\n");
> > +             return;
> > +     }
> > +
> > +     pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
> > +     csr_writel(pcie,
> > +             pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), PAB_PEX_PIO_CTRL);
> > +     amap_ctrl_dw =  read_paged_register(pcie, PAB_PEX_AMAP_CTRL(win_num));
> > +     amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT));
> > +     amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT));
> > +
> > +     write_paged_register(pcie, amap_ctrl_dw | lower_32_bits(size64),
> > +                             PAB_PEX_AMAP_CTRL(win_num));
> > +
> > +     write_paged_register(pcie, upper_32_bits(size64),
> > +                             PAB_EXT_PEX_AMAP_SIZEN(win_num));
> > +
> > +     write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num));
> > +     write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num));
> > +     write_paged_register(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num));
> > +}
> > +
> > +static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num,
> > +             u64 cpu_addr, u64 pci_addr, int config_io_bit,
> > +             int size_kb)
> > +{
> > +
> > +     u32 value, type;
> > +     u64
> size64 = (UINT64_MAX << (WIN_SIZE_SHIFT +
> ilog2(size_kb)));
>
> Can you explain to me the sizing mechanism please ? It is just to make
> sure I got it right.

this register follows the PCI BAR sizing convention.

size64 is actually ~(size-1),
Looks like we can just do that instead of jugglery of bit shifting and ilog2().
>
>
> > +     if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) {
> > +             dev_err(&pcie->pdev->dev,
> > +                     "ERROR: max outbound windows reached !\n");
> > +             return;
> > +     }
> > +
> > +     /*
> > +      * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit
> > +      * to 4 KB in PAB_AXI_AMAP_CTRL register
> > +      */
> > +     type = config_io_bit;
> > +     value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> > +     csr_writel(pcie,
> > +                     1 << WIN_ENABLE_SHIFT |
> > +                     type << WIN_TYPE_SHIFT |
> > +                     lower_32_bits(size64),
> > +                     PAB_AXI_AMAP_CTRL(win_num));
> > +
> > +     write_paged_register(pcie, upper_32_bits(size64),
> > +                             PAB_EXT_AXI_AMAP_SIZE(win_num));
> > +
> > +     /*
> > +      *
> program
> AXI window base with appropriate value in
> > +      * PAB_AXI_AMAP_AXI_WIN0 register
> > +      */
> > +     value = csr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(win_num));
> > +     csr_writel(pcie,
> > +                     cpu_addr >> AXI_WINDOW_BASE_SHIFT <<
> > +                     AXI_WINDOW_BASE_SHIFT, PAB_AXI_AMAP_AXI_WIN(win_num));
> > +
> > +     value = csr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(win_num));
>
> What's "value" used for in this function ?

My bad, it was used in the previous versions and not cleaned up. I will now.
>
>
>
> > +     csr_writel(pcie, lower_32_bits(pci_addr),
> > +                     PAB_AXI_AMAP_PEX_WIN_L(win_num));
> > +     csr_writel(pcie, upper_32_bits(pci_addr),
> > +                     PAB_AXI_AMAP_PEX_WIN_H(win_num));
> > +
> > +     pcie->ob_wins_configured++;
> > +}
> > +
> > +static int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> > +{
> > +     int retries;
> > +
> > +     /* check if the link is up or not */
> > +     for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> > +             if (mobiveil_pcie_link_up(pcie))
> > +                     return 0;
> > +
> > +             usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
> > +     }
> > +     dev_err(&pcie->pdev->dev, "link never came up\n");
> > +     return -ETIMEDOUT;
> > +}
> > +
> > +static int
> mobiveil_host_init(struct mobiveil_pcie *pcie)
> > +{
> > +     u32 value;
> > +     int type = 0;
>
> You reinitialize it later and it should not be an int but an unsigned
> type.
>
> > +     u32 pab_ctrl;
> > +     int err;
> > +     struct resource_entry *win, *tmp;
> > +
> > +     /* setup the PCIe slot link power*/
>
> Comment is useless, remove it.
>
> > +     err = mobiveil_bringup_link(pcie);
> > +     if (err) {
> > +             dev_info(&pcie->pdev->dev, "link bring-up failed\n");
> > +             return err;
> > +     }
> > +
> > +     /*
> > +      * program Bus Master Enable Bit in Command Register in PAB Config
> > +      * Space
> > +      */
> > +     value = csr_readl(pcie, PCI_COMMAND);
> > +     csr_writel(pcie,
> > +             value |
>
> Why is this on a separate line ?
>
> > +             PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER,
> > +             PCI_COMMAND);
> > +
> > +     /*
> > +      * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL
> > +      * register
> > +      */
> > +     pab_ctrl = csr_readl(pcie, PAB_CTRL);
> > +     csr_writel(pcie, pab_ctrl | (1 << AMBA_PIO_ENABLE_SHIFT) |
> > +             (1 << PEX_PIO_ENABLE_SHIFT),
> > +             PAB_CTRL);
> > +
> > +     csr_writel(pcie, PAB_INTP_INTX_MASK, PAB_INTP_AMBA_MISC_ENB);
> > +
> > +     /* program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in
> > +      * PAB_AXI_PIO_CTRL Register
> > +      */
> > +     value = csr_readl(pcie, PAB_AXI_PIO_CTRL);
> > +     csr_writel(pcie, value | APIO_EN_MASK, PAB_AXI_PIO_CTRL);
> > +
> > +     /*
> > +      * we'll program one outbound window for config reads and
> > +      * another default inbound window for all the upstream traffic
> > +      * rest of the outbound windows will be configured according to
> > +      * the "ranges" field defined in device tree
> > +      */
> > +
> > +     /* config outbound translation window */
> > +     program_ob_windows(pcie, pcie->ob_wins_configured,
> > +                     pcie->ob_io_res->start, 0, CFG_WINDOW_TYPE,
> > +                     resource_size(pcie->ob_io_res)/1024);
> > +
> > +     /* memory inbound  translation window */
> > +     program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE);
> > +
> > +     /* Get the I/O and memory ranges from DT */
> > +     resource_list_for_each_entry_safe(win, tmp, &pcie->resources) {
> > +             type = 0;
> > +             if (resource_type(win->res) == IORESOURCE_MEM)
> > +                     type = MEM_WINDOW_TYPE;
> > +             if (resource_type(win->res) == IORESOURCE_IO)
> > +                     type = IO_WINDOW_TYPE;
> > +             if (type) {
> > +                     /* configure outbound translation window */
> > +                     program_ob_windows(pcie, pcie->ob_wins_configured,
> > +                             win->res->start, 0, type,
> > +                             resource_size(win->res)/1024);
> > +             }
> > +     }
> > +
> > +     return err;
> > +}
> > +
> > +/* routine to setup the INTx related data */
> > +static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> > +             irq_hw_number_t hwirq)
> > +{
> > +     irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
>
> IIUC, the host bridge acts as an INTX/MSI controller/multiplexer.
>
> So, instead of relying on
> dummy_irq_chip, you should create your own
> irq_chip with the methods that you require (eg irq_ack(), irq_mask(),
> irq_compose_msi_msg()) and use that as bottom irq_chip for both
> INTX and MSI domains.
>
> That's why I asked you to have a look at pcie-tango.c (except that there
> INTX aren't supported but the basic idea does not change) and implement
> IRQ domains handling like that same driver.
>
are there any disadvantages of keeping dummy_irq_chip, as I see quite
a few host bridge

drivers including
otehr two major soft IP drivers from altera and xilinx with similar
MSI implementaion.
>
> > +     irq_set_chip_data(irq, domain->host_data);
> > +     return 0;
> > +}
> > +
> > +/* INTx domain opeartions structure */
>
> s/opeartions/operations
>
> > +static const struct irq_domain_ops intx_domain_ops = {
> > +     .map = mobiveil_pcie_intx_map,
> > +};
> > +
> > +static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
> > +{
> > +     struct device *dev = &pcie->pdev->dev;
> > +     struct device_node *node = dev->of_node;
> > +     int ret;
>
> ret is unused
>
> > +     /* setup INTx */
> > +     pcie->intx_domain = irq_domain_add_linear(node,
> > +                             PCI_NUM_INTX + 1, &intx_domain_ops, pcie);
>
> You should use PCI_NUM_INTX and add pci_irqd_intx_xlate() as the
> domain ops .xlate.
>
> > +     if (!pcie->intx_domain) {
> > +             dev_err(dev, "Failed to get a INTx IRQ domain\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int mobiveil_pcie_probe(struct platform_device *pdev)
> > +{
> > +     struct mobiveil_pcie *pcie;
> > +     struct pci_bus *bus;
> > +     struct pci_bus *child;
> > +     struct pci_host_bridge *bridge;
> > +     struct device *dev = &pdev->dev;
> > +     struct device_node *node = dev->of_node;
> > +     resource_size_t iobase;
> > +     int ret;
> > +
> > +     /* allocate the PCIe port */
> > +     bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
> > +     if (!bridge)
> > +             return -ENODEV;
> > +
> > +     pcie = pci_host_bridge_priv(bridge);
> > +     if (!pcie)
> > +             return -ENOMEM;
> > +
> > +     pcie->pdev = pdev;
> > +
> > +     /* parse the device tree */
>
> Remove this comment, it does not add anything.
>
> > +     ret = mobiveil_pcie_parse_dt(pcie);
> > +     if (ret) {
> > +             dev_err(dev, "Parsing DT failed, ret: %x\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     INIT_LIST_HEAD(&pcie->resources);
> > +
> > +     /* parse the host bridge base addresses from the device tree file */
> > +     ret = of_pci_get_host_bridge_resources(node,
> > +                     0, 0xff, &pcie->resources, &iobase);
> > +     if (ret) {
> > +             dev_err(dev, "Getting bridge resources failed\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     /*
> > +      * configure all inbound and outbound windows and prepare the RC for
> > +      * config access
> > +      */
> > +     ret = mobiveil_host_init(pcie);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to initialize host\n");
> > +             goto error;
> > +     }
> > +
> > +     /* fixup for PCIe class register */
> > +     csr_writel(pcie, 0x060402ab, PAB_INTP_AXI_PIO_CLASS);
> > +
> > +     /* initialize the IRQ domains */
> > +     ret = mobiveil_pcie_init_irq_domain(pcie);
> > +     if (ret) {
> > +             dev_err(dev, "Failed creating IRQ Domain\n");
> > +             goto error;
> > +     }
> > +
> > +
>
> Two empty lines, should be one.
>
> > +     ret = devm_request_pci_bus_resources(dev, &pcie->resources);
> > +     if (ret)
> > +             goto error;
> > +
> > +     /* Initialize bridge */
> > +     list_splice_init(&pcie->resources, &bridge->windows);
> > +     bridge->dev.parent = dev;
> > +     bridge->sysdata = pcie;
> > +     bridge->busnr = pcie->root_bus_nr;
> > +     bridge->ops = &mobiveil_pcie_ops;
> > +     bridge->map_irq = of_irq_parse_and_map_pci;
> > +     bridge->swizzle_irq = pci_common_swizzle;
> > +
> > +     /* setup the kernel resources for the newly added PCIe root bus */
> > +     ret = pci_scan_root_bus_bridge(bridge);
> > +     if (ret)
> > +             goto error;
> > +
> > +     bus = bridge->bus;
> > +
> > +     pci_assign_unassigned_bus_resources(bus);
> > +     list_for_each_entry(child, &bus->children, node)
> > +             pcie_bus_configure_settings(child);
> > +     pci_bus_add_devices(bus);
> > +
> > +     platform_set_drvdata(pdev, pcie);
>
> Is this really needed ?
>
> Thanks,
> Lorenzo
>
> > +
> > +     return 0;
> > +error:
> > +     pci_free_resource_list(&pcie->resources);
> > +     return ret;
> > +}
> > +
> > +static const struct of_device_id mobiveil_pcie_of_match[] = {
> > +     {.compatible = "mbvl,gpex40-pcie",},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, mobiveil_pcie_of_match);
> > +
> > +static struct platform_driver mobiveil_pcie_driver = {
> > +     .probe = mobiveil_pcie_probe,
> > +     .driver = {
> > +                     .name = "mobiveil-pcie",
> > +                     .of_match_table = mobiveil_pcie_of_match,
> > +                     .suppress_bind_attrs = true,
> > +             },
> > +};
> > +
> > +builtin_platform_driver(mobiveil_pcie_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("Mobiveil PCIe host controller driver");
> > +MODULE_AUTHOR("Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>");
> > --
> > 1.8.3.1
> >


Thanks,
Subrahmanya

^ permalink raw reply

* Re: [PATCH] ASoC: rcar: tidyup simple-card example for CPU node
From: Geert Uytterhoeven @ 2017-12-22  8:02 UTC (permalink / raw)
  To: Kuninori Morimoto
  Cc: Mark Rutland, devicetree, Linux-ALSA, Geert Uytterhoeven,
	Mark Brown, Rob Herring, Simon, Yokoyama
In-Reply-To: <873743nxuj.wl%kuninori.morimoto.gx@renesas.com>

CC DT

On Fri, Dec 22, 2017 at 6:29 AM, Kuninori Morimoto
<kuninori.morimoto.gx@renesas.com> wrote:
>
> From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
>
> commit a5702e1cb3c ("ASoC: rsnd: Drop unit-addresses without reg
> properties") modifies simple-card multi CPU nodes.
> But, naming of "cpu-x" breaks probing.
> Let's add reg = <x>; instead of renaming node.
>
> Reported-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
> CC: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
> ---
>  Documentation/devicetree/bindings/sound/renesas,rsnd.txt | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
> index 085bec3..51b7324 100644
> --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
> +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
> @@ -197,12 +197,17 @@ Ex)
>         [MEM] -> [SRC2] -> [CTU03] -+
>
>         sound {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
>                 compatible = "simple-scu-audio-card";
>                 ...
> -               simple-audio-card,cpu-0 {
> +               simple-audio-card,cpu@0 {
> +                       reg = <0>;
>                         sound-dai = <&rcar_sound 0>;
>                 };
> -               simple-audio-card,cpu-1 {
> +               simple-audio-card,cpu@1 {
> +                       reg = <1>;
>                         sound-dai = <&rcar_sound 1>;
>                 };
>                 simple-audio-card,codec {
> --
> 1.9.1

^ permalink raw reply


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