Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 4/4] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
From: Vivek Gautam @ 2016-11-22 12:02 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland, devicetree, linux-kernel
  Cc: srinivas.kandagatla, sboyd, linux-arm-msm, Vivek Gautam
In-Reply-To: <1479816163-5260-1-git-send-email-vivek.gautam@codeaurora.org>

Qualcomm SOCs have QMP phy controller that provides support
to a number of controller, viz. PCIe, UFS, and USB.
Add a new driver, based on generic phy framework, for this
phy controller.

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---

Changes since v1:
 - Fixed missing mutex_unlock() calls in error cases, reported by
   Julia Lawall.
 - Selecting CONFIG_RESET_CONTROLLER when this driver is enabled.
 - Added a boolean property to check if the phy has individual lane
   reset available.
 - Took care or EPROBE_DEFER, dev_vdbg() and other minor nits.
 - Removed references to non-lkml links from commit message.
 - Moved to use separate iomem resources for each lanes.
   Tx, Rx and PCS offsets per lane can now come from dt bindings.

Comments not addressed in this version:
 -- Have not addressed Kishon's comment to move phy init table stuff
    to generic phy calibration bindings.
    The qmp phy driver has 100 odd register writes (that do phy calibration
    at much finer level). Incorporating all such into generic phy calibration
    bindings does not seem possible.
    One way could have been to add dt-binding for the init table as
    'init-sequence'. But that is something strongly disliked by Rob and other
    dt maintainers.
 -- Have not addressed Kishon's comments to use phandle label based phy
    association to the consumer in order to get rid of index based PHY
    retrieval. This would require adding child nodes, something
    that we want to avoid.

 drivers/phy/Kconfig        |    9 +
 drivers/phy/Makefile       |    1 +
 drivers/phy/phy-qcom-qmp.c | 1141 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1151 insertions(+)
 create mode 100644 drivers/phy/phy-qcom-qmp.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index f1dcec1..8970d9e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -430,6 +430,15 @@ config PHY_STIH407_USB
 	  Enable this support to enable the picoPHY device used by USB2
 	  and USB3 controllers on STMicroelectronics STiH407 SoC families.
 
+config PHY_QCOM_QMP
+	tristate "Qualcomm QMP PHY Driver"
+	depends on OF && (ARCH_QCOM || COMPILE_TEST)
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the QMP PHY transceiver that is used
+	  with controllers such as PCIe, UFS, and USB on Qualcomm chips.
+
 config PHY_QCOM_QUSB2
 	tristate "Qualcomm QUSB2 PHY Driver"
 	depends on OF && (ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index dad1682..dbe7731 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
 obj-$(CONFIG_PHY_STIH407_USB)		+= phy-stih407-usb.o
 obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
+obj-$(CONFIG_PHY_QCOM_QMP) 	+= phy-qcom-qmp.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-14nm.o
diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
new file mode 100644
index 0000000..f85289e
--- /dev/null
+++ b/drivers/phy/phy-qcom-qmp.c
@@ -0,0 +1,1141 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/phy/phy.h>
+
+/* QMP PHY QSERDES COM registers */
+#define QSERDES_COM_BG_TIMER				0x00c
+#define QSERDES_COM_SSC_EN_CENTER			0x010
+#define QSERDES_COM_SSC_ADJ_PER1			0x014
+#define QSERDES_COM_SSC_ADJ_PER2			0x018
+#define QSERDES_COM_SSC_PER1				0x01c
+#define QSERDES_COM_SSC_PER2				0x020
+#define QSERDES_COM_SSC_STEP_SIZE1			0x024
+#define QSERDES_COM_SSC_STEP_SIZE2			0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
+#define QSERDES_COM_CLK_ENABLE1				0x038
+#define QSERDES_COM_SYS_CLK_CTRL			0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
+#define QSERDES_COM_PLL_IVCO				0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
+#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
+#define QSERDES_COM_BG_TRIM				0x070
+#define QSERDES_COM_CLK_EP_DIV				0x074
+#define QSERDES_COM_CP_CTRL_MODE0			0x078
+#define QSERDES_COM_CP_CTRL_MODE1			0x07c
+#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
+#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
+#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
+#define QSERDES_COM_RESETSM_CNTRL			0x0b4
+#define QSERDES_COM_RESTRIM_CTRL			0x0bc
+#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
+#define QSERDES_COM_LOCK_CMP_EN				0x0c8
+#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
+#define QSERDES_COM_DEC_START_MODE0			0x0d0
+#define QSERDES_COM_DEC_START_MODE1			0x0d4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
+#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
+#define QSERDES_COM_VCO_TUNE_CTRL			0x124
+#define QSERDES_COM_VCO_TUNE_MAP			0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
+#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
+#define QSERDES_COM_BG_CTRL				0x170
+#define QSERDES_COM_CLK_SELECT				0x174
+#define QSERDES_COM_HSCLK_SEL				0x178
+#define QSERDES_COM_CORECLK_DIV				0x184
+#define QSERDES_COM_CORE_CLK_EN				0x18c
+#define QSERDES_COM_C_READY_STATUS			0x190
+#define QSERDES_COM_CMN_CONFIG				0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
+#define QSERDES_COM_DEBUG_BUS0				0x1a0
+#define QSERDES_COM_DEBUG_BUS1				0x1a4
+#define QSERDES_COM_DEBUG_BUS2				0x1a8
+#define QSERDES_COM_DEBUG_BUS3				0x1ac
+#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
+#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
+
+/* QMP PHY TX registers */
+#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
+#define QSERDES_TX_DEBUG_BUS_SEL			0x064
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
+#define QSERDES_TX_LANE_MODE				0x094
+#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
+
+/* QMP PHY RX registers */
+#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
+#define QSERDES_RX_UCDR_SO_GAIN				0x01c
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
+#define QSERDES_RX_RX_TERM_BW				0x090
+#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
+#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
+#define QSERDES_RX_SIGDET_ENABLES			0x110
+#define QSERDES_RX_SIGDET_CNTRL				0x114
+#define QSERDES_RX_SIGDET_LVL				0x118
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
+#define QSERDES_RX_RX_BAND				0x120
+#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
+
+/* QMP PHY PCS registers */
+#define QPHY_SW_RESET					0x00
+#define QPHY_POWER_DOWN_CONTROL				0x04
+#define QPHY_START_CTRL					0x08
+#define QPHY_TXDEEMPH_M6DB_V0				0x24
+#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
+#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
+#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
+#define QPHY_POWER_STATE_CONFIG1			0x60
+#define QPHY_POWER_STATE_CONFIG2			0x64
+#define QPHY_POWER_STATE_CONFIG4			0x6c
+#define QPHY_LOCK_DETECT_CONFIG1			0x80
+#define QPHY_LOCK_DETECT_CONFIG2			0x84
+#define QPHY_LOCK_DETECT_CONFIG3			0x88
+#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
+#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
+
+/* PHY_SW_RESET bit */
+#define PHY_SW_RESET				BIT(0)
+/* PHY_POWER_DOWN_CONTROL */
+#define PHY_SW_PWRDN				BIT(0)
+#define PHY_REFCLK_DRV_DSBL			BIT(1)
+/* PHY_START_CONTROL bits */
+#define PHY_SERDES_START			BIT(0)
+#define PHY_PCS_START				BIT(1)
+#define PHY_PLL_READY_GATE_EN			BIT(3)
+/* PHY_PCS_STATUS bit */
+#define MASK_PHYSTATUS				BIT(6)
+/* PCS_READY_STATUS bit */
+#define MASK_COM_PCS_READY			BIT(0)
+
+#define REFCLK_STABILIZATION_DELAY_US_MIN	1000
+#define REFCLK_STABILIZATION_DELAY_US_MAX	1005
+#define PHY_READY_TIMEOUT_COUNT			10
+#define POWER_DOWN_DELAY_US_MIN			10
+#define POWER_DOWN_DELAY_US_MAX			11
+
+#define MAX_PROP_NAME		32
+
+struct qmp_phy_init_tbl {
+	unsigned int reg_offset;
+	unsigned int cfg_val;
+	/*
+	 * register part of layout ?
+	 * if yes, then reg_offset gives index in the reg-layout
+	 */
+	int in_layout;
+};
+#define QCOM_QMP_PHY_INIT_CFG(reg, val) \
+	{				\
+		.reg_offset = reg,	\
+		.cfg_val = val,		\
+	}
+#define QCOM_QMP_PHY_INIT_CFG_L(reg, val) \
+	{				  \
+		.reg_offset = reg,	  \
+		.cfg_val = val,		  \
+		.in_layout = 1,		  \
+	}
+
+/* set of registers with offsets different per-PHY */
+enum qphy_reg_layout {
+	/* Common block control registers */
+	QPHY_COM_SW_RESET,
+	QPHY_COM_POWER_DOWN_CONTROL,
+	QPHY_COM_START_CONTROL,
+	QPHY_COM_PCS_READY_STATUS,
+	/* PCS registers */
+	QPHY_PLL_LOCK_CHK_DLY_TIME,
+	QPHY_FLL_CNTRL1,
+	QPHY_FLL_CNTRL2,
+	QPHY_FLL_CNT_VAL_L,
+	QPHY_FLL_CNT_VAL_H_TOL,
+	QPHY_FLL_MAN_CODE,
+	QPHY_PCS_READY_STATUS,
+};
+
+unsigned int pciephy_regs_layout[] = {
+	[QPHY_COM_SW_RESET]		= 0x400,
+	[QPHY_COM_POWER_DOWN_CONTROL]	= 0x404,
+	[QPHY_COM_START_CONTROL]	= 0x408,
+	[QPHY_COM_PCS_READY_STATUS]	= 0x448,
+	[QPHY_PLL_LOCK_CHK_DLY_TIME]	= 0xa8,
+	[QPHY_FLL_CNTRL1]		= 0xc4,
+	[QPHY_FLL_CNTRL2]		= 0xc8,
+	[QPHY_FLL_CNT_VAL_L]		= 0xcc,
+	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xd0,
+	[QPHY_FLL_MAN_CODE]		= 0xd4,
+	[QPHY_PCS_READY_STATUS]		= 0x174,
+};
+
+unsigned int usb3phy_regs_layout[] = {
+	[QPHY_FLL_CNTRL1]		= 0xc0,
+	[QPHY_FLL_CNTRL2]		= 0xc4,
+	[QPHY_FLL_CNT_VAL_L]		= 0xc8,
+	[QPHY_FLL_CNT_VAL_H_TOL]	= 0xcc,
+	[QPHY_FLL_MAN_CODE]		= 0xd0,
+	[QPHY_PCS_READY_STATUS]		= 0x17c,
+};
+
+static struct qmp_phy_init_tbl pciephy_serdes_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
+};
+
+static struct qmp_phy_init_tbl pciephy_tx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static struct qmp_phy_init_tbl pciephy_rx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
+};
+
+static struct qmp_phy_init_tbl pciephy_pcs_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05),
+
+	QCOM_QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
+};
+
+static struct qmp_phy_init_tbl usb3phy_serdes_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04),
+	/* PLL and Loop filter settings */
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+	/* SSC settings */
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static struct qmp_phy_init_tbl usb3phy_tx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static struct qmp_phy_init_tbl usb3phy_rx_init_tbl[] = {
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18),
+	QCOM_QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+};
+
+static struct qmp_phy_init_tbl usb3phy_pcs_init_tbl[] = {
+	/* FLL settings */
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42),
+	QCOM_QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85),
+
+	/* Lock Det settings */
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47),
+	QCOM_QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08),
+};
+
+/**
+ * struct qmp_phy_init_cfg:- per-PHY init config.
+ */
+struct qmp_phy_init_cfg {
+	/* phy-type - PCIE/UFS/USB */
+	unsigned int type;
+	/* number of lanes provided by phy */
+	int nlanes;
+
+	/* Initialization sequence for PHY blocks - Serdes, tx, rx, pcs */
+	struct qmp_phy_init_tbl *phy_init_serdes_tbl;
+	int phy_init_serdes_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_tx_tbl;
+	int phy_init_tx_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_rx_tbl;
+	int phy_init_rx_tbl_sz;
+	struct qmp_phy_init_tbl *phy_init_pcs_tbl;
+	int phy_init_pcs_tbl_sz;
+
+	/* array of registers with different offsets */
+	unsigned int *regs;
+
+	unsigned int mask_start_ctrl;
+	unsigned int mask_pwr_dn_ctrl;
+	/* true, if PHY has a separate PHY_COM_CNTRL block */
+	bool has_phy_com_ctrl;
+	/* true, if PHY has a reset for individual lanes */
+	bool has_lane_rst;
+};
+
+/**
+ * struct qmp_phy_desc:- per-lane phy-descriptor.
+ *
+ * @phy: pointer to generic phy
+ * @tx: pointer to iomapped memory space for PHY's tx
+ * @rx: pointer to iomapped memory space for PHY's rx
+ * @pcs: pointer to iomapped memory space for PHY's pcs
+ * @pipe_clk: pointer to pipe lock
+ * @index: lane index
+ * @qphy: pointer to QMP phy to which this lane belongs
+ * @lane_rst: pointer to lane's reset controller
+ */
+struct qmp_phy_desc {
+	struct phy *phy;
+	void __iomem *tx;
+	void __iomem *rx;
+	void __iomem *pcs;
+	struct clk *pipe_clk;
+	unsigned int index;
+	struct qcom_qmp_phy *qphy;
+	struct reset_control *lane_rst;
+};
+
+/**
+ * struct qcom_qmp_phy:- structure holding QMP PHY attributes.
+ *
+ * @dev: pointer to device
+ * @serdes: pointer to iomapped memory space for phy's serdes
+ *
+ * @aux_clk: pointer to phy core clock
+ * @cfg_ahb_clk: pointer to AHB2PHY interface clock
+ * @ref_clk: pointer to reference clock
+ * @ref_clk_src: pointer to source to reference clock
+ *
+ * @vdda_phy: vdd supply to the phy core block
+ * @vdda_pll: 1.8V vdd supply to ref_clk block
+ * @vddp_ref_clk: vdd supply to specific ref_clk block (Optional)
+ *
+ * @phy_rst: Pointer to phy reset control
+ * @phycom_rst: Pointer to phy common reset control
+ * @phycfg_rst: Pointer to phy ahb cfg reset control (Optional)
+ *
+ * @cfg: pointer to init config for each phys
+ * @phys: array of pointer to per-lane phy descriptors
+ * @phy_mutex: mutex lock for PHY common block initialization
+ * @init_count: Phy common block initialization count
+ */
+struct qcom_qmp_phy {
+	struct device *dev;
+	void __iomem *serdes;
+
+	struct clk *aux_clk;
+	struct clk *cfg_ahb_clk;
+	struct clk *ref_clk;
+	struct clk *ref_clk_src;
+
+	struct regulator *vdda_phy;
+	struct regulator *vdda_pll;
+	struct regulator *vddp_ref_clk;
+
+	struct reset_control *phy_rst;
+	struct reset_control *phycom_rst;
+	struct reset_control *phycfg_rst;
+
+	const struct qmp_phy_init_cfg *cfg;
+	struct qmp_phy_desc **phys;
+
+	struct mutex phy_mutex;
+	int init_count;
+};
+
+static inline void qphy_setbits(void __iomem *reg, u32 val)
+{
+	u32 reg_val;
+
+	reg_val = readl_relaxed(reg);
+	reg_val |= val;
+	writel_relaxed(reg_val, reg);
+}
+
+static inline void qphy_clrbits(void __iomem *reg, u32 val)
+{
+	u32 reg_val;
+
+	reg_val = readl_relaxed(reg);
+	reg_val &= ~val;
+	writel_relaxed(reg_val, reg);
+}
+
+const struct qmp_phy_init_cfg msm8996_pciephy_init_cfg = {
+	.type			= PHY_TYPE_PCIE,
+	.nlanes			= 3,
+
+	.phy_init_serdes_tbl	= pciephy_serdes_init_tbl,
+	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(pciephy_serdes_init_tbl),
+	.phy_init_tx_tbl	= pciephy_tx_init_tbl,
+	.phy_init_tx_tbl_sz	= ARRAY_SIZE(pciephy_tx_init_tbl),
+	.phy_init_rx_tbl	= pciephy_rx_init_tbl,
+	.phy_init_rx_tbl_sz	= ARRAY_SIZE(pciephy_rx_init_tbl),
+	.phy_init_pcs_tbl	= pciephy_pcs_init_tbl,
+	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(pciephy_pcs_init_tbl),
+	.regs			= pciephy_regs_layout,
+	.mask_start_ctrl	= (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
+	.mask_pwr_dn_ctrl	= (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
+
+	.has_phy_com_ctrl	= true,
+	.has_lane_rst		= true,
+};
+
+const struct qmp_phy_init_cfg msm8996_usb3phy_init_cfg = {
+	.type			= PHY_TYPE_USB3,
+	.nlanes			= 1,
+
+	.phy_init_serdes_tbl	= usb3phy_serdes_init_tbl,
+	.phy_init_serdes_tbl_sz	= ARRAY_SIZE(usb3phy_serdes_init_tbl),
+	.phy_init_tx_tbl	= usb3phy_tx_init_tbl,
+	.phy_init_tx_tbl_sz	= ARRAY_SIZE(usb3phy_tx_init_tbl),
+	.phy_init_rx_tbl	= usb3phy_rx_init_tbl,
+	.phy_init_rx_tbl_sz	= ARRAY_SIZE(usb3phy_rx_init_tbl),
+	.phy_init_pcs_tbl	= usb3phy_pcs_init_tbl,
+	.phy_init_pcs_tbl_sz	= ARRAY_SIZE(usb3phy_pcs_init_tbl),
+	.regs			= usb3phy_regs_layout,
+	.mask_start_ctrl	= (PHY_SERDES_START | PHY_PCS_START),
+	.mask_pwr_dn_ctrl	= PHY_SW_PWRDN,
+};
+
+static void qcom_qmp_phy_configure(void __iomem *base,
+				unsigned int *regs_layout,
+				struct qmp_phy_init_tbl init_tbl[],
+				int init_tbl_sz)
+{
+	int i;
+
+	for (i = 0; i < init_tbl_sz; i++) {
+		if (init_tbl[i].in_layout)
+			writel_relaxed(init_tbl[i].cfg_val,
+				base + regs_layout[init_tbl[i].reg_offset]);
+		else
+			writel_relaxed(init_tbl[i].cfg_val,
+				base + init_tbl[i].reg_offset);
+	}
+
+	/* flush buffered writes */
+	mb();
+}
+
+static int qcom_qmp_phy_poweron(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	int ret;
+
+	dev_vdbg(&phy->dev, "Powering on QMP phy\n");
+
+	ret = regulator_enable(qphy->vdda_phy);
+	if (ret) {
+		dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regulator_enable(qphy->vdda_pll);
+	if (ret) {
+		dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
+				__func__, ret);
+		goto err_vdda_pll;
+	}
+
+	if (qphy->vddp_ref_clk) {
+		ret = regulator_enable(qphy->vddp_ref_clk);
+		if (ret) {
+			dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
+					__func__, ret);
+			goto err_vddp_refclk;
+		}
+	}
+
+	clk_prepare_enable(qphy->ref_clk_src);
+	clk_prepare_enable(qphy->ref_clk);
+	clk_prepare_enable(phydesc->pipe_clk);
+
+	return 0;
+
+err_vddp_refclk:
+	regulator_disable(qphy->vdda_pll);
+err_vdda_pll:
+	regulator_disable(qphy->vdda_phy);
+	return ret;
+}
+
+static int qcom_qmp_phy_poweroff(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+
+	clk_disable_unprepare(qphy->ref_clk_src);
+	clk_disable_unprepare(qphy->ref_clk);
+	clk_disable_unprepare(phydesc->pipe_clk);
+
+	if (qphy->vddp_ref_clk)
+		regulator_disable(qphy->vddp_ref_clk);
+
+	regulator_disable(qphy->vdda_pll);
+	regulator_disable(qphy->vdda_phy);
+
+	return 0;
+}
+
+static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
+				void __iomem *pcs_status, u32 mask)
+{
+	unsigned int init_timeout;
+
+	init_timeout = PHY_READY_TIMEOUT_COUNT;
+	do {
+		if (readl_relaxed(pcs_status) & mask)
+			break;
+
+		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
+				 REFCLK_STABILIZATION_DELAY_US_MAX);
+	} while (--init_timeout);
+
+	if (!init_timeout)
+		return -EBUSY;
+
+	return 0;
+}
+
+static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
+{
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *serdes = qphy->serdes;
+	int ret;
+
+	mutex_lock(&qphy->phy_mutex);
+	if (qphy->init_count++) {
+		mutex_unlock(&qphy->phy_mutex);
+		return 0;
+	}
+
+	ret = reset_control_deassert(qphy->phy_rst);
+	if (ret) {
+		dev_err(qphy->dev, "phy reset deassert failed\n");
+		goto err;
+	}
+
+	ret = reset_control_deassert(qphy->phycom_rst);
+	if (ret) {
+		dev_err(qphy->dev, "common reset deassert failed\n");
+		goto err_phycom_rst;
+	}
+
+	if (qphy->phycfg_rst) {
+		ret = reset_control_deassert(qphy->phycfg_rst);
+		if (ret) {
+			dev_err(qphy->dev, "common reset deassert failed\n");
+			goto err_phycfg_rst;
+		}
+	}
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_setbits(serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
+				PHY_SW_PWRDN);
+		/* Make sure that above write is completed */
+		mb();
+	}
+
+	/* Serdes configuration */
+	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
+				cfg->phy_init_serdes_tbl_sz);
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_clrbits(serdes + cfg->regs[QPHY_COM_SW_RESET],
+				PHY_SW_RESET);
+		qphy_setbits(serdes + cfg->regs[QPHY_COM_START_CONTROL],
+				PHY_SERDES_START | PHY_PCS_START);
+		/* Make sure that above write is completed */
+		mb();
+
+		ret = qcom_qmp_phy_is_ready(qphy, serdes +
+					cfg->regs[QPHY_COM_PCS_READY_STATUS],
+					MASK_COM_PCS_READY);
+		if (ret) {
+			dev_err(qphy->dev,
+				"common control block init timed-out\n");
+			goto err_phy_comctrl;
+		}
+	}
+
+	mutex_unlock(&qphy->phy_mutex);
+
+	return 0;
+
+err_phy_comctrl:
+	if (qphy->phycfg_rst)
+		reset_control_assert(qphy->phycfg_rst);
+err_phycfg_rst:
+	reset_control_assert(qphy->phycom_rst);
+err_phycom_rst:
+	reset_control_assert(qphy->phy_rst);
+err:
+	mutex_unlock(&qphy->phy_mutex);
+	return ret;
+}
+
+static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
+{
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *serdes = qphy->serdes;
+
+	mutex_lock(&qphy->phy_mutex);
+	if (--qphy->init_count) {
+		mutex_unlock(&qphy->phy_mutex);
+		return 0;
+	}
+
+	if (cfg->has_phy_com_ctrl) {
+		qphy_setbits(serdes + cfg->regs[QPHY_COM_START_CONTROL],
+				PHY_SERDES_START | PHY_PCS_START);
+		qphy_clrbits(serdes + cfg->regs[QPHY_COM_SW_RESET],
+				PHY_SW_RESET);
+		qphy_setbits(serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
+				PHY_SW_PWRDN);
+
+		/* Make sure that above writes are completed */
+		mb();
+	}
+
+	reset_control_assert(qphy->phy_rst);
+	reset_control_assert(qphy->phycom_rst);
+	if (qphy->phycfg_rst)
+		reset_control_assert(qphy->phycfg_rst);
+
+	mutex_unlock(&qphy->phy_mutex);
+
+	return 0;
+}
+
+/* PHY Initialization */
+static int qcom_qmp_phy_init(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+	void __iomem *tx = phydesc->tx;
+	void __iomem *rx = phydesc->rx;
+	void __iomem *pcs = phydesc->pcs;
+	int ret;
+
+	dev_vdbg(qphy->dev, "Initializing QMP phy\n");
+
+	/* enable interface clocks to program phy */
+	clk_prepare_enable(qphy->aux_clk);
+	clk_prepare_enable(qphy->cfg_ahb_clk);
+
+	ret = qcom_qmp_phy_com_init(qphy);
+	if (ret)
+		goto err;
+
+	if (phydesc->lane_rst) {
+		ret = reset_control_deassert(phydesc->lane_rst);
+		if (ret) {
+			dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
+					phydesc->index);
+			goto err_lane_rst;
+		}
+	}
+
+	/* Tx, Rx, and PCS configurations */
+	qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
+				cfg->phy_init_tx_tbl_sz);
+	qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
+				cfg->phy_init_rx_tbl_sz);
+	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
+				cfg->phy_init_pcs_tbl_sz);
+
+	/*
+	 * Pull out PHY from POWER DOWN state:
+	 * This is active low enable signal to power-down PHY.
+	 */
+	qphy_setbits(pcs + QPHY_POWER_DOWN_CONTROL, cfg->mask_pwr_dn_ctrl);
+	/* XXX: 10 us delay; given in PCIE phy programming guide only */
+	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
+
+	/* start SerDes and Phy-Coding-Sublayer */
+	qphy_setbits(pcs + QPHY_START_CTRL, cfg->mask_start_ctrl);
+
+	/* Pull PHY out of reset state */
+	qphy_clrbits(pcs + QPHY_SW_RESET, PHY_SW_RESET);
+	/* Make sure that above writes are completed */
+	mb();
+
+	ret = qcom_qmp_phy_is_ready(qphy, pcs +
+					cfg->regs[QPHY_PCS_READY_STATUS],
+					MASK_PHYSTATUS);
+	if (ret) {
+		dev_err(qphy->dev, "phy initialization timed-out\n");
+		goto err_pcs_ready;
+	}
+
+	return 0;
+
+err_pcs_ready:
+	if (phydesc->lane_rst)
+		reset_control_assert(phydesc->lane_rst);
+err_lane_rst:
+	qcom_qmp_phy_com_exit(qphy);
+err:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->aux_clk);
+	return ret;
+}
+
+static int qcom_qmp_phy_exit(struct phy *phy)
+{
+	struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
+	struct qcom_qmp_phy *qphy = phydesc->qphy;
+	const struct qmp_phy_init_cfg *cfg = qphy->cfg;
+
+	/* PHY reset */
+	qphy_setbits(phydesc->pcs + QPHY_SW_RESET, PHY_SW_RESET);
+
+	/* stop SerDes and Phy-Coding-Sublayer */
+	qphy_clrbits(phydesc->pcs + QPHY_START_CTRL, cfg->mask_start_ctrl);
+
+	/* Put PHY into POWER DOWN state: active low */
+	qphy_clrbits(phydesc->pcs + QPHY_POWER_DOWN_CONTROL,
+			cfg->mask_pwr_dn_ctrl);
+
+	/* Make sure that above writes are completed */
+	mb();
+
+	if (phydesc->lane_rst)
+		reset_control_assert(phydesc->lane_rst);
+
+	qcom_qmp_phy_com_exit(qphy);
+
+	clk_disable_unprepare(qphy->aux_clk);
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+
+	return 0;
+}
+
+
+static int qcom_qmp_phy_regulator_init(struct device *dev)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int ret;
+
+	qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
+	if (IS_ERR(qphy->vdda_phy)) {
+		ret = PTR_ERR(qphy->vdda_phy);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get vdda-phy, %d\n", ret);
+		return ret;
+	}
+
+	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
+	if (IS_ERR(qphy->vdda_pll)) {
+		ret = PTR_ERR(qphy->vdda_pll);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get vdda-pll, %d\n", ret);
+		return ret;
+	}
+
+	/* optional regulator */
+	qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
+	if (IS_ERR(qphy->vddp_ref_clk)) {
+		ret = PTR_ERR(qphy->vddp_ref_clk);
+		if (ret != -EPROBE_DEFER) {
+			dev_dbg(dev, "failed to get vddp-ref-clk, %d\n", ret);
+			qphy->vddp_ref_clk = NULL;
+		} else {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int qcom_qmp_phy_clk_init(struct device *dev)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int ret;
+
+	qphy->aux_clk = devm_clk_get(dev, "aux");
+	if (IS_ERR(qphy->aux_clk)) {
+		ret = PTR_ERR(qphy->aux_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get aux_clk\n");
+		return ret;
+	}
+
+	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
+	if (IS_ERR(qphy->cfg_ahb_clk)) {
+		ret = PTR_ERR(qphy->cfg_ahb_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get cfg_ahb_clk\n");
+		return ret;
+	}
+
+	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+	if (IS_ERR(qphy->ref_clk_src)) {
+		ret = PTR_ERR(qphy->ref_clk_src);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get ref_clk_src\n");
+		return ret;
+	}
+
+	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(qphy->ref_clk)) {
+		ret = PTR_ERR(qphy->ref_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get ref_clk\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct phy *qcom_qmp_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
+	int i;
+
+	if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
+		return ERR_PTR(-ENODEV);
+
+	for (i = 0; i < qphy->cfg->nlanes; i++) {
+		if (qphy->phys[i]->index == args->args[0])
+			break;
+	}
+
+	if (i == qphy->cfg->nlanes)
+		return ERR_PTR(-ENODEV);
+
+	return qphy->phys[i]->phy;
+}
+
+static const struct phy_ops qcom_qmp_phy_gen_ops = {
+	.init		= qcom_qmp_phy_init,
+	.exit		= qcom_qmp_phy_exit,
+	.power_on	= qcom_qmp_phy_poweron,
+	.power_off	= qcom_qmp_phy_poweroff,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
+	{
+		.compatible = "qcom,msm8996-qmp-pcie-phy",
+		.data = &msm8996_pciephy_init_cfg,
+	}, {
+		.compatible = "qcom,msm8996-qmp-usb3-phy",
+		.data = &msm8996_usb3phy_init_cfg,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
+
+static int qcom_qmp_phy_probe(struct platform_device *pdev)
+{
+	struct qcom_qmp_phy *qphy;
+	struct device *dev = &pdev->dev;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	const struct of_device_id *match;
+	void __iomem *base;
+	int ret = 0;
+	int id;
+
+	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+	if (!qphy)
+		return -ENOMEM;
+
+	qphy->dev = dev;
+	dev_set_drvdata(dev, qphy);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* per PHY serdes; usually located at base address */
+	qphy->serdes = base;
+
+	mutex_init(&qphy->phy_mutex);
+
+	/* Get the specific init parameters of QMP phy */
+	match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
+	qphy->cfg = match->data;
+
+	ret = qcom_qmp_phy_clk_init(dev);
+	if (ret) {
+		dev_err(dev, "clock init failed\n");
+		return ret;
+	}
+
+	ret = qcom_qmp_phy_regulator_init(dev);
+	if (ret) {
+		dev_err(dev, "regulator init failed\n");
+		return ret;
+	}
+
+	qphy->phy_rst = devm_reset_control_get(dev, "phy");
+	if (IS_ERR(qphy->phy_rst)) {
+		dev_err(dev, "failed to get phy core reset\n");
+		return PTR_ERR(qphy->phy_rst);
+	}
+
+	qphy->phycom_rst = devm_reset_control_get(dev, "common");
+	if (IS_ERR(qphy->phycom_rst)) {
+		dev_err(dev, "failed to get phy common reset\n");
+		return PTR_ERR(qphy->phycom_rst);
+	}
+
+	qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
+	if (IS_ERR(qphy->phycfg_rst)) {
+		dev_dbg(dev, "failed to get phy ahb cfg reset\n");
+		qphy->phycfg_rst = NULL;
+	}
+
+	qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
+					sizeof(*qphy->phys), GFP_KERNEL);
+	if (!qphy->phys)
+		return -ENOMEM;
+
+	for (id = 0; id < qphy->cfg->nlanes; id++) {
+		struct phy *generic_phy;
+		struct qmp_phy_desc *phy_desc;
+		char prop_name[MAX_PROP_NAME];
+		unsigned int lane_offsets[3];
+
+		/* mem resources from index 1 to N for N number of lanes */
+		res = platform_get_resource(pdev, IORESOURCE_MEM, id + 1);
+		base = devm_ioremap_resource(dev, res);
+		if (IS_ERR(base))
+			return PTR_ERR(base);
+
+		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+		if (!phy_desc)
+			return -ENOMEM;
+
+		/*
+		 * read offsets to Tx, Rx, and PCS blocks into a u32 array:
+		 *  ------------------------------------
+		 * | tx offset | rx offset | pcs offset |
+		 *  ------------------------------------
+		 */
+		ret = of_property_read_u32_array(dev->of_node, "lane-offsets",
+							   lane_offsets, 3);
+		if (ret) {
+			dev_err(dev,
+				"failed to get tx/rx/pcs offsets for lane%d\n",
+				id);
+				return ret;
+		}
+
+		phy_desc->tx = base + lane_offsets[0];
+		phy_desc->rx = base + lane_offsets[1];
+		phy_desc->pcs = base + lane_offsets[2];
+
+		/*
+		 * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE3
+		 * based phys, so they essentially have pipe clock. So,
+		 * we return error in case phy is USB3 or PIPE type.
+		 * Otherwise, we initialize pipe clock to NULL for
+		 * all phys that don't need this.
+		 */
+		memset(&prop_name, 0, sizeof(prop_name));
+		snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
+		phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
+		if (IS_ERR(phy_desc->pipe_clk)) {
+			if (qphy->cfg->type == PHY_TYPE_PCIE ||
+			    qphy->cfg->type == PHY_TYPE_USB3) {
+				ret = PTR_ERR(phy_desc->pipe_clk);
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev,
+					"failed to get lane%d pipe_clk\n", id);
+				return ret;
+			} else {
+				phy_desc->pipe_clk = NULL;
+			}
+		}
+
+		/* Get lane reset, if any */
+		if (qphy->cfg->has_lane_rst) {
+			memset(&prop_name, 0, sizeof(prop_name));
+			snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
+			phy_desc->lane_rst = devm_reset_control_get(dev,
+								    prop_name);
+			if (IS_ERR(phy_desc->lane_rst)) {
+				dev_err(dev, "failed to get lane%d reset\n",
+					id);
+				return PTR_ERR(phy_desc->lane_rst);
+			}
+		}
+
+		generic_phy = devm_phy_create(dev, NULL, &qcom_qmp_phy_gen_ops);
+		if (IS_ERR(generic_phy)) {
+			ret = PTR_ERR(generic_phy);
+			dev_err(dev, "failed to create qphy %d\n", ret);
+			return ret;
+		}
+
+		phy_desc->phy = generic_phy;
+		phy_desc->index = id;
+		phy_desc->qphy = qphy;
+		phy_set_drvdata(generic_phy, phy_desc);
+		qphy->phys[id] = phy_desc;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev, qcom_qmp_phy_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "failed to register qphy %d\n", ret);
+	}
+
+	return ret;
+}
+
+static struct platform_driver qcom_qmp_phy_driver = {
+	.probe		= qcom_qmp_phy_probe,
+	.driver = {
+		.name	= "qcom_qmp_phy",
+		.of_match_table = of_match_ptr(qcom_qmp_phy_of_match_table),
+	},
+};
+
+module_platform_driver(qcom_qmp_phy_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH v2 3/4] dt-bindings: phy: Add support for QMP phy
From: Vivek Gautam @ 2016-11-22 12:02 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland, devicetree, linux-kernel
  Cc: srinivas.kandagatla, sboyd, linux-arm-msm, Vivek Gautam
In-Reply-To: <1479816163-5260-1-git-send-email-vivek.gautam@codeaurora.org>

Qualcomm chipsets have QMP phy controller that provides
support to a number of controller, viz. PCIe, UFS, and USB.
Adding dt binding information for the same.

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Acked-by: Rob Herring <robh@kernel.org>
---

Changes since v1:
 - New patch, forked out of the original driver patch:
   "phy: qcom-qmp: new qmp phy driver for qcom-chipsets"
 - updated bindings to include mem resource as a list of
   offset - length pair for serdes block and for each lane.
 - added a new binding for 'lane-offsets' that contains offsets
   to tx, rx and pcs blocks from each lane base address.

 .../devicetree/bindings/phy/qcom-qmp-phy.txt       | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
new file mode 100644
index 0000000..ffb173b
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
@@ -0,0 +1,74 @@
+Qualcomm QMP PHY
+----------------
+
+QMP phy controller supports physical layer functionality for a number of
+controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
+
+Required properties:
+ - compatible: compatible list, contains:
+	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
+	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
+ - reg: list of offset and length pair of the PHY register sets.
+	at index 0: offset and length of register set for PHY common
+		    serdes block.
+	from index 1 - N: offset and length of register set for each lane,
+			  for N number of phy lanes (ports).
+ - lane-offsets: array of offsets to tx, rx and pcs blocks for phy lanes.
+ - #phy-cells: must be 1
+    - Cell after phy phandle should be the port (lane) number.
+ - clocks: a list of phandles and clock-specifier pairs,
+	   one for each entry in clock-names.
+ - clock-names: must be "cfg_ahb" for phy config clock,
+			"aux" for phy aux clock,
+			"ref_clk" for 19.2 MHz ref clk,
+			"ref_clk_src" for reference clock source,
+			"pipe<port-number>" for pipe clock specific to
+			each port/lane (Optional).
+ - resets: a list of phandles and reset controller specifier pairs,
+	   one for each entry in reset-names.
+ - reset-names: must be "phy" for reset of phy block,
+			"common" for phy common block reset,
+			"cfg" for phy's ahb cfg block reset (Optional).
+			"port<port-number>" for reset specific to
+			each port/lane (Optional).
+ - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
+ - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+Optional properties:
+ - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
+			pll block.
+
+Example:
+	pcie_phy: pciephy@34000 {
+		compatible = "qcom,msm8996-qmp-pcie-phy";
+		reg = <0x034000 0x48f>,
+			<0x035000 0x5bf>,
+			<0x036000 0x5bf>,
+			<0x037000 0x5bf>;
+				/* tx, rx, pcs */
+		lane-offsets = <0x0 0x200 0x400>;
+		#phy-cells = <1>;
+
+		clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
+			<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
+			<&rpmcc MSM8996_RPM_SMD_LN_BB_CLK>,
+			<&gcc GCC_PCIE_CLKREF_CLK>,
+			<&gcc GCC_PCIE_0_PIPE_CLK>,
+			<&gcc GCC_PCIE_1_PIPE_CLK>,
+			<&gcc GCC_PCIE_2_PIPE_CLK>;
+		clock-names = "aux", "cfg_ahb",
+				"ref_clk_src", "ref_clk",
+				"pipe0", "pipe1", "pipe2";
+
+		vdda-phy-supply = <&pm8994_l28>;
+		vdda-pll-supply = <&pm8994_l12>;
+
+		resets = <&gcc GCC_PCIE_PHY_BCR>,
+			<&gcc GCC_PCIE_PHY_COM_BCR>,
+			<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
+			<&gcc GCC_PCIE_0_PHY_BCR>,
+			<&gcc GCC_PCIE_1_PHY_BCR>,
+			<&gcc GCC_PCIE_2_PHY_BCR>;
+		reset-names = "phy", "common", "cfg",
+				"lane0", "lane1", "lane2";
+	};
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH v2 2/4] phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
From: Vivek Gautam @ 2016-11-22 12:02 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland, devicetree, linux-kernel
  Cc: srinivas.kandagatla, sboyd, linux-arm-msm, Vivek Gautam
In-Reply-To: <1479816163-5260-1-git-send-email-vivek.gautam@codeaurora.org>

PHY transceiver driver for QUSB2 phy controller that provides
HighSpeed functionality for DWC3 controller present on
Qualcomm chipsets.

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
---

Changes since v1:
 - removed reference to clk_enabled/pwr_enabled.
 - moved clock and regulator enable code to phy_power_on/off() callbacks.
 - fixed return on EPROBE_DEFER in qusb2_phy_probe().
 - fixed phy create and phy register ordering.
 - removed references to non-lkml links from commit message.
 - took care of other minor nits.
 - Fixed coccinelle warnings -
   'PTR_ERR applied after initialization to constant'
 - Addressed review comment, regarding qfprom access for tune2 param value.
   This driver is now based on qfprom patch[1] that allows byte access now.

Comments not addressed in this version:
 -- Have not addressed Kishon's comment to move phy init table stuff
    to generic phy calibration bindings.

[1] https://lkml.org/lkml/2016/11/17/21

 drivers/phy/Kconfig          |  11 +
 drivers/phy/Makefile         |   1 +
 drivers/phy/phy-qcom-qusb2.c | 549 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 561 insertions(+)
 create mode 100644 drivers/phy/phy-qcom-qusb2.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e8eb7f2..f1dcec1 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -430,6 +430,17 @@ config PHY_STIH407_USB
 	  Enable this support to enable the picoPHY device used by USB2
 	  and USB3 controllers on STMicroelectronics STiH407 SoC families.
 
+config PHY_QCOM_QUSB2
+	tristate "Qualcomm QUSB2 PHY Driver"
+	depends on OF && (ARCH_QCOM || COMPILE_TEST)
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the HighSpeed QUSB2 PHY transceiver for USB
+	  controllers on Qualcomm chips. This driver supports the high-speed
+	  PHY which is usually paired with either the ChipIdea or Synopsys DWC3
+	  USB IPs on MSM SOCs.
+
 config PHY_QCOM_UFS
 	tristate "Qualcomm UFS PHY driver"
 	depends on OF && ARCH_QCOM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 65eb2f4..dad1682 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
 obj-$(CONFIG_PHY_STIH407_USB)		+= phy-stih407-usb.o
+obj-$(CONFIG_PHY_QCOM_QUSB2) 	+= phy-qcom-qusb2.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-20nm.o
 obj-$(CONFIG_PHY_QCOM_UFS) 	+= phy-qcom-ufs-qmp-14nm.o
diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c
new file mode 100644
index 0000000..d3f9657
--- /dev/null
+++ b/drivers/phy/phy-qcom-qusb2.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define QUSB2PHY_PLL_TEST		0x04
+#define CLK_REF_SEL			BIT(7)
+
+#define QUSB2PHY_PLL_TUNE		0x08
+#define QUSB2PHY_PLL_USER_CTL1		0x0c
+#define QUSB2PHY_PLL_USER_CTL2		0x10
+#define QUSB2PHY_PLL_AUTOPGM_CTL1	0x1c
+#define QUSB2PHY_PLL_PWR_CTRL		0x18
+
+#define QUSB2PHY_PLL_STATUS		0x38
+#define PLL_LOCKED			BIT(5)
+
+#define QUSB2PHY_PORT_TUNE1             0x80
+#define QUSB2PHY_PORT_TUNE2             0x84
+#define QUSB2PHY_PORT_TUNE3             0x88
+#define QUSB2PHY_PORT_TUNE4             0x8C
+#define QUSB2PHY_PORT_TUNE5		0x90
+#define QUSB2PHY_PORT_TEST2		0x9c
+
+#define QUSB2PHY_PORT_POWERDOWN		0xB4
+#define CLAMP_N_EN			BIT(5)
+#define FREEZIO_N			BIT(1)
+#define POWER_DOWN			BIT(0)
+
+#define QUSB2PHY_REFCLK_ENABLE		BIT(0)
+
+#define PHY_CLK_SCHEME_SEL		BIT(0)
+
+struct qusb2_phy_init_tbl {
+	unsigned int reg_offset;
+	unsigned int cfg_val;
+};
+#define QCOM_QUSB2_PHY_INIT_CFG(reg, val) \
+	{				\
+		.reg_offset = reg,	\
+		.cfg_val = val,		\
+	}
+
+static struct qusb2_phy_init_tbl msm8996_phy_init_tbl[] = {
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xF8),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xB3),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xC0),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9F),
+	QCOM_QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
+};
+
+struct qusb2_phy_init_cfg {
+	struct qusb2_phy_init_tbl *phy_init_tbl;
+	int phy_init_tbl_sz;
+	/* offset to PHY_CLK_SCHEME register in TCSR map. */
+	unsigned int clk_scheme_offset;
+};
+
+const struct qusb2_phy_init_cfg msm8996_phy_init_cfg = {
+	.phy_init_tbl = msm8996_phy_init_tbl,
+	.phy_init_tbl_sz = ARRAY_SIZE(msm8996_phy_init_tbl),
+};
+
+/**
+ * struct qusb2_phy: Structure holding qusb2 phy attributes.
+ *
+ * @phy: pointer to generic phy.
+ * @base: pointer to iomapped memory space for qubs2 phy.
+ *
+ * @cfg_ahb_clk: pointer to AHB2PHY interface clock.
+ * @ref_clk: pointer to reference clock.
+ * @ref_clk_src: pointer to source to reference clock.
+ * @iface_src: pointer to phy interface clock.
+ *
+ * @phy_reset: Pointer to phy reset control
+ *
+ * @vdda_phy: vdd supply to the phy core block.
+ * @vdda_pll: 1.8V vdd supply to ref_clk block.
+ * @vdda_phy_dpdm: 3.1V vdd supply to Dp/Dm port signals.
+ * @tcsr: pointer to TCSR syscon register map.
+ *
+ * @cfg: phy initialization config data
+ * @has_se_clk_scheme: indicate if PHY has Single-ended ref clock scheme
+ */
+struct qusb2_phy {
+	struct phy *phy;
+	void __iomem *base;
+
+	struct clk *cfg_ahb_clk;
+	struct clk *ref_clk;
+	struct clk *ref_clk_src;
+	struct clk *iface_clk;
+
+	struct reset_control *phy_reset;
+
+	struct regulator *vdd_phy;
+	struct regulator *vdda_pll;
+	struct regulator *vdda_phy_dpdm;
+
+	struct regmap *tcsr;
+
+	const struct qusb2_phy_init_cfg *cfg;
+	bool has_se_clk_scheme;
+};
+
+static inline void qusb2_setbits(void __iomem *reg, u32 val)
+{
+	u32 reg_val;
+
+	reg_val = readl_relaxed(reg);
+	reg_val |= val;
+	writel_relaxed(reg_val, reg);
+
+	/* Ensure above write is completed */
+	mb();
+}
+
+static inline void qusb2_clrbits(void __iomem *reg, u32 val)
+{
+	u32 reg_val;
+
+	reg_val = readl_relaxed(reg);
+	reg_val &= ~val;
+	writel_relaxed(reg_val, reg);
+
+	/* Ensure above write is completed */
+	mb();
+}
+
+static void qcom_qusb2_phy_configure(void __iomem *base,
+				struct qusb2_phy_init_tbl init_tbl[],
+				int init_tbl_sz)
+{
+	int i;
+
+	for (i = 0; i < init_tbl_sz; i++) {
+		writel_relaxed(init_tbl[i].cfg_val,
+				base + init_tbl[i].reg_offset);
+	}
+
+	/* flush buffered writes */
+	mb();
+}
+
+static void qusb2_phy_enable_clocks(struct qusb2_phy *qphy, bool on)
+{
+	if (on) {
+		clk_prepare_enable(qphy->iface_clk);
+		clk_prepare_enable(qphy->ref_clk_src);
+	} else {
+		clk_disable_unprepare(qphy->ref_clk_src);
+		clk_disable_unprepare(qphy->iface_clk);
+	}
+
+	dev_vdbg(&qphy->phy->dev, "%s(): clocks enabled\n", __func__);
+}
+
+static int qusb2_phy_enable_power(struct qusb2_phy *qphy, bool on)
+{
+	int ret;
+	struct device *dev = &qphy->phy->dev;
+
+	if (!on)
+		goto disable_vdda_phy_dpdm;
+
+	ret = regulator_enable(qphy->vdd_phy);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdd-phy:%d\n", ret);
+		goto err_vdd_phy;
+	}
+
+	ret = regulator_enable(qphy->vdda_pll);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdda-pll:%d\n", ret);
+		goto disable_vdd_phy;
+	}
+
+	ret = regulator_enable(qphy->vdda_phy_dpdm);
+	if (ret) {
+		dev_err(dev, "Unable to enable vdda-phy-dpdm:%d\n", ret);
+		goto disable_vdda_pll;
+	}
+
+	dev_vdbg(dev, "%s() regulators are turned on.\n", __func__);
+
+	return ret;
+
+disable_vdda_phy_dpdm:
+	regulator_disable(qphy->vdda_phy_dpdm);
+disable_vdda_pll:
+	regulator_disable(qphy->vdda_pll);
+disable_vdd_phy:
+	regulator_disable(qphy->vdd_phy);
+err_vdd_phy:
+	dev_vdbg(dev, "%s() regulators are turned off.\n", __func__);
+	return ret;
+}
+
+/*
+ * Fetches HS Tx tuning value from e-fuse and sets QUSB2PHY_PORT_TUNE2
+ * register.
+ * For any error case, skip setting the value and use the default value.
+ */
+static int qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
+{
+	struct device *dev = &qphy->phy->dev;
+	struct nvmem_cell *cell;
+	ssize_t len;
+	u8 *val;
+
+	/*
+	 * Read EFUSE register having TUNE2 parameter's high nibble.
+	 * If efuse register shows value as 0x0, or if we fail to find
+	 * a valid efuse register settings, then use default value
+	 * as 0xB for high nibble that we have already set while
+	 * configuring phy.
+	 */
+	cell = devm_nvmem_cell_get(dev, "tune2_hstx_trim_efuse");
+	if (IS_ERR(cell)) {
+		if (PTR_ERR(cell) == -EPROBE_DEFER)
+			return PTR_ERR(cell);
+		goto skip;
+	}
+
+	/*
+	 * we need to read only one byte here, since the required
+	 * parameter value fits in one nibble
+	 */
+	val = (u8 *)nvmem_cell_read(cell, &len);
+	if (!IS_ERR(val)) {
+		/* Fused TUNE2 value is the higher nibble only */
+		qusb2_setbits(qphy->base + QUSB2PHY_PORT_TUNE2,
+							val[0] << 0x4);
+	} else {
+		dev_dbg(dev, "failed reading hs-tx trim value: %ld\n",
+							PTR_ERR(val));
+	}
+
+skip:
+	return 0;
+}
+
+static int qusb2_phy_poweron(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+	int ret;
+
+	dev_vdbg(&phy->dev, "Powering-on QUSB2 phy\n");
+
+	ret = qusb2_phy_enable_power(qphy, true);
+	if (ret)
+		return ret;
+
+	qusb2_phy_enable_clocks(qphy, true);
+
+	return ret;
+}
+
+static int qusb2_phy_poweroff(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	qusb2_phy_enable_clocks(qphy, false);
+	qusb2_phy_enable_power(qphy, false);
+
+	return 0;
+}
+
+static int qusb2_phy_init(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+	unsigned int reset_val;
+	unsigned int clk_scheme;
+	int ret;
+
+	dev_vdbg(&phy->dev, "Initializing QUSB2 phy\n");
+
+	/* enable ahb interface clock to program phy */
+	clk_prepare_enable(qphy->cfg_ahb_clk);
+
+	/* Perform phy reset */
+	ret = reset_control_assert(qphy->phy_reset);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to assert phy_reset\n");
+		return ret;
+	}
+	/* 100 us delay to keep PHY in reset mode */
+	usleep_range(100, 150);
+	ret = reset_control_deassert(qphy->phy_reset);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to de-assert phy_reset\n");
+		return ret;
+	}
+
+	/* Disable the PHY */
+	qusb2_setbits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
+			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+
+	/* save reset value to override based on clk scheme */
+	reset_val = readl_relaxed(qphy->base + QUSB2PHY_PLL_TEST);
+
+	qcom_qusb2_phy_configure(qphy->base, qphy->cfg->phy_init_tbl,
+				qphy->cfg->phy_init_tbl_sz);
+
+	/* Check for efuse value for tuning the PHY */
+	ret = qusb2_phy_set_tune2_param(qphy);
+	if (ret)
+		return ret;
+
+	/* Enable the PHY */
+	qusb2_clrbits(qphy->base + QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
+
+	/* Require to get phy pll lock successfully */
+	usleep_range(150, 160);
+
+	/* Default is Single-ended clock on msm8996 */
+	qphy->has_se_clk_scheme = true;
+	/*
+	 * read TCSR_PHY_CLK_SCHEME register to check if Single-ended
+	 * clock scheme is selected. If yes, then disable differential
+	 * ref_clk and use single-ended clock, otherwise use differential
+	 * ref_clk only.
+	 */
+	if (qphy->tcsr) {
+		ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset,
+							&clk_scheme);
+		/* is it a differential clock scheme ? */
+		if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) {
+			dev_vdbg(&phy->dev, "%s: select differential clk src\n",
+								__func__);
+			qphy->has_se_clk_scheme = false;
+		} else {
+			dev_vdbg(&phy->dev, "%s: select single-ended clk src\n",
+								__func__);
+		}
+	}
+
+	if (!qphy->has_se_clk_scheme) {
+		reset_val &= ~CLK_REF_SEL;
+		clk_prepare_enable(qphy->ref_clk);
+	} else {
+		reset_val |= CLK_REF_SEL;
+	}
+
+	writel_relaxed(reset_val, qphy->base + QUSB2PHY_PLL_TEST);
+
+	/* Make sure that above write is completed to get PLL source clock */
+	wmb();
+
+	/* Required to get PHY PLL lock successfully */
+	usleep_range(100, 110);
+
+	if (!(readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS) &
+					PLL_LOCKED)) {
+		dev_err(&phy->dev, "QUSB PHY PLL LOCK fails:%x\n",
+			readb_relaxed(qphy->base + QUSB2PHY_PLL_STATUS));
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int qusb2_phy_exit(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	/* Disable the PHY */
+	qusb2_setbits(qphy->base + QUSB2PHY_PORT_POWERDOWN,
+			CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+
+	if (!qphy->has_se_clk_scheme)
+		clk_disable_unprepare(qphy->ref_clk);
+
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+
+	return 0;
+}
+
+static const struct phy_ops qusb2_phy_gen_ops = {
+	.init		= qusb2_phy_init,
+	.exit		= qusb2_phy_exit,
+	.power_on	= qusb2_phy_poweron,
+	.power_off	= qusb2_phy_poweroff,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id qusb2_phy_of_match_table[] = {
+	{
+		.compatible	= "qcom,msm8996-qusb2-phy",
+		.data		= &msm8996_phy_init_cfg,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
+
+static int qusb2_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qusb2_phy *qphy;
+	struct phy_provider *phy_provider;
+	struct phy *generic_phy;
+	const struct of_device_id *match;
+	struct resource *res;
+	int ret;
+
+	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+	if (!qphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	qphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(qphy->base))
+		return PTR_ERR(qphy->base);
+
+	qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
+	if (IS_ERR(qphy->cfg_ahb_clk)) {
+		ret = PTR_ERR(qphy->cfg_ahb_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to get cfg_ahb_clk\n");
+		return ret;
+	}
+
+	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+	if (IS_ERR(qphy->ref_clk_src)) {
+		ret = PTR_ERR(qphy->ref_clk_src);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "clk get failed for ref_clk_src\n");
+		return ret;
+	}
+
+	qphy->ref_clk = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(qphy->ref_clk)) {
+		ret = PTR_ERR(qphy->ref_clk);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "clk get failed for ref_clk\n");
+		return ret;
+	} else {
+		clk_set_rate(qphy->ref_clk, 19200000);
+	}
+
+	qphy->iface_clk = devm_clk_get(dev, "iface_clk");
+	if (IS_ERR(qphy->iface_clk)) {
+		ret = PTR_ERR(qphy->iface_clk);
+		if (ret != -EPROBE_DEFER) {
+			qphy->iface_clk = NULL;
+			dev_dbg(dev, "clk get failed for iface_clk\n");
+		} else {
+			return ret;
+		}
+	}
+
+	qphy->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
+	if (IS_ERR(qphy->phy_reset)) {
+		dev_err(dev, "failed to get phy core reset\n");
+		return PTR_ERR(qphy->phy_reset);
+	}
+
+	qphy->vdd_phy = devm_regulator_get(dev, "vdd-phy");
+	if (IS_ERR(qphy->vdd_phy)) {
+		dev_err(dev, "unable to get vdd-phy supply\n");
+		return PTR_ERR(qphy->vdd_phy);
+	}
+
+	qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
+	if (IS_ERR(qphy->vdda_pll)) {
+		dev_err(dev, "unable to get vdda-pll supply\n");
+		return PTR_ERR(qphy->vdda_pll);
+	}
+
+	qphy->vdda_phy_dpdm = devm_regulator_get(dev, "vdda-phy-dpdm");
+	if (IS_ERR(qphy->vdda_phy_dpdm)) {
+		dev_err(dev, "unable to get vdda-phy-dpdm supply\n");
+		return PTR_ERR(qphy->vdda_phy_dpdm);
+	}
+
+	/* Get the specific init parameters of QMP phy */
+	match = of_match_node(qusb2_phy_of_match_table, dev->of_node);
+	qphy->cfg = match->data;
+
+	qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
+							"qcom,tcsr-syscon");
+	if (IS_ERR(qphy->tcsr)) {
+		dev_dbg(dev, "Failed to lookup TCSR regmap\n");
+		qphy->tcsr = NULL;
+	}
+
+	generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
+	if (IS_ERR(generic_phy)) {
+		ret = PTR_ERR(generic_phy);
+		dev_err(dev, "%s: failed to create phy %d\n", __func__, ret);
+		return ret;
+	}
+	qphy->phy = generic_phy;
+
+	dev_set_drvdata(dev, qphy);
+	phy_set_drvdata(generic_phy, qphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "%s: failed to register phy %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver qusb2_phy_driver = {
+	.probe		= qusb2_phy_probe,
+	.driver = {
+		.name	= "qcom-qusb2-phy",
+		.of_match_table = of_match_ptr(qusb2_phy_of_match_table),
+	},
+};
+
+module_platform_driver(qusb2_phy_driver);
+
+MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH v2 1/4] dt-bindings: phy: Add support for QUSB2 phy
From: Vivek Gautam @ 2016-11-22 12:02 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland, devicetree, linux-kernel
  Cc: srinivas.kandagatla, sboyd, linux-arm-msm, Vivek Gautam
In-Reply-To: <1479816163-5260-1-git-send-email-vivek.gautam@codeaurora.org>

Qualcomm chipsets have QUSB2 phy controller that provides
HighSpeed functionality for DWC3 controller.
Adding dt binding information for the same.

Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
---

Changes since v1:
 - New patch, forked out of the original driver patch:
   "phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips"
 - Updated dt bindings to remove 'hstx-trim-bit-offset' and
   'hstx-trim-bit-len' bindings.

 .../devicetree/bindings/phy/qcom-qusb2-phy.txt     | 55 ++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
new file mode 100644
index 0000000..38c8b30
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
@@ -0,0 +1,55 @@
+Qualcomm QUSB2 phy controller
+=============================
+
+QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
+
+Required properties:
+ - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
+ - reg: offset and length of the PHY register set.
+ - #phy-cells: must be 0.
+
+ - clocks: a list of phandles and clock-specifier pairs,
+	   one for each entry in clock-names.
+ - clock-names: must be "cfg_ahb" for phy config clock,
+			"ref_clk" for 19.2 MHz ref clk,
+			"ref_clk_src" reference clock source.
+			"iface" for phy interface clock (Optional).
+
+ - vdd-phy-supply: Phandle to a regulator supply to PHY core block.
+ - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
+ - vdda-phy-dpdm: Phandle to 3.1V regulator supply to Dp/Dm port signals.
+
+ - resets: a list of phandles and reset controller specifier pairs,
+	   one for each entry in reset-names.
+ - reset-names: must be "phy" for reset of phy block.
+
+Optional properties:
+ - nvmem-cells: a list of phandles to nvmem cells that contain fused
+		tuning parameters for qusb2 phy, one for each entry
+		in nvmem-cell-names.
+ - nvmem-cell-names: must be "tune2_hstx_trim_efuse" for cell containing
+		     HS Tx trim value.
+
+ - qcom,tcsr-syscon: Phandle to TCSR syscon register region.
+
+Example:
+	hsphy: qusb2phy@7411000 {
+		compatible = "qcom,msm8996-qusb2-phy";
+		reg = <0x07411000 0x180>;
+		#phy-cells = <0>;
+
+		clocks = <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			<&gcc GCC_RX1_USB2_CLKREF_CLK>,
+			<&rpmcc MSM8996_RPM_SMD_LN_BB_CLK>;
+		clock-names = "cfg_ahb_clk", "ref_clk", "ref_clk_src";
+
+		vdd-phy-supply = <&pm8994_s2>;
+		vdda-pll-supply = <&pm8994_l12>;
+		vdda-phy-dpdm-supply = <&pm8994_l24>;
+
+		resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
+		reset-names = "phy";
+
+		nvmem-cells = <&qusb2p_hstx_trim>;
+		nvmem-cell-names = "tune2_hstx_trim_efuse";
+        };
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* [PATCH v2 0/4] phy: USB and PCIe phy drivers for Qcom chipsets
From: Vivek Gautam @ 2016-11-22 12:02 UTC (permalink / raw)
  To: kishon, robh+dt, mark.rutland, devicetree, linux-kernel
  Cc: srinivas.kandagatla, sboyd, linux-arm-msm, Vivek Gautam

This patch series adds couple of PHY drivers for Qualcomm chipsets.
a) qcom-qusb2 phy driver: that provides High Speed USB functionality.
b) qcom-qmp phy driver: that is a combo phy providing support for
   USB3, PCIe and UFS controllers.[1]

The patches are based on next branch of linux-phy tree.

These have been tested on Dragon board db820c hardware with
required set of patches on integration tree maintained by
Linaro landing team for Qualcomm [2]. The QUSB2 phy driver
is also based on qfprom patch [3]:
[PATCH v2] nvmem: qfprom: Allow single byte accesses for read/write

Changes since v1:
 - Moved device tree binding documentation to separate patches, as suggested
   by Rob.
 - Addressed review comment from Rob, regarding qfprom accesses by qusb2 phy driver,
   given by Rob.
 - Addressed review comments from Kishon.
 - Addressed review comments from Srinivas for QMP phy driver.
 - Addressed kbuild warning.
 - Please see patches 2 & 4 in the series for details on changes in v2.

[1] Currently the qcom-qmp phy driver supports only USB3 and PCIe
controllers. Later, we plan to add the UFS phy support as well to this.
[2] https://git.linaro.org/?p=landing-teams/working/qualcomm/kernel.git;a=shortlog;h=refs/heads/integration-linux-qcomlt
[3] https://lkml.org/lkml/2016/11/17/21

Vivek Gautam (4):
  dt-bindings: phy: Add support for QUSB2 phy
  phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
  dt-bindings: phy: Add support for QMP phy
  phy: qcom-qmp: new qmp phy driver for qcom-chipsets

 .../devicetree/bindings/phy/qcom-qmp-phy.txt       |   74 ++
 .../devicetree/bindings/phy/qcom-qusb2-phy.txt     |   55 +
 drivers/phy/Kconfig                                |   20 +
 drivers/phy/Makefile                               |    2 +
 drivers/phy/phy-qcom-qmp.c                         | 1141 ++++++++++++++++++++
 drivers/phy/phy-qcom-qusb2.c                       |  549 ++++++++++
 6 files changed, 1841 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
 create mode 100644 drivers/phy/phy-qcom-qmp.c
 create mode 100644 drivers/phy/phy-qcom-qusb2.c

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

^ permalink raw reply

* [PATCH] ARM64: dts: meson-gxl: Add support for Nexbox A95X
From: Neil Armstrong @ 2016-11-22 11:41 UTC (permalink / raw)
  To: khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A
  Cc: Neil Armstrong, linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The Nexbox A95X exists with a Meson GXBB (S905) Soc or a Meson GXL SoC (S905X).
Add the S905X variant which uses the internal PHY instead of an external PHY.
---
 Documentation/devicetree/bindings/arm/amlogic.txt  |   1 +
 arch/arm64/boot/dts/amlogic/Makefile               |   1 +
 .../boot/dts/amlogic/meson-gxl-nexbox-a95x.dts     | 205 +++++++++++++++++++++
 3 files changed, 207 insertions(+)
 create mode 100644 arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts

diff --git a/Documentation/devicetree/bindings/arm/amlogic.txt b/Documentation/devicetree/bindings/arm/amlogic.txt
index 6ef7c52..9b2b41a 100644
--- a/Documentation/devicetree/bindings/arm/amlogic.txt
+++ b/Documentation/devicetree/bindings/arm/amlogic.txt
@@ -45,4 +45,5 @@ Board compatible values:
   - "amlogic,p231" (Meson gxl s905d)
   - "amlogic,q200" (Meson gxm s912)
   - "amlogic,q201" (Meson gxm s912)
+  - "nexbox,a95x" (Meson gxbb or Meson gxl s905x)
   - "nexbox,a1" (Meson gxm s912)
diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
index 2fbb8e3..0d7bfbf 100644
--- a/arch/arm64/boot/dts/amlogic/Makefile
+++ b/arch/arm64/boot/dts/amlogic/Makefile
@@ -8,6 +8,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxbb-vega-s95-telos.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905x-p212.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-p230.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-p231.dtb
+dtb-$(CONFIG_ARCH_MESON) += meson-gxl-nexbox-a95x.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxm-s912-q200.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxm-s912-q201.dtb
 dtb-$(CONFIG_ARCH_MESON) += meson-gxm-nexbox-a1.dtb
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
new file mode 100644
index 0000000..e99101a
--- /dev/null
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2016 Andreas Färber
+ * Copyright (c) 2016 BayLibre, Inc.
+ * Author: Neil Armstrong <narmstrong-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     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.
+ */
+
+/dts-v1/;
+
+#include "meson-gxl-s905x.dtsi"
+
+/ {
+	compatible = "nexbox,a95x", "amlogic,s905x", "amlogic,meson-gxl";
+	model = "NEXBOX A95X (S905X)";
+
+	aliases {
+		serial0 = &uart_AO;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	memory@0 {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x80000000>;
+	};
+
+	vddio_card: gpio-regulator {
+		compatible = "regulator-gpio";
+
+		regulator-name = "VDDIO_CARD";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+
+		gpios = <&gpio_ao GPIOAO_5 GPIO_ACTIVE_HIGH>;
+		gpios-states = <1>;
+
+		/* Based on P200 schematics, signal CARD_1.8V/3.3V_CTR */
+		states = <1800000 0
+			  3300000 1>;
+	};
+
+	vddio_boot: regulator-vddio_boot {
+		compatible = "regulator-fixed";
+		regulator-name = "VDDIO_BOOT";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vddao_3v3: regulator-vddao_3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "VDDAO_3V3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_3v3: regulator-vcc_3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "VCC_3V3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	emmc_pwrseq: emmc-pwrseq {
+		compatible = "mmc-pwrseq-emmc";
+		reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
+	};
+
+	wifi32k: wifi32k {
+		compatible = "pwm-clock";
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		pwms = <&pwm_ef 0 30518 0>; /* PWM_E at 32.768KHz */
+	};
+
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		reset-gpios = <&gpio GPIOX_6 GPIO_ACTIVE_LOW>;
+		clocks = <&wifi32k>;
+		clock-names = "ext_clock";
+	};
+};
+
+&uart_AO {
+	status = "okay";
+	pinctrl-0 = <&uart_ao_a_pins>;
+	pinctrl-names = "default";
+};
+
+&ethmac {
+	status = "okay";
+	phy-mode = "rmii";
+	phy-handle = <&internal_phy>;
+};
+
+&ir {
+	status = "okay";
+	pinctrl-0 = <&remote_input_ao_pins>;
+	pinctrl-names = "default";
+};
+
+/* Wireless SDIO Module */
+&sd_emmc_a {
+	status = "okay";
+	pinctrl-0 = <&sdio_pins>;
+	pinctrl-names = "default";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	bus-width = <4>;
+	cap-sd-highspeed;
+	max-frequency = <100000000>;
+
+	non-removable;
+	disable-wp;
+
+	mmc-pwrseq = <&sdio_pwrseq>;
+
+	vmmc-supply = <&vddao_3v3>;
+	vqmmc-supply = <&vddio_boot>;
+};
+
+/* SD card */
+&sd_emmc_b {
+	status = "okay";
+	pinctrl-0 = <&sdcard_pins>;
+	pinctrl-names = "default";
+
+	bus-width = <4>;
+	cap-sd-highspeed;
+	max-frequency = <100000000>;
+	disable-wp;
+
+	cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
+	cd-inverted;
+
+	vmmc-supply = <&vddao_3v3>;
+	vqmmc-supply = <&vddio_card>;
+};
+
+/* eMMC */
+&sd_emmc_c {
+	status = "okay";
+	pinctrl-0 = <&emmc_pins>;
+	pinctrl-names = "default";
+
+	bus-width = <8>;
+	cap-sd-highspeed;
+	cap-mmc-highspeed;
+	max-frequency = <200000000>;
+	non-removable;
+	disable-wp;
+	mmc-ddr-1_8v;
+	mmc-hs200-1_8v;
+
+	mmc-pwrseq = <&emmc_pwrseq>;
+	vmmc-supply = <&vcc_3v3>;
+	vqmmc-supply = <&vddio_boot>;
+};
+
+&pwm_ef {
+	status = "okay";
+	pinctrl-0 = <&pwm_e_pins>;
+	pinctrl-names = "default";
+	clocks = <&clkc CLKID_FCLK_DIV4>;
+	clock-names = "clkin0";
+};
-- 
2.7.0

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

^ permalink raw reply related

* Re: [PATCH 1/3] of: base: add support to get machine compatible string
From: Sudeep Holla @ 2016-11-22 11:06 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Sudeep Holla, Kevin Hilman, Michael Turquette, Sekhar Nori,
	Rob Herring, Frank Rowand, Mark Rutland, Peter Ujfalusi,
	Russell King, LKML, arm-soc, linux-drm, linux-devicetree,
	Jyri Sarha, Tomi Valkeinen, David Airlie, Laurent Pinchart,
	Robin Murphy
In-Reply-To: <CAMpxmJU=wR4mdQr5tpNSv8POirtWXR=_s7D_Q_H=Cuy=W=-Jtw@mail.gmail.com>



On 22/11/16 10:57, Bartosz Golaszewski wrote:
> 2016-11-22 11:53 GMT+01:00 Sudeep Holla <sudeep.holla@arm.com>:
>>
>>
>> On 22/11/16 10:41, Bartosz Golaszewski wrote:
>>>
>>> Add a function allowing to retrieve the compatible string of the root
>>> node of the device tree.
>>>
>>
>> Rob has queued [1] and it's in -next today. You can reuse that if you
>> are planning to target this for v4.11 or just use open coding in your
>> driver for v4.10 and target this move for v4.11 to avoid cross tree
>> dependencies as I already mentioned in your previous thread.
>
> Rob's patch checks the model first - I'm not sure this is the behavior
> we want here as Sekhar suggested we print the machine compatible.

IIUC, you are replacing of_flat_dt_get_machine_name and
of_machine_get_model_name does exactly same. So I don't see any point in
adding this new function and it's just used for logging purpose.
Also Sekhar just gave example by using just compatible adding that
function in the driver itself.

As Arnd suggested me[1], you should for v4.10 fix it in the driver 
itself to avoid the cross tree dependencies at this point similar to [2]

--
Regards,
Sudeep

[1] 
https://www.mail-archive.com/linuxppc-dev@lists.ozlabs.org/msg111428.html
[2] http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1274502.html

^ permalink raw reply

* Re: [PATCH v2 01/13] devicetree/bindings: display: Document common panel properties
From: Thierry Reding @ 2016-11-22 11:05 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Tomi Valkeinen,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479526093-7014-2-git-send-email-laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 3023 bytes --]

On Sat, Nov 19, 2016 at 05:28:01AM +0200, Laurent Pinchart wrote:
> Document properties common to several display panels in a central
> location that can be referenced by the panel device tree bindings.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnhk3lzF8UVTdg@public.gmane.orgm>
> ---
>  .../bindings/display/panel/panel-common.txt        | 91 ++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/panel-common.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.txt b/Documentation/devicetree/bindings/display/panel/panel-common.txt
> new file mode 100644
> index 000000000000..ec52c472c845
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/panel-common.txt
> @@ -0,0 +1,91 @@
> +Common Properties for Display Panel
> +===================================
> +
> +This document defines device tree properties common to several classes of
> +display panels. It doesn't constitue a device tree binding specification by
> +itself but is meant to be referenced by device tree bindings.
> +
> +When referenced from panel device tree bindings the properties defined in this
> +document are defined as follows. The panel device tree bindings are
> +responsible for defining whether each property is required or optional.
> +
> +
> +Descriptive Properties
> +----------------------
> +
> +- width-mm,
> +- height-mm: The width-mm and height-mm specify the width and height of the
> +  physical area where images are displayed. These properties are expressed in
> +  millimeters and rounded to the closest unit.

Erm... this is already implied by the compatible string. Having this in
device tree is completely redundant.

> +- label: The label property specifies a symbolic name for the panel as a
> +  string suitable for use by humans. It typically contains a name inscribed on
> +  the system (e.g. as an affixed label) or specified in the system's
> +  documentation (e.g. in the user's manual).
> +
> +  If no such name exists, and unless the property is mandatory according to
> +  device tree bindings, it shall rather be omitted than constructed of
> +  non-descriptive information. For instance an LCD panel in a system that
> +  contains a single panel shall not be labelled "LCD" if that name is not
> +  inscribed on the system or used in a descriptive fashion in system
> +  documentation.
> +
> +
> +Display Timings
> +---------------
> +
> +- panel-timing: Most display panels are restricted to a single resolution and
> +  require specific display timings. The panel-timing subnode expresses those
> +  timings as specified in the timing subnode section of the display timing
> +  bindings defined in
> +  Documentation/devicetree/bindings/display/display-timing.txt.

Why? That's also implied by the compatible string. Honestly, I thought
by now we had been over this often enough...

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply

* Re: [PATCH v2 02/13] devicetree/bindings: display: Add bindings for LVDS panels
From: Thierry Reding @ 2016-11-22 11:02 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Tomi Valkeinen
In-Reply-To: <1479526093-7014-3-git-send-email-laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 3215 bytes --]

On Sat, Nov 19, 2016 at 05:28:02AM +0200, Laurent Pinchart wrote:
> LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A.
> Multiple incompatible data link layers have been used over time to
> transmit image data to LVDS panels. This binding supports display panels
> compatible with the JEIDA-59-1999, Open-LDI and VESA SWPG
> specifications.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnhk3lzF8UVTdg@public.gmane.orgm>
> ---
>  .../bindings/display/panel/panel-lvds.txt          | 120 +++++++++++++++++++++
>  1 file changed, 120 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/panel-lvds.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.txt b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
> new file mode 100644
> index 000000000000..b938269f841e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.txt
> @@ -0,0 +1,120 @@
> +LVDS Display Panel
> +==================
> +
> +LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple
> +incompatible data link layers have been used over time to transmit image data
> +to LVDS panels. This bindings supports display panels compatible with the
> +following specifications.
> +
> +[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February
> +1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA)
> +[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National
> +Semiconductor
> +[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video
> +Electronics Standards Association (VESA)
> +
> +Device compatible with those specifications have been marketed under the
> +FPD-Link and FlatLink brands.
> +
> +
> +Required properties:
> +
> +- compatible: Shall contain "panel-lvds" in addition to a mandatory
> +  panel-specific compatible string defined in individual panel bindings. The
> +  "panel-lvds" value shall never be used on its own.

What good is it if it shall never be used on its own? The above sounds
to me like the panel-specific compatible string implies the LVDS
binding, in a way that many compatible strings imply the simple binding.
Note that initially we did the very same thing with "panel-simple", only
to realize that it's completely redundant because it is never used.

> +- width-mm: See panel-common.txt.
> +- height-mm: See panel-common.txt.
> +- data-mapping: The color signals mapping order, "jeida-18", "jeida-24"
> +  or "vesa-24".
> +
> +Optional properties:
> +
> +- label: See panel-common.txt.
> +- gpios: See panel-common.txt.
> +- backlight: See panel-common.txt.
> +- data-mirror: If set, reverse the bit order described in the data mappings
> +  below on all data lanes, transmitting bits for slots 6 to 0 instead of
> +  0 to 6.
> +
> +Required nodes:
> +
> +- panel-timing: See panel-common.txt.
> +- ports: See panel-common.txt. These bindings require a single port subnode
> +  corresponding to the panel LVDS input.

Looks like I should go read the patch that introduces panel-common.txt
first...

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply

* Re: [PATCH 1/3] of: base: add support to get machine compatible string
From: Bartosz Golaszewski @ 2016-11-22 10:58 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Mark Rutland, linux-devicetree, Tomi Valkeinen, Kevin Hilman,
	Michael Turquette, Sekhar Nori, Russell King, linux-drm, LKML,
	Peter Ujfalusi, Rob Herring, Jyri Sarha, Robin Murphy,
	Frank Rowand, arm-soc, Laurent Pinchart
In-Reply-To: <CAMpxmJU=wR4mdQr5tpNSv8POirtWXR=_s7D_Q_H=Cuy=W=-Jtw@mail.gmail.com>

2016-11-22 11:57 GMT+01:00 Bartosz Golaszewski <bgolaszewski@baylibre.com>:
> 2016-11-22 11:53 GMT+01:00 Sudeep Holla <sudeep.holla@arm.com>:
>>
>>
>> On 22/11/16 10:41, Bartosz Golaszewski wrote:
>>>
>>> Add a function allowing to retrieve the compatible string of the root
>>> node of the device tree.
>>>
>>
>> Rob has queued [1] and it's in -next today. You can reuse that if you
>> are planning to target this for v4.11 or just use open coding in your
>> driver for v4.10 and target this move for v4.11 to avoid cross tree
>> dependencies as I already mentioned in your previous thread.
>>
>> --
>> Regards,
>> Sudeep
>>
>> [1] http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1274549.html
>
> Rob's patch checks the model first - I'm not sure this is the behavior
> we want here as Sekhar suggested we print the machine compatible.
>

I meant your patch of course.

Thanks,
Bartosz
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* Re: [PATCH 1/3] of: base: add support to get machine compatible string
From: Bartosz Golaszewski @ 2016-11-22 10:57 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King, LKML,
	arm-soc, linux-drm, linux-devicetree, Jyri Sarha, Tomi Valkeinen,
	David Airlie, Laurent Pinchart, Robin Murphy
In-Reply-To: <5ce9fb9f-459a-562b-2e9f-85d35f9ec035-5wv7dgnIgG8@public.gmane.org>

2016-11-22 11:53 GMT+01:00 Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org>:
>
>
> On 22/11/16 10:41, Bartosz Golaszewski wrote:
>>
>> Add a function allowing to retrieve the compatible string of the root
>> node of the device tree.
>>
>
> Rob has queued [1] and it's in -next today. You can reuse that if you
> are planning to target this for v4.11 or just use open coding in your
> driver for v4.10 and target this move for v4.11 to avoid cross tree
> dependencies as I already mentioned in your previous thread.
>
> --
> Regards,
> Sudeep
>
> [1] http://www.mail-archive.com/linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg1274549.html

Rob's patch checks the model first - I'm not sure this is the behavior
we want here as Sekhar suggested we print the machine compatible.

Thanks,
Bartosz Golaszewski
--
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/3] of: base: add support to get machine compatible string
From: Sudeep Holla @ 2016-11-22 10:53 UTC (permalink / raw)
  To: Bartosz Golaszewski, Kevin Hilman, Michael Turquette, Sekhar Nori,
	Rob Herring, Frank Rowand, Mark Rutland, Peter Ujfalusi,
	Russell King
  Cc: Sudeep Holla, LKML, arm-soc, linux-drm, linux-devicetree,
	Jyri Sarha, Tomi Valkeinen, David Airlie, Laurent Pinchart,
	Robin Murphy
In-Reply-To: <1479811311-3080-2-git-send-email-bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>



On 22/11/16 10:41, Bartosz Golaszewski wrote:
> Add a function allowing to retrieve the compatible string of the root
> node of the device tree.
>

Rob has queued [1] and it's in -next today. You can reuse that if you
are planning to target this for v4.11 or just use open coding in your
driver for v4.10 and target this move for v4.11 to avoid cross tree
dependencies as I already mentioned in your previous thread.

-- 
Regards,
Sudeep

[1] http://www.mail-archive.com/linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg1274549.html
--
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 v7] PCI: qcom: add support to msm8996 PCIe controller
From: Srinivas Kandagatla @ 2016-11-22 10:43 UTC (permalink / raw)
  To: svarbanov, linux-pci, bhelgaas
  Cc: robh+dt, linux-arm-msm, srinivas.kandagatla, devicetree

This patch adds support to msm8996/apq8096 PCIe, MSM8996 supports
Gen 1/2, One lane, 3 PCIe root-complex with support to MSI and
legacy interrupts and it conforms to PCI Express Base 2.1 specification.

This patch adds post_init callback to qcom_pcie_ops, as this is PCIe
pipe clocks are only setup after the phy is powered on.
It also adds ltssm_enable callback as it is very much different to other
supported SOCs in the driver.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Acked-by: Stanimir Varbanov <svarbanov@mm-sol.com>
---
 Changes since v5/v6:
 	- No code changes.
	- s/pcie/PCIe in change log based on Rob's comments.
	- Removed DT example for 8996 as requested by Rob.

 .../devicetree/bindings/pci/qcom,pcie.txt          |  14 +-
 drivers/pci/host/pcie-qcom.c                       | 175 ++++++++++++++++++++-
 2 files changed, 183 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
index 4059a6f..15ced03 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
@@ -7,6 +7,7 @@
 			- "qcom,pcie-ipq8064" for ipq8064
 			- "qcom,pcie-apq8064" for apq8064
 			- "qcom,pcie-apq8084" for apq8084
+			- "qcom,pcie-msm8996" for msm8996 or apq8096
 
 - reg:
 	Usage: required
@@ -92,6 +93,17 @@
 			- "aux"		Auxiliary (AUX) clock
 			- "bus_master"	Master AXI clock
 			- "bus_slave"	Slave AXI clock
+
+- clock-names:
+	Usage: required for msm8996/apq8096
+	Value type: <stringlist>
+	Definition: Should contain the following entries
+			- "pipe"	Pipe Clock driving internal logic.
+			- "aux"		Auxiliary (AUX) clock.
+			- "cfg"		Configuration clk.
+			- "bus_master"	Master AXI clock.
+			- "bus_slave"	Slave AXI clock.
+
 - resets:
 	Usage: required
 	Value type: <prop-encoded-array>
@@ -115,7 +127,7 @@
 			- "core" Core reset
 
 - power-domains:
-	Usage: required for apq8084
+	Usage: required for apq8084 and msm8996/apq8096
 	Value type: <prop-encoded-array>
 	Definition: A phandle and power domain specifier pair to the
 		    power domain which is responsible for collapsing
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c
index 3593640..25c5556 100644
--- a/drivers/pci/host/pcie-qcom.c
+++ b/drivers/pci/host/pcie-qcom.c
@@ -36,11 +36,17 @@
 
 #include "pcie-designware.h"
 
+#define PCIE20_PARF_SYS_CTRL			0x00
 #define PCIE20_PARF_PHY_CTRL			0x40
 #define PCIE20_PARF_PHY_REFCLK			0x4C
 #define PCIE20_PARF_DBI_BASE_ADDR		0x168
 #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE		0x16c
+#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL	0x174
 #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT	0x178
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2	0x1A8
+#define PCIE20_PARF_LTSSM			0x1B0
+#define PCIE20_PARF_SID_OFFSET			0x234
+#define PCIE20_PARF_BDF_TRANSLATE_CFG		0x24C
 
 #define PCIE20_ELBI_SYS_CTRL			0x04
 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE		BIT(0)
@@ -72,9 +78,18 @@ struct qcom_pcie_resources_v1 {
 	struct regulator *vdda;
 };
 
+struct qcom_pcie_resources_v2 {
+	struct clk *aux_clk;
+	struct clk *master_clk;
+	struct clk *slave_clk;
+	struct clk *cfg_clk;
+	struct clk *pipe_clk;
+};
+
 union qcom_pcie_resources {
 	struct qcom_pcie_resources_v0 v0;
 	struct qcom_pcie_resources_v1 v1;
+	struct qcom_pcie_resources_v2 v2;
 };
 
 struct qcom_pcie;
@@ -82,7 +97,9 @@ struct qcom_pcie;
 struct qcom_pcie_ops {
 	int (*get_resources)(struct qcom_pcie *pcie);
 	int (*init)(struct qcom_pcie *pcie);
+	int (*post_init)(struct qcom_pcie *pcie);
 	void (*deinit)(struct qcom_pcie *pcie);
+	void (*ltssm_enable)(struct qcom_pcie *pcie);
 };
 
 struct qcom_pcie {
@@ -116,17 +133,33 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
 	return dw_handle_msi_irq(pp);
 }
 
-static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+static void qcom_pcie_v0_v1_ltssm_enable(struct qcom_pcie *pcie)
 {
 	u32 val;
-
-	if (dw_pcie_link_up(&pcie->pp))
-		return 0;
-
 	/* enable link training */
 	val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
 	val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
 	writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+}
+
+static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie)
+{
+	u32 val;
+	/* enable link training */
+	val = readl(pcie->parf + PCIE20_PARF_LTSSM);
+	val |= BIT(8);
+	writel(val, pcie->parf + PCIE20_PARF_LTSSM);
+}
+
+static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+{
+
+	if (dw_pcie_link_up(&pcie->pp))
+		return 0;
+
+	/* Enable Link Training state machine */
+	if (pcie->ops->ltssm_enable)
+		pcie->ops->ltssm_enable(pcie);
 
 	return dw_pcie_wait_for_link(&pcie->pp);
 }
@@ -421,6 +454,113 @@ static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
 	return ret;
 }
 
+static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+	struct device *dev = pcie->pp.dev;
+
+	res->aux_clk = devm_clk_get(dev, "aux");
+	if (IS_ERR(res->aux_clk))
+		return PTR_ERR(res->aux_clk);
+
+	res->cfg_clk = devm_clk_get(dev, "cfg");
+	if (IS_ERR(res->cfg_clk))
+		return PTR_ERR(res->cfg_clk);
+
+	res->master_clk = devm_clk_get(dev, "bus_master");
+	if (IS_ERR(res->master_clk))
+		return PTR_ERR(res->master_clk);
+
+	res->slave_clk = devm_clk_get(dev, "bus_slave");
+	if (IS_ERR(res->slave_clk))
+		return PTR_ERR(res->slave_clk);
+
+	res->pipe_clk = devm_clk_get(dev, "pipe");
+	if (IS_ERR(res->pipe_clk))
+		return PTR_ERR(res->pipe_clk);
+
+	return 0;
+}
+
+static int qcom_pcie_init_v2(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+	struct device *dev = pcie->pp.dev;
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(res->aux_clk);
+	if (ret) {
+		dev_err(dev, "cannot prepare/enable aux clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(res->cfg_clk);
+	if (ret) {
+		dev_err(dev, "cannot prepare/enable cfg clock\n");
+		goto err_cfg_clk;
+	}
+
+	ret = clk_prepare_enable(res->master_clk);
+	if (ret) {
+		dev_err(dev, "cannot prepare/enable master clock\n");
+		goto err_master_clk;
+	}
+
+	ret = clk_prepare_enable(res->slave_clk);
+	if (ret) {
+		dev_err(dev, "cannot prepare/enable slave clock\n");
+		goto err_slave_clk;
+	}
+
+	/* enable PCIe clocks and resets */
+	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+	val &= ~BIT(0);
+	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+	/* change DBI base address */
+	writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+	/* MAC PHY_POWERDOWN MUX DISABLE  */
+	val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
+	val &= ~BIT(29);
+	writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
+
+	val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+	val |= BIT(4);
+	writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+
+	val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
+	val |= BIT(31);
+	writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
+
+	return 0;
+
+err_slave_clk:
+	clk_disable_unprepare(res->master_clk);
+err_master_clk:
+	clk_disable_unprepare(res->cfg_clk);
+err_cfg_clk:
+	clk_disable_unprepare(res->aux_clk);
+
+	return ret;
+}
+
+static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+	struct device *dev = pcie->pp.dev;
+	int ret;
+
+	ret = clk_prepare_enable(res->pipe_clk);
+	if (ret) {
+		dev_err(dev, "cannot prepare/enable pipe clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
 static int qcom_pcie_link_up(struct pcie_port *pp)
 {
 	struct qcom_pcie *pcie = to_qcom_pcie(pp);
@@ -429,6 +569,17 @@ static int qcom_pcie_link_up(struct pcie_port *pp)
 	return !!(val & PCI_EXP_LNKSTA_DLLLA);
 }
 
+static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+
+	clk_disable_unprepare(res->pipe_clk);
+	clk_disable_unprepare(res->slave_clk);
+	clk_disable_unprepare(res->master_clk);
+	clk_disable_unprepare(res->cfg_clk);
+	clk_disable_unprepare(res->aux_clk);
+}
+
 static void qcom_pcie_host_init(struct pcie_port *pp)
 {
 	struct qcom_pcie *pcie = to_qcom_pcie(pp);
@@ -444,6 +595,9 @@ static void qcom_pcie_host_init(struct pcie_port *pp)
 	if (ret)
 		goto err_deinit;
 
+	if (pcie->ops->post_init)
+		pcie->ops->post_init(pcie);
+
 	dw_pcie_setup_rc(pp);
 
 	if (IS_ENABLED(CONFIG_PCI_MSI))
@@ -487,12 +641,22 @@ static const struct qcom_pcie_ops ops_v0 = {
 	.get_resources = qcom_pcie_get_resources_v0,
 	.init = qcom_pcie_init_v0,
 	.deinit = qcom_pcie_deinit_v0,
+	.ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
 };
 
 static const struct qcom_pcie_ops ops_v1 = {
 	.get_resources = qcom_pcie_get_resources_v1,
 	.init = qcom_pcie_init_v1,
 	.deinit = qcom_pcie_deinit_v1,
+	.ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
+};
+
+static const struct qcom_pcie_ops ops_v2 = {
+	.get_resources = qcom_pcie_get_resources_v2,
+	.init = qcom_pcie_init_v2,
+	.post_init = qcom_pcie_post_init_v2,
+	.deinit = qcom_pcie_deinit_v2,
+	.ltssm_enable = qcom_pcie_v2_ltssm_enable,
 };
 
 static int qcom_pcie_probe(struct platform_device *pdev)
@@ -572,6 +736,7 @@ static const struct of_device_id qcom_pcie_match[] = {
 	{ .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
 	{ .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
 	{ .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
+	{ .compatible = "qcom,pcie-msm8996", .data = &ops_v2 },
 	{ }
 };
 
-- 
2.10.1

^ permalink raw reply related

* [PATCH 3/3] memory: da8xx-ddrctl: drop the call to of_flat_dt_get_machine_name()
From: Bartosz Golaszewski @ 2016-11-22 10:41 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: linux-devicetree, LKML, linux-drm, Bartosz Golaszewski,
	Tomi Valkeinen, Jyri Sarha, Sudeep Holla, Robin Murphy, arm-soc,
	Laurent Pinchart
In-Reply-To: <1479811311-3080-1-git-send-email-bgolaszewski@baylibre.com>

In order to avoid a section mismatch use of_machine_get_compatible()
instead of of_flat_dt_get_machine_name() when printing the error
message.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/memory/da8xx-ddrctl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/memory/da8xx-ddrctl.c b/drivers/memory/da8xx-ddrctl.c
index a20e7bb..179f505 100644
--- a/drivers/memory/da8xx-ddrctl.c
+++ b/drivers/memory/da8xx-ddrctl.c
@@ -118,7 +118,7 @@ static int da8xx_ddrctl_probe(struct platform_device *pdev)
 	setting = da8xx_ddrctl_get_board_settings();
 	if (!setting) {
 		dev_err(dev, "no settings for board '%s'\n",
-			of_flat_dt_get_machine_name());
+			of_machine_get_compatible());
 		return -EINVAL;
 	}
 
-- 
2.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH 2/3] bus: da8xx-mstpri: drop the call to of_flat_dt_get_machine_name()
From: Bartosz Golaszewski @ 2016-11-22 10:41 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: linux-devicetree, LKML, linux-drm, Bartosz Golaszewski,
	Tomi Valkeinen, Jyri Sarha, Sudeep Holla, Robin Murphy, arm-soc,
	Laurent Pinchart
In-Reply-To: <1479811311-3080-1-git-send-email-bgolaszewski@baylibre.com>

In order to avoid a section mismatch use of_machine_get_compatible()
instead of of_flat_dt_get_machine_name() when printing the error
message.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/bus/da8xx-mstpri.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/bus/da8xx-mstpri.c b/drivers/bus/da8xx-mstpri.c
index 85f0b53..41cbbe6 100644
--- a/drivers/bus/da8xx-mstpri.c
+++ b/drivers/bus/da8xx-mstpri.c
@@ -227,7 +227,7 @@ static int da8xx_mstpri_probe(struct platform_device *pdev)
 	prio_list = da8xx_mstpri_get_board_prio();
 	if (!prio_list) {
 		dev_err(dev, "no master priotities defined for board '%s'\n",
-			of_flat_dt_get_machine_name());
+			of_machine_get_compatible());
 		return -EINVAL;
 	}
 
-- 
2.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH 1/3] of: base: add support to get machine compatible string
From: Bartosz Golaszewski @ 2016-11-22 10:41 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: linux-devicetree, LKML, linux-drm, Bartosz Golaszewski,
	Tomi Valkeinen, Jyri Sarha, Sudeep Holla, Robin Murphy, arm-soc,
	Laurent Pinchart
In-Reply-To: <1479811311-3080-1-git-send-email-bgolaszewski@baylibre.com>

Add a function allowing to retrieve the compatible string of the root
node of the device tree.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/of/base.c  | 22 ++++++++++++++++++++++
 include/linux/of.h |  6 ++++++
 2 files changed, 28 insertions(+)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index a0bccb5..bbfe5e9 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -546,6 +546,28 @@ int of_machine_is_compatible(const char *compat)
 EXPORT_SYMBOL(of_machine_is_compatible);
 
 /**
+ * of_machine_get_compatible - Get the compatible property of the root node
+ *
+ * Returns a NULL-terminated string containing the compatible if it could
+ * be found, NULL otherwise.
+ */
+const char *of_machine_get_compatible(void)
+{
+	struct device_node *root;
+	const char *compatible;
+	int ret = -1;
+
+	root = of_find_node_by_path("/");
+	if (root) {
+		ret = of_property_read_string(root, "compatible", &compatible);
+		of_node_put(root);
+	}
+
+	return ret ? NULL : compatible;
+}
+EXPORT_SYMBOL(of_machine_get_compatible);
+
+/**
  *  __of_device_is_available - check if a device is available for use
  *
  *  @device: Node to check for availability, with locks already held
diff --git a/include/linux/of.h b/include/linux/of.h
index 299aeb1..664b734 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -367,6 +367,7 @@ extern int of_alias_get_id(struct device_node *np, const char *stem);
 extern int of_alias_get_highest_id(const char *stem);
 
 extern int of_machine_is_compatible(const char *compat);
+extern const char *of_machine_get_compatible(void);
 
 extern int of_add_property(struct device_node *np, struct property *prop);
 extern int of_remove_property(struct device_node *np, struct property *prop);
@@ -788,6 +789,11 @@ static inline int of_machine_is_compatible(const char *compat)
 	return 0;
 }
 
+static inline const char *of_machine_get_compatible(void)
+{
+	return NULL;
+}
+
 static inline bool of_console_check(const struct device_node *dn, const char *name, int index)
 {
 	return false;
-- 
2.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH 0/3] ARM: da8xx: fix section mismatch in new drivers
From: Bartosz Golaszewski @ 2016-11-22 10:41 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
	Tomi Valkeinen, David Airlie, Laurent Pinchart, Robin Murphy,
	Sudeep Holla, Bartosz Golaszewski

Sekhar noticed there's a section mismatch in the da8xx-mstpri and
da8xx-ddrctl drivers. This is caused by calling
of_flat_dt_get_machine_name() which has an __init annotation.

This series addresses this issue by introducing a new function that
allows to retrieve the compatible property of the root node and
using it instead of of_flat_dt_get_machine_name() in the new drivers.

Bartosz Golaszewski (3):
  of: base: add support to get machine compatible string
  bus: da8xx-mstpri: drop the call to of_flat_dt_get_machine_name()
  memory: da8xx-ddrctl: drop the call to of_flat_dt_get_machine_name()

 drivers/bus/da8xx-mstpri.c    |  2 +-
 drivers/memory/da8xx-ddrctl.c |  2 +-
 drivers/of/base.c             | 22 ++++++++++++++++++++++
 include/linux/of.h            |  6 ++++++
 4 files changed, 30 insertions(+), 2 deletions(-)

-- 
2.9.3

--
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: dts: da850: specify max width for display node
From: Bartosz Golaszewski @ 2016-11-22 10:30 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King, LKML,
	arm-soc, linux-drm, linux-devicetree, Jyri Sarha, David Airlie,
	Laurent Pinchart
In-Reply-To: <77c993f3-1b5f-94cf-c4bc-8a30148e0a4d-l0cyMroinI0@public.gmane.org>

2016-11-22 11:27 GMT+01:00 Tomi Valkeinen <tomi.valkeinen-l0cyMroinI0@public.gmane.org>:
> On 22/11/16 11:42, Bartosz Golaszewski wrote:
>> It has been determined that the highest resolution supported correctly
>> by LCDC rev1 is 800x600 on da850 due to memory bandwidth constraints.
>>
>> Set the max_width property in da850.dtsi to 800.
>>
>> Signed-off-by: Bartosz Golaszewski <bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
>> ---
>>  arch/arm/boot/dts/da850.dtsi | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
>> index 36066fa..0876238 100644
>> --- a/arch/arm/boot/dts/da850.dtsi
>> +++ b/arch/arm/boot/dts/da850.dtsi
>> @@ -441,6 +441,7 @@
>>                       compatible = "ti,da850-tilcdc";
>>                       reg = <0x213000 0x1000>;
>>                       interrupts = <52>;
>> +                     max-width = <800>;
>>                       status = "disabled";
>>               };
>>       };
>>
>
> Does 1024x768@10 work?
>
> The max-width should be the hardware's maximum supported width, not used
> for bandwidth. tilcdc has max-bandwidth property for that.
>
>  Tomi
>

Eeek I misread Jyri's answer.

Will fix that in v2.

Thanks,
Bartosz
--
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: dts: da850: specify max width for display node
From: Tomi Valkeinen @ 2016-11-22 10:27 UTC (permalink / raw)
  To: Bartosz Golaszewski, Kevin Hilman, Michael Turquette, Sekhar Nori,
	Rob Herring, Frank Rowand, Mark Rutland, Peter Ujfalusi,
	Russell King
  Cc: linux-devicetree, LKML, linux-drm, Jyri Sarha, arm-soc,
	Laurent Pinchart
In-Reply-To: <1479807775-28283-1-git-send-email-bgolaszewski@baylibre.com>


[-- Attachment #1.1.1: Type: text/plain, Size: 958 bytes --]

On 22/11/16 11:42, Bartosz Golaszewski wrote:
> It has been determined that the highest resolution supported correctly
> by LCDC rev1 is 800x600 on da850 due to memory bandwidth constraints.
> 
> Set the max_width property in da850.dtsi to 800.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> ---
>  arch/arm/boot/dts/da850.dtsi | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi
> index 36066fa..0876238 100644
> --- a/arch/arm/boot/dts/da850.dtsi
> +++ b/arch/arm/boot/dts/da850.dtsi
> @@ -441,6 +441,7 @@
>  			compatible = "ti,da850-tilcdc";
>  			reg = <0x213000 0x1000>;
>  			interrupts = <52>;
> +			max-width = <800>;
>  			status = "disabled";
>  		};
>  	};
> 

Does 1024x768@10 work?

The max-width should be the hardware's maximum supported width, not used
for bandwidth. tilcdc has max-bandwidth property for that.

 Tomi


[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* Re: [PATCH 3/6] reset: hisilicon: add reset-hi3660
From: Philipp Zabel @ 2016-11-22 10:22 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: zhangfei, Rob Herring, haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	xuwei5-C8/M+/jPZTeaMJb+Lgu22Q, Chen Feng, Xinliang Liu, Xia Qing,
	Jiancheng Xue, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <3166877.sQekoU5ezv@wuerfel>

Am Dienstag, den 22.11.2016, 10:42 +0100 schrieb Arnd Bergmann:
> On Tuesday, November 22, 2016 5:34:05 PM CET zhangfei wrote:
> > On 2016年11月22日 16:50, Arnd Bergmann wrote:
> > > On Tuesday, November 22, 2016 3:49:18 PM CET Zhangfei Gao wrote:
> > >> +static const struct hisi_reset_channel_data hi3660_iomcu_rst[] = {
> > >> +       [HI3660_RST_I2C0] = HISI_RST_SEP(0x20, 3),
> > >> +       [HI3660_RST_I2C1] = HISI_RST_SEP(0x20, 4),
> > >> +       [HI3660_RST_I2C2] = HISI_RST_SEP(0x20, 5),
> > >> +       [HI3660_RST_I2C6] = HISI_RST_SEP(0x20, 27),
> > >> +};
> > >> +
> > >> +static struct hisi_reset_controller_data hi3660_iomcu_controller = {
> > >> +       .nr_channels = ARRAY_SIZE(hi3660_iomcu_rst),
> > >> +       .channels = hi3660_iomcu_rst,
> > >> +};
> > >> +
> > >> +static const struct hisi_reset_channel_data hi3660_crgctrl_rst[] = {
> > >> +       [HI3660_RST_I2C3] = HISI_RST_SEP(0x78, 7),
> > >> +       [HI3660_RST_I2C4] = HISI_RST_SEP(0x78, 27),
> > >> +       [HI3660_RST_I2C7] = HISI_RST_SEP(0x60, 14),
> > >> +       [HI3660_RST_SD] = HISI_RST_SEP(0x90, 18),
> > >> +       [HI3660_RST_SDIO] = HISI_RST_SEP(0x90, 20),
> > >> +       [HI3660_RST_UFS] = HISI_RST_SEP(0x84, 12),
> > >> +       [HI3660_RST_UFS_ASSERT] = HISI_RST_SEP(0x84, 7),
> > >> +       [HI3660_RST_PCIE_SYS] = HISI_RST_SEP(0x84, 26),
> > >> +       [HI3660_RST_PCIE_PHY] = HISI_RST_SEP(0x84, 27),
> > >> +       [HI3660_RST_PCIE_BUS] = HISI_RST_SEP(0x84, 31),
> > >> +       [HI3660_RST_USB3OTG_PHY] = HISI_RST_SEP(0x90, 3),
> > >> +       [HI3660_RST_USB3OTG] = HISI_RST_SEP(0x90, 5),
> > >> +       [HI3660_RST_USB3OTG_32K] = HISI_RST_SEP(0x90, 6),
> > >> +       [HI3660_RST_USB3OTG_AHB] = HISI_RST_SEP(0x90, 7),
> > >> +       [HI3660_RST_USB3OTG_MUX] = HISI_RST_SEP(0x90, 8),
> > >> +};
> > > I think you can avoid the trap of the ABI incompatibility if
> > > you just define those as in the binding as tuples, using #reset-cells=2.
> > >
> > > In particular for the first set, it seems really silly to redefine
> > > the numbers when there is just a simple integer number.
> > 
> > Could you clarify more, still not understand.
> > The number is index of the arrays, and the index will be used in dts.
> > The arrays lists the registers offset and bit shift.
> > For example:
> > 
> > [HI3660_RST_I2C0] = HISI_RST_SEP(0x20, 3), means register offset : 0x20, and bit shift = 3.
> > 
> > And Documentation/devicetree/bindings/reset/reset.txt
> > Required properties:
> > #reset-cells:   Number of cells in a reset specifier; Typically 0 for nodes
> >                  with a single reset output and 1 for nodes with multiple
> >                  reset outputs.

This is just a suggestion, for reset controllers where the reset lines
can reasonably be enumerated by a single integer. If there is a good
reason to use more complicated bindings, more cells can be used.
That being said, I dislike having to spread register/bit information
throughout the device trees at the consumer/phandle sites, if the
register/bit information absolutely has to be put into the device tree,
I'd prefer a binding similar to ti-syscon, where it's all in one place.

> You can easily enumerate the registers that contain reset bits here,
> so just use one cell for the register and another one for the index.

Changing the reset cells is an incompatible change, and this is not a
straight forward register/bit mapping in hardware either. There are
currently three registers involved: enable (+0x0), disable (+0x4), and
status (+0x8). Also, what if in the future one of these reset bits have
to be handled inverted (as just happened for hi3519)?

regards
Philipp


--
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/6] reset: hisilicon: add reset core
From: Philipp Zabel @ 2016-11-22 10:22 UTC (permalink / raw)
  To: Zhangfei Gao
  Cc: Rob Herring, haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	xuwei5-C8/M+/jPZTeaMJb+Lgu22Q, Chen Feng, Xinliang Liu, Xia Qing,
	Jiancheng Xue, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479800961-6249-2-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Hi Zhangfei,

Am Dienstag, den 22.11.2016, 15:49 +0800 schrieb Zhangfei Gao:
> reset.c will be shared by hisilicon chips like hi3660 and hi6220
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  drivers/reset/Makefile           |   2 +-
>  drivers/reset/hisilicon/Makefile |   1 +
>  drivers/reset/hisilicon/reset.c  | 108 +++++++++++++++++++++++++++++++++++++++
>  drivers/reset/hisilicon/reset.h  |  37 ++++++++++++++
>  4 files changed, 147 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/reset/hisilicon/reset.c
>  create mode 100644 drivers/reset/hisilicon/reset.h
> 
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index bbe7026..7e3dc4e 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -1,8 +1,8 @@
>  obj-y += core.o
> -obj-y += hisilicon/
>  obj-$(CONFIG_ARCH_STI) += sti/
>  obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
>  obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
> +obj-$(CONFIG_ARCH_HISI) += hisilicon/
>  obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
>  obj-$(CONFIG_RESET_MESON) += reset-meson.o
>  obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
> diff --git a/drivers/reset/hisilicon/Makefile b/drivers/reset/hisilicon/Makefile
> index c932f86..df511f5 100644
> --- a/drivers/reset/hisilicon/Makefile
> +++ b/drivers/reset/hisilicon/Makefile
> @@ -1 +1,2 @@
> +obj-y	+= reset.o
>  obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o
> diff --git a/drivers/reset/hisilicon/reset.c b/drivers/reset/hisilicon/reset.c
> new file mode 100644
> index 0000000..c4971c9
> --- /dev/null
> +++ b/drivers/reset/hisilicon/reset.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright (c) 2016-2017 Linaro Ltd.
> + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
> + *
> + * 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/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +
> +#include "reset.h"
> +
> +struct hisi_reset_controller {
> +	struct reset_controller_dev rst;
> +	const struct hisi_reset_channel_data *channels;
> +	struct regmap *map;
> +};
> +
> +#define to_hisi_reset_controller(_rst) \
> +	container_of(_rst, struct hisi_reset_controller, rst)
> +
> +static int hisi_reset_program_hw(struct reset_controller_dev *rcdev,
> +				 unsigned long idx, bool assert)
> +{
> +	struct hisi_reset_controller *rc = to_hisi_reset_controller(rcdev);
> +	const struct hisi_reset_channel_data *ch;
> +
> +	if (idx >= rcdev->nr_resets)
> +		return -EINVAL;
> +
> +	ch = &rc->channels[idx];
> +
> +	if (assert)
> +		return regmap_write(rc->map, ch->enable.reg,
> +				    GENMASK(ch->enable.msb, ch->enable.lsb));
> +	else
> +		return regmap_write(rc->map, ch->disable.reg,
> +				    GENMASK(ch->disable.msb, ch->disable.lsb));

These fields are always 1-bit wide and you are not using the
regmap_field functions to access them, so I'd suggest to remove the
struct reg_field indirection and overhead and just write

	if (assert)
		return regmap_write(rc->map, ch->enable_reg, ch->bit);
	else
		return regmap_write(rc->map, ch->disable_reg, ch->bit);

here.

> +}
> +
> +static int hisi_reset_assert(struct reset_controller_dev *rcdev,
> +			     unsigned long idx)
> +{
> +	return hisi_reset_program_hw(rcdev, idx, true);
> +}
> +
> +static int hisi_reset_deassert(struct reset_controller_dev *rcdev,
> +			       unsigned long idx)
> +{
> +	return hisi_reset_program_hw(rcdev, idx, false);
> +}
> +
> +static int hisi_reset_dev(struct reset_controller_dev *rcdev,
> +			  unsigned long idx)
> +{
> +	int err;
> +
> +	err = hisi_reset_assert(rcdev, idx);
> +	if (err)
> +		return err;
> +
> +	return hisi_reset_deassert(rcdev, idx);
> +}
> +
> +static struct reset_control_ops hisi_reset_ops = {
> +	.reset    = hisi_reset_dev,
> +	.assert   = hisi_reset_assert,
> +	.deassert = hisi_reset_deassert,
> +};
> +
> +int hisi_reset_probe(struct platform_device *pdev)
> +{
> +	struct hisi_reset_controller *rc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct hisi_reset_controller_data *d;
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +
> +	match = of_match_device(dev->driver->of_match_table, dev);
> +	if (!match || !match->data)
> +		return -EINVAL;
> +
> +	d = (struct hisi_reset_controller_data *)match->data;
> +	rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
> +	if (!rc)
> +		return -ENOMEM;
> +
> +	rc->map = syscon_regmap_lookup_by_phandle(np, "hisi,rst-syscon");
> +	if (IS_ERR(rc->map)) {
> +		dev_err(dev, "failed to get hisi,rst-syscon\n");
> +		return PTR_ERR(rc->map);
> +	}
> +
> +	rc->rst.ops = &hisi_reset_ops,
> +	rc->rst.of_node = np;
> +	rc->rst.nr_resets = d->nr_channels;
> +	rc->channels = d->channels;
> +
> +	return reset_controller_register(&rc->rst);
> +}
> +EXPORT_SYMBOL_GPL(hisi_reset_probe);
> diff --git a/drivers/reset/hisilicon/reset.h b/drivers/reset/hisilicon/reset.h
> new file mode 100644
> index 0000000..77259ee
> --- /dev/null
> +++ b/drivers/reset/hisilicon/reset.h
> @@ -0,0 +1,37 @@
> +/*
> + * Copyright (c) 2016-2017 Linaro Ltd.
> + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
> + *
> + * 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 __HISILICON_RESET_H
> +#define __HISILICON_RESET_H
> +
> +#include <linux/device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset-controller.h>
> +
> +/* reset separated register offset is 0x4 */
> +#define HISI_RST_SEP(off, bit)					\
> +	{ .enable	= REG_FIELD(off, bit, bit),		\
> +	  .disable	= REG_FIELD(off + 0x4, bit, bit),	\
> +	  .status	= REG_FIELD(off + 0x8, bit, bit), }
> +
> +struct hisi_reset_channel_data {
> +	struct reg_field enable;
> +	struct reg_field disable;
> +	struct reg_field status;
> +};

Are you expecting the bits to be at different positions in the
enable/disable/status registers? How about just

#define HISI_RST_SEP(off, _bit)		\
	{ .enable_reg	= (off),	\
	  .disable_reg	= (off) + 0x4,	\
	  .status_reg	= (off) + 0x8,	\
	  .bit		= (_bit), }

struct hisi_reset_channel_data {
	unsigned int enable_reg;
	unsigned int disable_reg;
	unsigned int status_reg;
	unsigned int bit;
};

as those struct reg_field are not accessed via regmap_field_* functions
anyway.

> +
> +struct hisi_reset_controller_data {
> +	int nr_channels;
> +	const struct hisi_reset_channel_data *channels;
> +};
> +
> +int hisi_reset_probe(struct platform_device *pdev);
> +
> +#endif /* __HISILICON_RESET_H */

regards
Philipp


--
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/6] reset: hisilicon: add reset core
From: Philipp Zabel @ 2016-11-22 10:22 UTC (permalink / raw)
  To: Zhangfei Gao
  Cc: Rob Herring, haojian.zhuang-QSEj5FYQhm4dnm+yROfE0A,
	xuwei5-C8/M+/jPZTeaMJb+Lgu22Q, Chen Feng, Xinliang Liu, Xia Qing,
	Jiancheng Xue, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479800961-6249-2-git-send-email-zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Hi Zhangfei,

Am Dienstag, den 22.11.2016, 15:49 +0800 schrieb Zhangfei Gao:
> reset.c will be shared by hisilicon chips like hi3660 and hi6220
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  drivers/reset/Makefile           |   2 +-
>  drivers/reset/hisilicon/Makefile |   1 +
>  drivers/reset/hisilicon/reset.c  | 108 +++++++++++++++++++++++++++++++++++++++
>  drivers/reset/hisilicon/reset.h  |  37 ++++++++++++++
>  4 files changed, 147 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/reset/hisilicon/reset.c
>  create mode 100644 drivers/reset/hisilicon/reset.h
> 
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index bbe7026..7e3dc4e 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -1,8 +1,8 @@
>  obj-y += core.o
> -obj-y += hisilicon/
>  obj-$(CONFIG_ARCH_STI) += sti/
>  obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
>  obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
> +obj-$(CONFIG_ARCH_HISI) += hisilicon/
>  obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
>  obj-$(CONFIG_RESET_MESON) += reset-meson.o
>  obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
> diff --git a/drivers/reset/hisilicon/Makefile b/drivers/reset/hisilicon/Makefile
> index c932f86..df511f5 100644
> --- a/drivers/reset/hisilicon/Makefile
> +++ b/drivers/reset/hisilicon/Makefile
> @@ -1 +1,2 @@
> +obj-y	+= reset.o
>  obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o
> diff --git a/drivers/reset/hisilicon/reset.c b/drivers/reset/hisilicon/reset.c
> new file mode 100644
> index 0000000..c4971c9
> --- /dev/null
> +++ b/drivers/reset/hisilicon/reset.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright (c) 2016-2017 Linaro Ltd.
> + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
> + *
> + * 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/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +
> +#include "reset.h"
> +
> +struct hisi_reset_controller {
> +	struct reset_controller_dev rst;
> +	const struct hisi_reset_channel_data *channels;
> +	struct regmap *map;
> +};
> +
> +#define to_hisi_reset_controller(_rst) \
> +	container_of(_rst, struct hisi_reset_controller, rst)
> +
> +static int hisi_reset_program_hw(struct reset_controller_dev *rcdev,
> +				 unsigned long idx, bool assert)
> +{
> +	struct hisi_reset_controller *rc = to_hisi_reset_controller(rcdev);
> +	const struct hisi_reset_channel_data *ch;
> +
> +	if (idx >= rcdev->nr_resets)
> +		return -EINVAL;
> +
> +	ch = &rc->channels[idx];
> +
> +	if (assert)
> +		return regmap_write(rc->map, ch->enable.reg,
> +				    GENMASK(ch->enable.msb, ch->enable.lsb));
> +	else
> +		return regmap_write(rc->map, ch->disable.reg,
> +				    GENMASK(ch->disable.msb, ch->disable.lsb));

These fields are always 1-bit wide and you are not using the
regmap_field functions to access them, so I'd suggest to remove the
struct reg_field indirection and overhead and just write

	if (assert)
		return regmap_write(rc->map, ch->enable_reg, ch->bit);
	else
		return regmap_write(rc->map, ch->disable_reg, ch->bit);

here.

> +}
> +
> +static int hisi_reset_assert(struct reset_controller_dev *rcdev,
> +			     unsigned long idx)
> +{
> +	return hisi_reset_program_hw(rcdev, idx, true);
> +}
> +
> +static int hisi_reset_deassert(struct reset_controller_dev *rcdev,
> +			       unsigned long idx)
> +{
> +	return hisi_reset_program_hw(rcdev, idx, false);
> +}
> +
> +static int hisi_reset_dev(struct reset_controller_dev *rcdev,
> +			  unsigned long idx)
> +{
> +	int err;
> +
> +	err = hisi_reset_assert(rcdev, idx);
> +	if (err)
> +		return err;
> +
> +	return hisi_reset_deassert(rcdev, idx);
> +}
> +
> +static struct reset_control_ops hisi_reset_ops = {
> +	.reset    = hisi_reset_dev,
> +	.assert   = hisi_reset_assert,
> +	.deassert = hisi_reset_deassert,
> +};
> +
> +int hisi_reset_probe(struct platform_device *pdev)
> +{
> +	struct hisi_reset_controller *rc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct hisi_reset_controller_data *d;
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +
> +	match = of_match_device(dev->driver->of_match_table, dev);
> +	if (!match || !match->data)
> +		return -EINVAL;
> +
> +	d = (struct hisi_reset_controller_data *)match->data;
> +	rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
> +	if (!rc)
> +		return -ENOMEM;
> +
> +	rc->map = syscon_regmap_lookup_by_phandle(np, "hisi,rst-syscon");
> +	if (IS_ERR(rc->map)) {
> +		dev_err(dev, "failed to get hisi,rst-syscon\n");
> +		return PTR_ERR(rc->map);
> +	}
> +
> +	rc->rst.ops = &hisi_reset_ops,
> +	rc->rst.of_node = np;
> +	rc->rst.nr_resets = d->nr_channels;
> +	rc->channels = d->channels;
> +
> +	return reset_controller_register(&rc->rst);
> +}
> +EXPORT_SYMBOL_GPL(hisi_reset_probe);
> diff --git a/drivers/reset/hisilicon/reset.h b/drivers/reset/hisilicon/reset.h
> new file mode 100644
> index 0000000..77259ee
> --- /dev/null
> +++ b/drivers/reset/hisilicon/reset.h
> @@ -0,0 +1,37 @@
> +/*
> + * Copyright (c) 2016-2017 Linaro Ltd.
> + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
> + *
> + * 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 __HISILICON_RESET_H
> +#define __HISILICON_RESET_H
> +
> +#include <linux/device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset-controller.h>
> +
> +/* reset separated register offset is 0x4 */
> +#define HISI_RST_SEP(off, bit)					\
> +	{ .enable	= REG_FIELD(off, bit, bit),		\
> +	  .disable	= REG_FIELD(off + 0x4, bit, bit),	\
> +	  .status	= REG_FIELD(off + 0x8, bit, bit), }
> +
> +struct hisi_reset_channel_data {
> +	struct reg_field enable;
> +	struct reg_field disable;
> +	struct reg_field status;
> +};

Are you expecting the bits to be at different positions in the
enable/disable/status registers? How about just

#define HISI_RST_SEP(off, _bit)		\
	{ .enable_reg	= (off),	\
	  .disable_reg	= (off) + 0x4,	\
	  .status_reg	= (off) + 0x8,	\
	  .bit		= (_bit), }

struct hisi_reset_channel_data {
	unsigned int enable_reg;
	unsigned int disable_reg;
	unsigned int status_reg;
	unsigned int bit;
};

as those struct reg_field are not accessed via regmap_field_* functions
anyway.

> +
> +struct hisi_reset_controller_data {
> +	int nr_channels;
> +	const struct hisi_reset_channel_data *channels;
> +};
> +
> +int hisi_reset_probe(struct platform_device *pdev);
> +
> +#endif /* __HISILICON_RESET_H */

regards
Philipp

--
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 V3 2/2] pinctrl: tegra: Add driver to configure voltage and power of io pads
From: Laxman Dewangan @ 2016-11-22 10:20 UTC (permalink / raw)
  To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, swarren-3lzwWm7+Weoh9ZMKESR00Q,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w
  Cc: mark.rutland-5wv7dgnIgG8, gnurou-Re5JQEeQqe8AvxtiuMwx3w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	jonathanh-DDmLM1+adcrQT0dZR+AlfA, Laxman Dewangan
In-Reply-To: <1479810013-29894-1-git-send-email-ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

NVIDIA Tegra124 and later SoCs support the multi-voltage level and
low power state of some of its IO pads. The IO pads can work in
the voltage of the 1.8V and 3.3V of IO voltage from IO power rail
sources. When IO interfaces are not used then IO pads can be
configure in low power state to reduce the power consumption from
that IO pads.

On Tegra124, the voltage level of IO power rail source is auto
detected by hardware(SoC) and hence it is only require to configure
in low power mode if IO pads are not used.

On T210 onwards, the auto-detection of voltage level from IO power
rail is removed from SoC and hence SW need to configure the PMC
register explicitly to set proper voltage in IO pads based on
IO rail power source voltage.

This driver adds the IO pad driver to configure the power state and
IO pad voltage based on the usage and power tree via pincontrol
framework. The configuration can be static and dynamic.

Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

---
Changes from V1:
- Dropped the custom properties to set pad voltage and use regulator.
- Added support for regulator to get vottage in boot and configure IO
  pad voltage.
- Add support for callback to handle regulator notification and configure
  IO pad voltage based on voltage change.

Changes from V2:
 Mostly nit changes per Jon's feedback i.e. use macros for voltage, added
 comment on macros, reduce the structure and variable name size, optimise
 number of variables, and allocate memory for regulator info when it needed.
---
 drivers/pinctrl/tegra/Kconfig                |  12 +
 drivers/pinctrl/tegra/Makefile               |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c | 530 +++++++++++++++++++++++++++
 3 files changed, 543 insertions(+)
 create mode 100644 drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c

diff --git a/drivers/pinctrl/tegra/Kconfig b/drivers/pinctrl/tegra/Kconfig
index 24e20cc..6004e5c 100644
--- a/drivers/pinctrl/tegra/Kconfig
+++ b/drivers/pinctrl/tegra/Kconfig
@@ -23,6 +23,18 @@ config PINCTRL_TEGRA210
 	bool
 	select PINCTRL_TEGRA
 
+config PINCTRL_TEGRA_IO_PAD
+	bool "Tegra IO pad Control Driver"
+	depends on ARCH_TEGRA && REGULATOR
+	select PINCONF
+	select PINMUX
+	help
+	  NVIDIA Tegra124/210 SoC has IO pads which supports multi-voltage
+	  level of interfacing and deep power down mode of IO pads. The
+	  voltage of IO pads are SW configurable based on IO rail of that
+	  pads on T210. This driver provides the interface to change IO pad
+	  voltage and power state via pincontrol interface.
+
 config PINCTRL_TEGRA_XUSB
 	def_bool y if ARCH_TEGRA
 	select GENERIC_PHY
diff --git a/drivers/pinctrl/tegra/Makefile b/drivers/pinctrl/tegra/Makefile
index d9ea2be..3ebaaa2 100644
--- a/drivers/pinctrl/tegra/Makefile
+++ b/drivers/pinctrl/tegra/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_PINCTRL_TEGRA30)		+= pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_TEGRA114)		+= pinctrl-tegra114.o
 obj-$(CONFIG_PINCTRL_TEGRA124)		+= pinctrl-tegra124.o
 obj-$(CONFIG_PINCTRL_TEGRA210)		+= pinctrl-tegra210.o
+obj-$(CONFIG_PINCTRL_TEGRA_IO_PAD)	+= pinctrl-tegra-io-pad.o
 obj-$(CONFIG_PINCTRL_TEGRA_XUSB)	+= pinctrl-tegra-xusb.o
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c b/drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c
new file mode 100644
index 0000000..1613e13
--- /dev/null
+++ b/drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c
@@ -0,0 +1,530 @@
+/*
+ * pinctrl-tegra-io-pad: IO PAD driver for configuration of IO rail and deep
+ *			 Power Down mode via pinctrl framework.
+ *
+ * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <soc/tegra/pmc.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinctrl-utils.h"
+
+#define TEGRA_IO_RAIL_1800000UV 1800000
+#define TEGRA_IO_RAIL_3300000UV 3300000
+
+/* Covert IO voltage to IO pad voltage enum */
+#define tegra_io_uv_to_io_pads_uv(io_uv)				\
+		(((io_uv) == TEGRA_IO_RAIL_1800000UV) ?			\
+		  TEGRA_IO_PAD_1800000UV : TEGRA_IO_PAD_3300000UV)
+
+#define tegra_io_voltage_is_valid(io_uv)			\
+	({ typeof(io_uv) io_uv_ = (io_uv);			\
+	    ((io_uv_ == TEGRA_IO_RAIL_1800000UV) ||		\
+	     (io_uv_ == TEGRA_IO_RAIL_3300000UV)); })
+
+struct tegra_io_pads_cfg {
+	const char *name;
+	const unsigned int pins[1];
+	const char *vsupply;
+	enum tegra_io_pad id;
+	bool supports_low_power;
+};
+
+struct tegra_io_pads_soc_data {
+	const struct tegra_io_pads_cfg *cfg;
+	int num_cfg;
+	const struct pinctrl_pin_desc *desc;
+	int num_desc;
+};
+
+struct tegra_io_pads_info {
+	struct device *dev;
+	struct pinctrl_dev *pctl;
+	const struct tegra_io_pads_soc_data *soc_data;
+};
+
+struct tegra_io_pads_regulator_info {
+	struct tegra_io_pads_info *tiopi;
+	const struct tegra_io_pads_cfg *cfg;
+	struct regulator *regulator;
+	struct notifier_block regulator_nb;
+};
+
+static int tegra_io_pads_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct tegra_io_pads_info *tiopi = pinctrl_dev_get_drvdata(pctldev);
+
+	return tiopi->soc_data->num_cfg;
+}
+
+static const char *tegra_io_pads_pinctrl_get_group_name(
+		struct pinctrl_dev *pctldev, unsigned int group)
+{
+	struct tegra_io_pads_info *tiopi = pinctrl_dev_get_drvdata(pctldev);
+
+	return tiopi->soc_data->cfg[group].name;
+}
+
+static int tegra_io_pads_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+						unsigned int group,
+						const unsigned int **pins,
+						unsigned int *num_pins)
+{
+	struct tegra_io_pads_info *tiopi = pinctrl_dev_get_drvdata(pctldev);
+
+	*pins = tiopi->soc_data->cfg[group].pins;
+	*num_pins = 1;
+
+	return 0;
+}
+
+static const struct pinctrl_ops tegra_io_pads_pinctrl_ops = {
+	.get_groups_count	= tegra_io_pads_pinctrl_get_groups_count,
+	.get_group_name		= tegra_io_pads_pinctrl_get_group_name,
+	.get_group_pins		= tegra_io_pads_pinctrl_get_group_pins,
+	.dt_node_to_map		= pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map		= pinctrl_utils_free_map,
+};
+
+static int tegra_io_pads_pinconf_get(struct pinctrl_dev *pctldev,
+				     unsigned int pin, unsigned long *config)
+{
+	struct tegra_io_pads_info *tiopi = pinctrl_dev_get_drvdata(pctldev);
+	int param = pinconf_to_config_param(*config);
+	const struct tegra_io_pads_cfg *cfg = &tiopi->soc_data->cfg[pin];
+	int arg = 0;
+	int ret;
+
+	switch (param) {
+	case PIN_CONFIG_LOW_POWER_MODE:
+		if (!cfg->supports_low_power) {
+			dev_err(tiopi->dev,
+				"IO pad %s does not support low power\n",
+				cfg->name);
+			return -EINVAL;
+		}
+
+		ret = tegra_io_pad_power_get_status(cfg->id);
+		if (ret < 0)
+			return ret;
+		arg = !ret;
+		break;
+
+	default:
+		dev_err(tiopi->dev, "The parameter %d not supported\n", param);
+		return -EINVAL;
+	}
+
+	*config = pinconf_to_config_packed(param, (u16)arg);
+
+	return 0;
+}
+
+static int tegra_io_pads_pinconf_set(struct pinctrl_dev *pctldev,
+				     unsigned int pin, unsigned long *configs,
+				    unsigned int num_configs)
+{
+	struct tegra_io_pads_info *tiopi = pinctrl_dev_get_drvdata(pctldev);
+	const struct tegra_io_pads_cfg *cfg = &tiopi->soc_data->cfg[pin];
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		int ret;
+		int param = pinconf_to_config_param(configs[i]);
+		u16 param_val = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_LOW_POWER_MODE:
+			if (!cfg->supports_low_power) {
+				dev_err(tiopi->dev,
+					"IO pad %s does not support low power\n",
+					cfg->name);
+				return -EINVAL;
+			}
+			if (param_val)
+				ret = tegra_io_pad_power_disable(cfg->id);
+			else
+				ret = tegra_io_pad_power_enable(cfg->id);
+			if (ret < 0) {
+				dev_err(tiopi->dev,
+					"Failed to set DPD %d of io-pad %s: %d\n",
+					param_val, cfg->name, ret);
+				return ret;
+			}
+			break;
+
+		default:
+			dev_err(tiopi->dev, "The parameter %d not supported\n",
+				param);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops tegra_io_pads_pinconf_ops = {
+	.pin_config_get = tegra_io_pads_pinconf_get,
+	.pin_config_set = tegra_io_pads_pinconf_set,
+};
+
+static struct pinctrl_desc tegra_io_pads_pinctrl_desc = {
+	.name = "pinctrl-tegra-io-pads",
+	.pctlops = &tegra_io_pads_pinctrl_ops,
+	.confops = &tegra_io_pads_pinconf_ops,
+};
+
+static int tegra_io_pads_rail_change_notify_cb(struct notifier_block *nb,
+					       unsigned long event, void *data)
+{
+	struct tegra_io_pads_regulator_info *rinfo;
+	struct pre_voltage_change_data *vdata;
+	unsigned long int io_volt_uv;
+	enum tegra_io_pad_voltage pad_volt;
+	int ret;
+
+	rinfo = container_of(nb, struct tegra_io_pads_regulator_info,
+			     regulator_nb);
+
+	switch (event) {
+	case REGULATOR_EVENT_PRE_VOLTAGE_CHANGE:
+		vdata = data;
+
+		if (!tegra_io_voltage_is_valid(vdata->old_uV) ||
+		    !tegra_io_voltage_is_valid(vdata->min_uV)) {
+			dev_err(rinfo->tiopi->dev,
+				"IO rail %s voltage is not 1.8/3.3V: %lu:%lu\n",
+				rinfo->cfg->name, vdata->old_uV, vdata->min_uV);
+			return -EINVAL;
+		}
+
+		/**
+		 * Change IO pad voltage before changing IO voltage when it
+		 * changes from 1.8V to 3.3V
+		 */
+		if (vdata->min_uV == TEGRA_IO_RAIL_1800000UV)
+			break;
+
+		ret = tegra_io_pad_set_voltage(rinfo->cfg->id,
+					       TEGRA_IO_PAD_3300000UV);
+		if (ret < 0) {
+			dev_err(rinfo->tiopi->dev,
+				"Failed to set voltage %lu of pad %s: %d\n",
+				vdata->min_uV, rinfo->cfg->name, ret);
+			return ret;
+		}
+		break;
+
+	case REGULATOR_EVENT_VOLTAGE_CHANGE:
+		io_volt_uv = (unsigned long)data;
+		ret = tegra_io_pad_get_voltage(rinfo->cfg->id);
+		if (ret < 0) {
+			dev_err(rinfo->tiopi->dev,
+				"Failed to get IO pad voltage: %d\n", ret);
+			return ret;
+		}
+
+		if (!tegra_io_voltage_is_valid(io_volt_uv)) {
+			dev_err(rinfo->tiopi->dev,
+				"IO rail %s voltage is not 1.8/3.3V: %lu\n",
+				rinfo->cfg->name, io_volt_uv);
+			return -EINVAL;
+		}
+
+		/*
+		 * If IO pad configuration matching with IO rail voltage then
+		 * do nothing.
+		 */
+		if (((io_volt_uv == TEGRA_IO_RAIL_1800000UV) &&
+		     (ret == TEGRA_IO_PAD_1800000UV)) ||
+		     ((io_volt_uv == TEGRA_IO_RAIL_3300000UV) &&
+		      (ret == TEGRA_IO_PAD_3300000UV)))
+			break;
+
+		ret = tegra_io_pad_set_voltage(rinfo->cfg->id,
+					       TEGRA_IO_PAD_1800000UV);
+		if (ret < 0) {
+			dev_err(rinfo->tiopi->dev,
+				"Failed to set voltage %lu of pad %s: %d\n",
+				vdata->min_uV, rinfo->cfg->name, ret);
+			return ret;
+		}
+		break;
+
+	case REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE:
+		io_volt_uv = (unsigned long)data;
+
+		if (!tegra_io_voltage_is_valid(io_volt_uv)) {
+			dev_err(rinfo->tiopi->dev,
+				"IO rail %s voltage is not 1.8/3.3V: %lu\n",
+				rinfo->cfg->name, io_volt_uv);
+			return -EINVAL;
+		}
+
+		pad_volt = tegra_io_uv_to_io_pads_uv(io_volt_uv);
+		ret = tegra_io_pad_set_voltage(rinfo->cfg->id, pad_volt);
+		if (ret < 0) {
+			dev_err(rinfo->tiopi->dev,
+				"Failed to set voltage %lu of pad %s: %d\n",
+				io_volt_uv, rinfo->cfg->name, ret);
+			return ret;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int tegra_io_pads_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct platform_device_id *id = platform_get_device_id(pdev);
+	const struct tegra_io_pads_soc_data *soc_data =
+			(const struct tegra_io_pads_soc_data *)id->driver_data;
+	struct tegra_io_pads_info *tiopi;
+	int ret, i;
+
+	if (!pdev->dev.parent->of_node) {
+		dev_err(dev, "PMC should be register from DT\n");
+		return -ENODEV;
+	}
+
+	tiopi = devm_kzalloc(dev, sizeof(*tiopi), GFP_KERNEL);
+	if (!tiopi)
+		return -ENOMEM;
+
+	tiopi->dev = &pdev->dev;
+	pdev->dev.of_node = pdev->dev.parent->of_node;
+	tiopi->soc_data = soc_data;
+
+	for (i = 0; i < soc_data->num_cfg; ++i) {
+		struct tegra_io_pads_regulator_info *rinfo;
+		enum tegra_io_pad_voltage pad_volt;
+		int io_volt_uv;
+
+		if (!soc_data->cfg[i].vsupply)
+			continue;
+
+		rinfo = devm_kzalloc(dev, sizeof(*rinfo), GFP_KERNEL);
+		if (!rinfo)
+			return -ENOMEM;
+
+		rinfo->tiopi = tiopi;
+		rinfo->cfg = &soc_data->cfg[i];
+
+		rinfo->regulator = devm_regulator_get_optional(dev,
+						soc_data->cfg[i].vsupply);
+		if (IS_ERR(rinfo->regulator)) {
+			ret = PTR_ERR(rinfo->regulator);
+			if (ret == -EPROBE_DEFER)
+				return ret;
+			continue;
+		}
+
+		io_volt_uv = regulator_get_voltage(rinfo->regulator);
+		if (io_volt_uv < 0) {
+			dev_err(dev, "Failed to get voltage for rail %s: %d\n",
+				soc_data->cfg[i].vsupply, io_volt_uv);
+			return ret;
+		}
+
+		if (!tegra_io_voltage_is_valid(io_volt_uv)) {
+			dev_err(dev, "IO rail %s voltage is not 1.8/3.3V: %d\n",
+				soc_data->cfg[i].vsupply, io_volt_uv);
+			continue;
+		}
+
+		pad_volt = tegra_io_uv_to_io_pads_uv(io_volt_uv);
+		ret = tegra_io_pad_set_voltage(soc_data->cfg[i].id, pad_volt);
+		if (ret < 0) {
+			dev_err(dev, "Failed to set voltage %d of pad %s: %d\n",
+				io_volt_uv, soc_data->cfg[i].name, ret);
+			return ret;
+		}
+
+		rinfo->regulator_nb.notifier_call =
+					tegra_io_pads_rail_change_notify_cb;
+		ret = devm_regulator_register_notifier(rinfo->regulator,
+						       &rinfo->regulator_nb);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register regulator %s notifier: %d\n",
+				soc_data->cfg[i].name, ret);
+			return ret;
+		}
+	}
+
+	tegra_io_pads_pinctrl_desc.pins = tiopi->soc_data->desc;
+	tegra_io_pads_pinctrl_desc.npins = tiopi->soc_data->num_desc;
+	platform_set_drvdata(pdev, tiopi);
+
+	tiopi->pctl = devm_pinctrl_register(dev, &tegra_io_pads_pinctrl_desc,
+					    tiopi);
+	if (IS_ERR(tiopi->pctl)) {
+		ret = PTR_ERR(tiopi->pctl);
+		dev_err(dev, "Failed to register io-pad pinctrl driver: %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#define TEGRA124_PAD_INFO_TABLE(_entry_)			\
+	_entry_(0, "audio", AUDIO, true, NULL),			\
+	_entry_(1, "bb", BB, true, NULL),			\
+	_entry_(2, "cam", CAM, true, NULL),			\
+	_entry_(3, "comp", COMP, true, NULL),			\
+	_entry_(4, "csia", CSIA, true, NULL),			\
+	_entry_(5, "csib", CSIB, true, NULL),			\
+	_entry_(6, "csie", CSIE, true, NULL),			\
+	_entry_(7, "dsi", DSI, true, NULL),			\
+	_entry_(8, "dsib", DSIB, true, NULL),			\
+	_entry_(9, "dsic", DSIC, true, NULL),			\
+	_entry_(10, "dsid", DSID, true, NULL),			\
+	_entry_(11, "hdmi", HDMI, true, NULL),			\
+	_entry_(12, "hsic", HSIC, true, NULL),			\
+	_entry_(13, "hv", HV, true, NULL),			\
+	_entry_(14, "lvds", LVDS, true, NULL),			\
+	_entry_(15, "mipi-bias", MIPI_BIAS, true, NULL),	\
+	_entry_(16, "nand", NAND, true, NULL),			\
+	_entry_(17, "pex-bias", PEX_BIAS, true, NULL),		\
+	_entry_(18, "pex-clk1", PEX_CLK1, true, NULL),		\
+	_entry_(19, "pex-clk2", PEX_CLK2, true, NULL),		\
+	_entry_(20, "pex-ctrl", PEX_CNTRL, true, NULL),		\
+	_entry_(21, "sdmmc1", SDMMC1, true, NULL),		\
+	_entry_(22, "sdmmc3", SDMMC3, true, NULL),		\
+	_entry_(23, "sdmmc4", SDMMC4, true, NULL),		\
+	_entry_(24, "sys-ddc", SYS_DDC, true, NULL),		\
+	_entry_(25, "uart", UART, true, NULL),			\
+	_entry_(26, "usb0", USB0, true, NULL),			\
+	_entry_(27, "usb1", USB1, true, NULL),			\
+	_entry_(28, "usb2", USB2, true, NULL),			\
+	_entry_(29, "usb-bias", USB_BIAS, true, NULL)
+
+#define TEGRA210_PAD_INFO_TABLE(_entry_)			\
+	_entry_(0, "audio", AUDIO, true, "vddio-audio"),	\
+	_entry_(1, "audio-hv", AUDIO_HV, true, "vddio-audio-hv"), \
+	_entry_(2, "cam", CAM, true, "vddio-cam"),		\
+	_entry_(3, "csia", CSIA, true, NULL),			\
+	_entry_(4, "csib", CSIB, true, NULL),			\
+	_entry_(5, "csic", CSIC, true, NULL),			\
+	_entry_(6, "csid", CSID, true, NULL),			\
+	_entry_(7, "csie", CSIE, true, NULL),			\
+	_entry_(8, "csif", CSIF, true, NULL),			\
+	_entry_(9, "dbg", DBG, true, "vddio-dbg"),		\
+	_entry_(10, "debug-nonao", DEBUG_NONAO, true, NULL),	\
+	_entry_(11, "dmic", DMIC, true, "vddio-dmic"),		\
+	_entry_(12, "dp", DP, true, NULL),			\
+	_entry_(13, "dsi", DSI, true, NULL),			\
+	_entry_(14, "dsib", DSIB, true, NULL),			\
+	_entry_(15, "dsic", DSIC, true, NULL),			\
+	_entry_(16, "dsid", DSID, true, NULL),			\
+	_entry_(17, "emmc", SDMMC4, true, NULL),		\
+	_entry_(18, "emmc2", EMMC2, true, NULL),		\
+	_entry_(19, "gpio", GPIO, true, "vddio-gpio"),		\
+	_entry_(20, "hdmi", HDMI, true, NULL),			\
+	_entry_(21, "hsic", HSIC, true, NULL),			\
+	_entry_(22, "lvds", LVDS, true, NULL),			\
+	_entry_(23, "mipi-bias", MIPI_BIAS, true, NULL),	\
+	_entry_(24, "pex-bias", PEX_BIAS, true, NULL),		\
+	_entry_(25, "pex-clk1", PEX_CLK1, true, NULL),		\
+	_entry_(26, "pex-clk2", PEX_CLK2, true, NULL),		\
+	_entry_(27, "pex-ctrl", PEX_CNTRL, false, "vddio-pex-ctrl"), \
+	_entry_(28, "sdmmc1", SDMMC1, true, "vddio-sdmmc1"),	\
+	_entry_(29, "sdmmc3", SDMMC3, true, "vddio-sdmmc3"),	\
+	_entry_(30, "spi", SPI, true, "vddio-spi"),		\
+	_entry_(31, "spi-hv", SPI_HV, true, "vddio-spi-hv"),	\
+	_entry_(32, "uart", UART, true, "vddio-uart"),		\
+	_entry_(33, "usb0", USB0, true, NULL),			\
+	_entry_(34, "usb1", USB1, true, NULL),			\
+	_entry_(35, "usb2", USB2, true, NULL),			\
+	_entry_(36, "usb3", USB3, true, NULL),			\
+	_entry_(37, "usb-bias", USB_BIAS, true, NULL)
+
+#define TEGRA_IO_PAD_INFO(_pin, _name, _id, _lpstate, _vsupply)	\
+	{							\
+		.name = _name,					\
+		.pins = {(_pin)},				\
+		.id = TEGRA_IO_PAD_##_id,			\
+		.vsupply = (_vsupply),				\
+		.supports_low_power = (_lpstate),		\
+	}
+
+static const struct tegra_io_pads_cfg tegra124_io_pads_cfg_info[] = {
+	TEGRA124_PAD_INFO_TABLE(TEGRA_IO_PAD_INFO),
+};
+
+static const struct tegra_io_pads_cfg tegra210_io_pads_cfg_info[] = {
+	TEGRA210_PAD_INFO_TABLE(TEGRA_IO_PAD_INFO),
+};
+
+#define TEGRA_IO_PAD_DESC(_pin, _name, _id, _lpstate, _vsupply)	\
+	PINCTRL_PIN(_pin, _name)
+
+static const struct pinctrl_pin_desc tegra124_io_pads_pinctrl_desc[] = {
+	TEGRA124_PAD_INFO_TABLE(TEGRA_IO_PAD_DESC),
+};
+
+static const struct pinctrl_pin_desc tegra210_io_pads_pinctrl_desc[] = {
+	TEGRA210_PAD_INFO_TABLE(TEGRA_IO_PAD_DESC),
+};
+
+static const struct tegra_io_pads_soc_data tegra124_io_pad_soc_data = {
+	.desc		= tegra124_io_pads_pinctrl_desc,
+	.num_desc	= ARRAY_SIZE(tegra124_io_pads_pinctrl_desc),
+	.cfg		= tegra124_io_pads_cfg_info,
+	.num_cfg	= ARRAY_SIZE(tegra124_io_pads_cfg_info),
+};
+
+static const struct tegra_io_pads_soc_data tegra210_io_pad_soc_data = {
+	.desc		= tegra210_io_pads_pinctrl_desc,
+	.num_desc	= ARRAY_SIZE(tegra210_io_pads_pinctrl_desc),
+	.cfg		= tegra210_io_pads_cfg_info,
+	.num_cfg	= ARRAY_SIZE(tegra210_io_pads_cfg_info),
+};
+
+static const struct platform_device_id tegra_io_pads_dev_id[] = {
+	{
+		.name = "pinctrl-t124-io-pad",
+		.driver_data = (kernel_ulong_t)&tegra124_io_pad_soc_data,
+	}, {
+		.name = "pinctrl-t210-io-pad",
+		.driver_data = (kernel_ulong_t)&tegra210_io_pad_soc_data,
+	}, {
+	},
+};
+MODULE_DEVICE_TABLE(platform, tegra_io_pads_dev_id);
+
+static struct platform_driver tegra_io_pads_pinctrl_driver = {
+	.driver		= {
+		.name	= "pinctrl-tegra-io-pad",
+	},
+	.probe		= tegra_io_pads_pinctrl_probe,
+	.id_table	= tegra_io_pads_dev_id,
+};
+
+module_platform_driver(tegra_io_pads_pinctrl_driver);
+
+MODULE_DESCRIPTION("NVIDIA TEGRA IO pad Control Driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4

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

^ permalink raw reply related

* [PATCH V3 1/2] pinctrl: tegra: Add DT binding for io pads control
From: Laxman Dewangan @ 2016-11-22 10:20 UTC (permalink / raw)
  To: linus.walleij, robh+dt, swarren, thierry.reding
  Cc: mark.rutland, gnurou, linux-gpio, devicetree, linux-tegra,
	linux-kernel, joe, jonathanh, Laxman Dewangan
In-Reply-To: <1479810013-29894-1-git-send-email-ldewangan@nvidia.com>

NVIDIA Tegra124 and later SoCs support the multi-voltage level and
low power state of some of its IO pads. The IO pads can work in
the voltage of the 1.8V and 3.3V of IO voltage from IO power rail
sources. When IO interfaces are not used then IO pads can be
configure in low power state to reduce the power consumption from
that IO pads.

On Tegra124, the voltage level of IO power rail source is auto
detected by hardware(SoC) and hence it is only require to configure
in low power mode if IO pads are not used.

On T210 onwards, the auto-detection of voltage level from IO power
rail is removed from SoC and hence SW need to configure the PMC
register explicitly to set proper voltage in IO pads based on
IO rail power source voltage.

Add DT binding document for detailing the DT properties for
configuring IO pads voltage levels and its power state.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>

---
Changes from V1:
 New in series based on pinctrl driver requirement.

Changes from V2:
 Updated the statement to say 1.8V and 3.3V as nominal voltage.
 Corrected DT example by adding -supply and taken care of V2 review
 from Rob.
---
 .../bindings/pinctrl/nvidia,tegra-io-pad.txt       | 126 +++++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra-io-pad.txt

diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra-io-pad.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra-io-pad.txt
new file mode 100644
index 0000000..a88c484
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra-io-pad.txt
@@ -0,0 +1,126 @@
+NVIDIA Tegra PMC IO pad controller
+
+NVIDIA Tegra124 and later SoCs support the multi-voltage level and low power
+state of some of its IO pads. When IO interface are not used then IO pads can
+be configure in low power state to reduce the power from that IO pads. The IO
+pads can work in the nominal IO voltage of 1.8V and 3.3V from power rail
+sources.
+
+On Tegra124, the voltage of IO power rail source is auto detected by SoC and
+hence it is only require to configure in low power mode if IO pads are not
+used.
+
+On T210 onwards, the HW based auto-detection for IO voltage is removed and
+hence SW need to configure the PMC register explicitly, to set proper voltage
+in IO pads, based on IO rail power source voltage.
+
+The voltage configurations and low power state of IO pads should be done in
+boot if it is not going to change otherwise dynamically based on IO rail
+voltage on that IO pads and usage of IO pads
+
+The DT property of the IO pads must be under the node of pmc i.e.
+pmc@7000e400 for Tegra124 onwards.
+
+Please refer to <pinctrl-bindings.txt> in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+Tegra's pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for an
+IO pads, or a list of IO pads. This configuration can include the voltage and
+power enable/disable control
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content. Each subnode only affects those
+parameters that are explicitly listed. Unspecified is represented as an absent
+property,
+
+See the TRM to determine which properties and values apply to each IO pads.
+
+Required subnode-properties:
+==========================
+- pins : An array of strings. Each string contains the name of an IO pads. Valid
+	 values for these names are listed below.
+
+Optional subnode-properties:
+==========================
+Following properties are supported from generic pin configuration explained
+in <dt-bindings/pinctrl/pinctrl-binding.txt>.
+low-power-enable:		enable low power mode.
+low-power-disable:		disable low power mode.
+
+Valid values for pin for T124 are:
+	audio, bb, cam, comp, csia, csib, csie, dsi, dsib, dsic, dsid, hdmi,
+	hsic, hv, lvds, mipi-bias, nand, pex-bias, pex-clk1, pex-clk2,
+	pex-ctrl, sdmmc1, sdmmc3, sdmmc4, sys-ddc, uart, usb0, usb1, usb2,
+	usb-bias
+
+Valid values for pin for T210 are:
+	audio, audio-hv, cam, csia, csib, csic, csid, csie, csif,
+	dbg, debug-nonao, dmic, dp, dsi, dsib, dsic, dsid, emmc, emmc2,
+	gpio, hdmi, hsic, lvds, mipi-bias, pex-bias, pex-clk1, pex-clk2,
+	pex-ctrl, sdmmc1, sdmmc3, spi, spi-hv, uart, usb-bias, usb0,
+	usb1, usb2, usb3.
+
+To find out the IO rail voltage for setting the voltage of IO pad by SW,
+the regulator supply handle must provided from the DT and it is explained
+in the regulator DT binding document
+	<devicetree/bindings/regulator/regulator.txt>.
+For example, for GPIO rail the supply name is vddio-gpio and regulator
+handle is supplied from DT as
+	vddio-gpio-supply = <&regulator_xyz>;
+
+For T210, following IO pads support the 1.8V/3.3V and the corresponding
+IO voltage pin names are as follows:
+	audio -> vddio-audio
+	audio-hv -> vddio-audio-hv
+	cam ->vddio-cam
+	dbg -> vddio-dbg
+	dmic -> vddio-dmic
+	gpio -> vddio-gpio
+	pex-ctrl -> vddio-pex-ctrl
+	sdmmc1 -> vddio-sdmmc1
+	sdmmc3 -> vddio-sdmmc3
+	spi -> vddio-spi
+	spi-hv -> vddio-spi-hv
+	uart -> vddio-uart
+
+Example:
+	i2c@7000d000 {
+		pmic@3c {
+			regulators {
+				vddio_sdmmc1: ldo2 {
+					/* Regulator entries for LDO2 */
+				};
+
+				vdd_cam: ldo3 {
+					/* Regulator entries for LDO3 */
+				};
+			};
+		};
+	};
+
+	pmc@7000e400 {
+		vddio-cam-supply = <&vdd_cam>;
+		vddio-sdmmc1-supply = <&vddio_sdmmc1>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&tegra_io_pad_volt_default>;
+		tegra_io_pad_volt_default: common {
+			audio-hv {
+				pins = "audio-hv";
+				low-power-disable;
+			};
+
+			gpio {
+				pins = "gpio";
+				low-power-disable;
+			};
+
+			audio {
+				pins = "audio", "dmic", "sdmmc3";
+				low-power-enable;
+			};
+		};
+
+	};
-- 
2.1.4

^ permalink raw reply related

* [PATCH V3 0/2] pinctrl: tegra: Add support for IO pad control
From: Laxman Dewangan @ 2016-11-22 10:20 UTC (permalink / raw)
  To: linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, swarren-3lzwWm7+Weoh9ZMKESR00Q,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w
  Cc: mark.rutland-5wv7dgnIgG8, gnurou-Re5JQEeQqe8AvxtiuMwx3w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	jonathanh-DDmLM1+adcrQT0dZR+AlfA, Laxman Dewangan

NVIDIA Tegra124 and later SoCs support the multi-voltage level and 
low power state of some of its IO pads. The IO pads can work in
the voltage of the 1.8V and 3.3V of IO power rail sources. When IO
interface are not used then IO pads can be configure in low power
state to reduce the power from that IO pads.

This series add the support of configuration of IO pad via pinctrl
framework. The io pad driver uses the tegra PMC interface.

---
This driver was sent earlier for review along with soc/tegra pmc 
changes. During review, decided to first conclude in soc/tegra pmc 
patches and then review this.
    
Thierry applied the pmc patches in the private tree
        https://github.com/thierryreding/linux/tree/tegra186
and he wanted to have the patches for user of the new APIs so that
it can be pushed to mainline.
    
Sending the pinctrl driver. This needs Ack/reviewed from pinctrl subsystem
i.e. Linus Welleij to apply in the Thierry's T186 branch along with
PMC patches.

---
Changes from V1: 
- use the regulator framework to get the IO voltage instead of table from
  DT. The regulator handle is provided from DT. 

Changes from V2: 
- Nit fixes and variable/allocation optimisation as per review comment from
  V2.

Laxman Dewangan (2):
  pinctrl: tegra: Add DT binding for io pads control
  pinctrl: tegra: Add driver to configure voltage and power of io pads

 .../bindings/pinctrl/nvidia,tegra-io-pad.txt       | 126 +++++
 drivers/pinctrl/tegra/Kconfig                      |  12 +
 drivers/pinctrl/tegra/Makefile                     |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c       | 530 +++++++++++++++++++++
 4 files changed, 669 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra-io-pad.txt
 create mode 100644 drivers/pinctrl/tegra/pinctrl-tegra-io-pad.c

-- 
2.1.4

^ 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