Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] DTS: MCCMON6: IMX: Provide support for iMX6Q based Liebherr mccmon6 board
From: Lukasz Majewski @ 2016-12-26 23:19 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lukasz Majewski <l.majewski@majess.pl>
---
MCCMON6 board support depends on following patches:

1. "video: backlight: pwm_bl: Initialize fb_bl_on[x] and use_count during pwm_backlight_probe()"
	http://patchwork.ozlabs.org/patch/708844/

2. "pwm: imx: Provide atomic operation for IMX PWM driver"
	http://patchwork.ozlabs.org/patch/708847/ - http://patchwork.ozlabs.org/patch/708843/


---
 arch/arm/boot/dts/Makefile          |   1 +
 arch/arm/boot/dts/imx6q-mccmon6.dts | 469 ++++++++++++++++++++++++++++++++++++
 2 files changed, 470 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6q-mccmon6.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index c558ba7..7ce1080 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -382,6 +382,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
 	imx6q-h100.dtb \
 	imx6q-hummingboard.dtb \
 	imx6q-icore-rqs.dtb \
+	imx6q-mccmon6.dtb \
 	imx6q-marsboard.dtb \
 	imx6q-nitrogen6x.dtb \
 	imx6q-nitrogen6_max.dtb \
diff --git a/arch/arm/boot/dts/imx6q-mccmon6.dts b/arch/arm/boot/dts/imx6q-mccmon6.dts
new file mode 100644
index 0000000..7445d01
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-mccmon6.dts
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2016
+ *
+ * Author: Lukasz Majewski <l.majewski@majess.pl>
+ *
+ * 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.
+ *
+ */
+/dts-v1/;
+#include "imx6q.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pwm/pwm.h>
+
+/ {
+	model = "Monitor6 i.MX6 Quad Board";
+	compatible = "mccmon6", "fsl,imx6q";
+
+	memory {
+		reg = <0x10000000 0x80000000>;
+	};
+
+	ethernet0 {
+		status = "okay";
+	};
+
+	backlight_lvds: backlight {
+		compatible = "pwm-backlight";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_display>;
+		pwms = <&pwm2 0 5000000 PWM_POLARITY_INVERTED>;
+		brightness-levels = <  0   1   2   3   4   5   6   7   8   9
+				      10  11  12  13  14  15  16  17  18  19
+				      20  21  22  23  24  25  26  27  28  29
+				      30  31  32  33  34  35  36  37  38  39
+				      40  41  42  43  44  45  46  47  48  49
+				      50  51  52  53  54  55  56  57  58  59
+				      60  61  62  63  64  65  66  67  68  69
+				      70  71  72  73  74  75  76  77  78  79
+				      80  81  82  83  84  85  86  87  88  89
+				      90  91  92  93  94  95  96  97  98  99
+				     100 101 102 103 104 105 106 107 108 109
+				     110 111 112 113 114 115 116 117 118 119
+				     120 121 122 123 124 125 126 127 128 129
+				     130 131 132 133 134 135 136 137 138 139
+				     140 141 142 143 144 145 146 147 148 149
+				     150 151 152 153 154 155 156 157 158 159
+				     160 161 162 163 164 165 166 167 168 169
+				     170 171 172 173 174 175 176 177 178 179
+				     180 181 182 183 184 185 186 187 188 189
+				     190 191 192 193 194 195 196 197 198 199
+				     200 201 202 203 204 205 206 207 208 209
+				     210 211 212 213 214 215 216 217 218 219
+				     220 221 222 223 224 225 226 227 228 229
+				     230 231 232 233 234 235 236 237 238 239
+				     240 241 242 243 244 245 246 247 248 249
+				     250 251 252 253 254 255>;
+		default-brightness-level = <50>;
+		enable-gpios = <&gpio1 2 GPIO_ACTIVE_LOW>;
+	};
+
+	reg_lvds: regulator-lvds {
+		compatible = "regulator-fixed";
+		regulator-name = "lvds_ppen";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		gpio = <&gpio1 19 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
+
+	panel-lvds0 {
+		compatible = "innolux,g121x1-l03";
+		backlight = <&backlight_lvds>;
+		power-supply = <&reg_lvds>;
+
+		port {
+			panel_in_lvds0: endpoint {
+				remote-endpoint = <&lvds0_out>;
+			};
+		};
+	};
+};
+
+&i2c1 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c1>;
+	status = "okay";
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c2>;
+	status = "okay";
+
+	pmic: pfuze100 at 08 {
+		compatible = "fsl,pfuze100";
+		reg = <0x08>;
+
+		regulators {
+			sw1a_reg: sw1ab {
+				regulator-min-microvolt = <300000>;
+				regulator-max-microvolt = <1875000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-ramp-delay = <6250>;
+			};
+
+			sw1c_reg: sw1c {
+				regulator-min-microvolt = <300000>;
+				regulator-max-microvolt = <1875000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-ramp-delay = <6250>;
+			};
+
+			sw2_reg: sw2 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3950000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw3a_reg: sw3a {
+				regulator-min-microvolt = <400000>;
+				regulator-max-microvolt = <1975000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw3b_reg: sw3b {
+				regulator-min-microvolt = <400000>;
+				regulator-max-microvolt = <1975000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw4_reg: sw4 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			swbst_reg: swbst {
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5150000>;
+			};
+
+			snvs_reg: vsnvs {
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			vref_reg: vrefddr {
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			vgen1_reg: vgen1 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1550000>;
+			};
+
+			vgen2_reg: vgen2 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1550000>;
+			};
+
+			vgen3_reg: vgen3 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vgen4_reg: vgen4 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vgen5_reg: vgen5 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vgen6_reg: vgen6 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&iomuxc {
+	pinctrl-names = "default";
+
+	imx6q-mccmon6 {
+
+		pinctrl_enet: enetgrp {
+			fsl,pins = <
+				MX6QDL_PAD_ENET_MDIO__ENET_MDIO		0x1b0b0
+				MX6QDL_PAD_ENET_MDC__ENET_MDC		0x1b0b0
+				MX6QDL_PAD_RGMII_TXC__RGMII_TXC		0x1b0b0
+				MX6QDL_PAD_RGMII_TD0__RGMII_TD0		0x1b0b0
+				MX6QDL_PAD_RGMII_TD1__RGMII_TD1		0x1b0b0
+				MX6QDL_PAD_RGMII_TD2__RGMII_TD2		0x1b0b0
+				MX6QDL_PAD_RGMII_TD3__RGMII_TD3		0x1b0b0
+				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL	0x1b0b0
+				MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK	0x1b0b0
+				MX6QDL_PAD_RGMII_RXC__RGMII_RXC		0x1b0b0
+				MX6QDL_PAD_RGMII_RD0__RGMII_RD0		0x1b0b0
+				MX6QDL_PAD_RGMII_RD1__RGMII_RD1		0x1b0b0
+				MX6QDL_PAD_RGMII_RD2__RGMII_RD2		0x1b0b0
+				MX6QDL_PAD_RGMII_RD3__RGMII_RD3		0x1b0b0
+				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x1b0b0
+				MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8
+				MX6QDL_PAD_GPIO_6__ENET_IRQ		0x000b1
+			>;
+		};
+
+		pinctrl_i2c1: i2c1grp {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT9__I2C1_SCL	0x4001b8b1
+				MX6QDL_PAD_CSI0_DAT8__I2C1_SDA	0x4001b8b1
+			>;
+		};
+
+		pinctrl_i2c2: i2c2grp {
+			fsl,pins = <
+				MX6QDL_PAD_KEY_COL3__I2C2_SCL	0x4001b8b1
+				MX6QDL_PAD_KEY_ROW3__I2C2_SDA	0x4001b8b1
+			>;
+		};
+
+		pinctrl_uart1: uart1grp {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA	0x1b0b1
+				MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA	0x1b0b1
+			>;
+		};
+
+		pinctrl_usdhc2: usdhc2grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD2_CMD__SD2_CMD		0x17059
+				MX6QDL_PAD_SD2_CLK__SD2_CLK		0x10059
+				MX6QDL_PAD_SD2_DAT0__SD2_DATA0		0x17059
+				MX6QDL_PAD_SD2_DAT1__SD2_DATA1		0x17059
+				MX6QDL_PAD_SD2_DAT2__SD2_DATA2		0x17059
+				MX6QDL_PAD_SD2_DAT3__SD2_DATA3		0x17059
+			>;
+		};
+
+		pinctrl_usdhc3: usdhc3grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD3_CMD__SD3_CMD		0x17059
+				MX6QDL_PAD_SD3_CLK__SD3_CLK		0x10059
+				MX6QDL_PAD_SD3_DAT0__SD3_DATA0		0x17059
+				MX6QDL_PAD_SD3_DAT1__SD3_DATA1		0x17059
+				MX6QDL_PAD_SD3_DAT2__SD3_DATA2		0x17059
+				MX6QDL_PAD_SD3_DAT3__SD3_DATA3		0x17059
+				MX6QDL_PAD_SD3_DAT4__SD3_DATA4		0x17059
+				MX6QDL_PAD_SD3_DAT5__SD3_DATA5		0x17059
+				MX6QDL_PAD_SD3_DAT6__SD3_DATA6		0x17059
+				MX6QDL_PAD_SD3_DAT7__SD3_DATA7		0x17059
+				MX6QDL_PAD_SD3_RST__SD3_RESET		0x17059
+			>;
+		};
+
+		pinctrl_weim_cs0: weimcs0grp {
+			fsl,pins = <
+				MX6QDL_PAD_EIM_CS0__EIM_CS0_B		0xb0b1
+			>;
+		};
+
+		pinctrl_weim_nor: weimnorgrp {
+			fsl,pins = <
+				MX6QDL_PAD_EIM_OE__EIM_OE_B		0xb0b1
+				MX6QDL_PAD_EIM_RW__EIM_RW		0xb0b1
+				MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B	0xb060
+				MX6QDL_PAD_EIM_D16__EIM_DATA16		0x1b0b0
+				MX6QDL_PAD_EIM_D17__EIM_DATA17		0x1b0b0
+				MX6QDL_PAD_EIM_D18__EIM_DATA18		0x1b0b0
+				MX6QDL_PAD_EIM_D19__EIM_DATA19		0x1b0b0
+				MX6QDL_PAD_EIM_D20__EIM_DATA20		0x1b0b0
+				MX6QDL_PAD_EIM_D21__EIM_DATA21		0x1b0b0
+				MX6QDL_PAD_EIM_D22__EIM_DATA22		0x1b0b0
+				MX6QDL_PAD_EIM_D23__EIM_DATA23		0x1b0b0
+				MX6QDL_PAD_EIM_D24__EIM_DATA24		0x1b0b0
+				MX6QDL_PAD_EIM_D25__EIM_DATA25		0x1b0b0
+				MX6QDL_PAD_EIM_D26__EIM_DATA26		0x1b0b0
+				MX6QDL_PAD_EIM_D27__EIM_DATA27		0x1b0b0
+				MX6QDL_PAD_EIM_D28__EIM_DATA28		0x1b0b0
+				MX6QDL_PAD_EIM_D29__EIM_DATA29		0x1b0b0
+				MX6QDL_PAD_EIM_D30__EIM_DATA30		0x1b0b0
+				MX6QDL_PAD_EIM_D31__EIM_DATA31		0x1b0b0
+				MX6QDL_PAD_EIM_A23__EIM_ADDR23		0xb0b1
+				MX6QDL_PAD_EIM_A22__EIM_ADDR22		0xb0b1
+				MX6QDL_PAD_EIM_A21__EIM_ADDR21		0xb0b1
+				MX6QDL_PAD_EIM_A20__EIM_ADDR20		0xb0b1
+				MX6QDL_PAD_EIM_A19__EIM_ADDR19		0xb0b1
+				MX6QDL_PAD_EIM_A18__EIM_ADDR18		0xb0b1
+				MX6QDL_PAD_EIM_A17__EIM_ADDR17		0xb0b1
+				MX6QDL_PAD_EIM_A16__EIM_ADDR16		0xb0b1
+				MX6QDL_PAD_EIM_DA15__EIM_AD15		0xb0b1
+				MX6QDL_PAD_EIM_DA14__EIM_AD14		0xb0b1
+				MX6QDL_PAD_EIM_DA13__EIM_AD13		0xb0b1
+				MX6QDL_PAD_EIM_DA12__EIM_AD12		0xb0b1
+				MX6QDL_PAD_EIM_DA11__EIM_AD11		0xb0b1
+				MX6QDL_PAD_EIM_DA10__EIM_AD10		0xb0b1
+				MX6QDL_PAD_EIM_DA9__EIM_AD09		0xb0b1
+				MX6QDL_PAD_EIM_DA8__EIM_AD08		0xb0b1
+				MX6QDL_PAD_EIM_DA7__EIM_AD07		0xb0b1
+				MX6QDL_PAD_EIM_DA6__EIM_AD06		0xb0b1
+				MX6QDL_PAD_EIM_DA5__EIM_AD05		0xb0b1
+				MX6QDL_PAD_EIM_DA4__EIM_AD04		0xb0b1
+				MX6QDL_PAD_EIM_DA3__EIM_AD03		0xb0b1
+				MX6QDL_PAD_EIM_DA2__EIM_AD02		0xb0b1
+				MX6QDL_PAD_EIM_DA1__EIM_AD01		0xb0b1
+				MX6QDL_PAD_EIM_DA0__EIM_AD00		0xb0b1
+			>;
+		};
+
+		pinctrl_ecspi3: ecspi3grp {
+			fsl,pins = <
+				MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO	0x100b1
+				MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI	0x100b1
+				MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK	0x100b1
+			>;
+		};
+
+		pinctrl_ecspi3_cs: ecspi3cs {
+			fsl,pins = <
+				MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x80000000
+			>;
+		};
+		pinctrl_ecspi3_flwp: ecspi3flwp {
+			fsl,pins = <
+				MX6QDL_PAD_DISP0_DAT6__GPIO4_IO27 0x80000000
+			>;
+		};
+
+		pinctrl_uart4: uart4grp {
+			fsl,pins = <
+				MX6QDL_PAD_KEY_COL0__UART4_TX_DATA	0x1b0b1
+				MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA	0x1b0b1
+				MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B	0x1b0b1
+				MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B	0x1b0b1
+			>;
+		};
+
+		pinctrl_display: dispgrp {
+			fsl,pins = <
+				/* BLEN_OUT */
+				MX6QDL_PAD_GPIO_2__GPIO1_IO02    0x1b0b0
+				/* LVDS_PPEN_OUT */
+				MX6QDL_PAD_SD1_DAT2__GPIO1_IO19   0x1b0b0
+			>;
+		};
+
+		pinctrl_pwm2: pwm2grp {
+			fsl,pins = <
+				 MX6QDL_PAD_GPIO_1__PWM2_OUT	0x1b0b1
+			>;
+		};
+	};
+};
+
+&fec {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_enet>;
+	phy-mode = "rgmii";
+	phy-reset-gpios = <&gpio1 27 0>;
+	interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
+			      <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart1>;
+	status = "okay";
+};
+
+&usdhc2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc2>;
+	cd-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
+	status = "okay";
+};
+
+&usdhc3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc3>;
+	bus-width = <8>;
+	status = "okay";
+};
+
+&weim {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_weim_nor &pinctrl_weim_cs0>;
+	#address-cells = <2>;
+	#size-cells = <1>;
+	ranges = <0 0 0x08000000 0x08000000>;
+	status = "okay";
+
+	nor at 0,0 {
+		compatible = "cfi-flash";
+		reg = <0 0 0x02000000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		bank-width = <2>;
+		use-advanced-sector-protection;
+		fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000
+				0x0000c000 0x1404a38e 0x00000000>;
+	};
+};
+
+&ecspi3 {
+	fsl,spi-num-chipselects = <1>;
+	cs-gpios = <&gpio4 24 0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_ecspi3 &pinctrl_ecspi3_cs &pinctrl_ecspi3_flwp>;
+	status = "okay";
+
+	flash: s25sl032p at 0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spansion,s25sl032p", "jedec,spi-nor";
+		spi-max-frequency = <40000000>;
+		reg = <0>;
+	};
+};
+
+&uart4 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart4>;
+	status = "okay";
+};
+
+&ldb {
+	status = "okay";
+
+	lvds0: lvds-channel at 0 {
+		fsl,data-mapping = "spwg";
+		fsl,data-width = <24>;
+		status = "okay";
+
+		port at 4 {
+			reg = <4>;
+
+			lvds0_out: endpoint {
+				remote-endpoint = <&panel_in_lvds0>;
+			};
+		};
+	};
+};
+
+&pwm2 {
+	#pwm-cells = <3>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pwm2>;
+	status = "okay";
+};
-- 
2.1.4

^ permalink raw reply related

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Jaehoon Chung @ 2016-12-27  1:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5860E6F7.2090703@samsung.com>

Dear Alim,

On 12/26/2016 06:46 PM, Alim Akhtar wrote:
> Hello Jaehoon
> 
> On 12/26/2016 02:32 PM, Jaehoon Chung wrote:
>> Hi Pankaj,
>>
>> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>
>>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>>> This patch does refactoring of Exynos PCIe driver to extend support
>>> for other Exynos SoC.
>>>
>>> Following are the main changes done via this patch:
>>> 1) It adds separate structs for memory, clock resources.
>>> 2) It add exynos_pcie_ops struct which will allow us to support the
>>> differences in resources in different Exynos SoC.
>>
>> It's nice to me for reusing this file.
>> but after considering too many times, i decided not to use this file.
>>
>> I'm not sure what block base is..actually this pci-exynos.c is really black-box.
>> (No one maintains this file, even Samsung didn't care.)
>> Who is using this?
>> If Someone can share the information about exynos5440, i can refactor everything.
>> Otherwise, there are two solution..
>>
>> One is "adding the new pci-exynos.c" likes pci-exynos5433.c
>> Other is "refactor this file" under assuming the each register's usage.
>>
> Its alway good to reuse code as far as possible.
> I am yet to see the details of 5440, but since people are now going to support more Exynos variants, in my opinion, instead of adding pci-exynos5433.c, you can rename current pci-exynos.c to something like pci-exynos5440.c (only in case its too much changes needed to support 5433/exynos7) and lets have a common pci-exynos.c where we can add exynos7/5433 and others SoCs, AFAIK at least exynos7 and 5433 has similar pci controller.

Yes, It's always good to reuse. but as you know, current pci-exynos.c file is really specific for only exynos5440.
I will try to check about combining.

>> I want to use the PHY generic Framework for EXYNOS PCIe.
>>
> I don't think you have an option here, you should use generic PHY APIs, otherwise phy drive should go to drivers/misc.
> Other thoughts are welcome.

Why go to drivers/misc? There is driver/phy/ for PHY generic Framework.
If i will touch this file, then i will put our phy-exynos-pcie file under driver/phy/ .
I already sent the patch for this. Could you check them?

http://patchwork.ozlabs.org/patch/708738/
http://patchwork.ozlabs.org/patch/708742/

Best Regards,
Jaehoon Chung

> 
>> If you or other guys really want to use the pci-exynos.c for other exynos,
>> I will rework with PHY generic framework. Then i will resend the my patches as V2.
>>
>> One more thing..Does anyone know what the usage of block base is?
>> Can i use that register as "syscon"?
>>
>> Best Regards,
>> Jaehoon Chung
>>
>>>
>>> No functional change intended.
>>>
>>> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>>> ---
>>> This patch set is prepared on top of Krzysztof's for-next and
>>> PCIe driver cleanup patch [1] by Jaehoon Chung.
>>>
>>> [1]: https://lkml.org/lkml/2016/12/19/44
>>>
>>>
>>>   drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++----------------
>>>   1 file changed, 217 insertions(+), 129 deletions(-)
>>>
>>> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
>>> index 33562cf..2dc54f7 100644
>>> --- a/drivers/pci/host/pci-exynos.c
>>> +++ b/drivers/pci/host/pci-exynos.c
>>> @@ -17,6 +17,7 @@
>>>   #include <linux/interrupt.h>
>>>   #include <linux/kernel.h>
>>>   #include <linux/init.h>
>>> +#include <linux/of_device.h>
>>>   #include <linux/of_gpio.h>
>>>   #include <linux/pci.h>
>>>   #include <linux/platform_device.h>
>>> @@ -28,16 +29,6 @@
>>>
>>>   #define to_exynos_pcie(x)    container_of(x, struct exynos_pcie, pp)
>>>
>>> -struct exynos_pcie {
>>> -    struct pcie_port    pp;
>>> -    void __iomem        *elbi_base;    /* DT 0th resource */
>>> -    void __iomem        *phy_base;    /* DT 1st resource */
>>> -    void __iomem        *block_base;    /* DT 2nd resource */
>>> -    int            reset_gpio;
>>> -    struct clk        *clk;
>>> -    struct clk        *bus_clk;
>>> -};
>>> -
>>>   /* PCIe ELBI registers */
>>>   #define PCIE_IRQ_PULSE            0x000
>>>   #define IRQ_INTA_ASSERT            BIT(0)
>>> @@ -102,6 +93,122 @@ struct exynos_pcie {
>>>   #define PCIE_PHY_TRSV3_PD_TSV        BIT(7)
>>>   #define PCIE_PHY_TRSV3_LVCC        0x31c
>>>
>>> +struct exynos_pcie_mem_res {
>>> +    void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
>>> +    void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
>>> +    void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
>>> +};
>>> +
>>> +struct exynos_pcie_clk_res {
>>> +    struct clk *clk;
>>> +    struct clk *bus_clk;
>>> +};
>>> +
>>> +struct exynos_pcie {
>>> +    struct pcie_port        pp;
>>> +    struct exynos_pcie_mem_res    *mem_res;
>>> +    struct exynos_pcie_clk_res    *clk_res;
>>> +    const struct exynos_pcie_ops    *ops;
>>> +    int                reset_gpio;
>>> +};
>>> +
>>> +struct exynos_pcie_ops {
>>> +    int (*get_mem_resources)(struct platform_device *pdev,
>>> +            struct exynos_pcie *ep);
>>> +    int (*get_clk_resources)(struct exynos_pcie *ep);
>>> +    int (*init_clk_resources)(struct exynos_pcie *ep);
>>> +    void (*deinit_clk_resources)(struct exynos_pcie *ep);
>>> +};
>>> +
>>> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
>>> +                        struct exynos_pcie *ep)
>>> +{
>>> +    struct resource *res;
>>> +    struct device *dev = ep->pp.dev;
>>> +
>>> +    ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
>>> +    if (!ep->mem_res)
>>> +        return -ENOMEM;
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +    ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
>>> +    if (IS_ERR(ep->mem_res->elbi_base))
>>> +        return PTR_ERR(ep->mem_res->elbi_base);
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> +    ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
>>> +    if (IS_ERR(ep->mem_res->phy_base))
>>> +        return PTR_ERR(ep->mem_res->phy_base);
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>> +    ep->mem_res->block_base = devm_ioremap_resource(dev, res);
>>> +    if (IS_ERR(ep->mem_res->block_base))
>>> +        return PTR_ERR(ep->mem_res->block_base);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
>>> +{
>>> +    struct device *dev = ep->pp.dev;
>>> +
>>> +    ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
>>> +    if (!ep->clk_res)
>>> +        return -ENOMEM;
>>> +
>>> +    ep->clk_res->clk = devm_clk_get(dev, "pcie");
>>> +    if (IS_ERR(ep->clk_res->clk)) {
>>> +        dev_err(dev, "Failed to get pcie rc clock\n");
>>> +        return PTR_ERR(ep->clk_res->clk);
>>> +    }
>>> +
>>> +    ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
>>> +    if (IS_ERR(ep->clk_res->bus_clk)) {
>>> +        dev_err(dev, "Failed to get pcie bus clock\n");
>>> +        return PTR_ERR(ep->clk_res->bus_clk);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
>>> +{
>>> +    struct device *dev = ep->pp.dev;
>>> +    int ret;
>>> +
>>> +    ret = clk_prepare_enable(ep->clk_res->clk);
>>> +    if (ret) {
>>> +        dev_err(dev, "cannot enable pcie rc clock");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = clk_prepare_enable(ep->clk_res->bus_clk);
>>> +    if (ret) {
>>> +        dev_err(dev, "cannot enable pcie bus clock");
>>> +        goto err_bus_clk;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +err_bus_clk:
>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
>>> +{
>>> +    clk_disable_unprepare(ep->clk_res->bus_clk);
>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>> +}
>>> +
>>> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
>>> +    .get_mem_resources    = exynos5440_pcie_get_mem_resources,
>>> +    .get_clk_resources    = exynos5440_pcie_get_clk_resources,
>>> +    .init_clk_resources    = exynos5440_pcie_init_clk_resources,
>>> +    .deinit_clk_resources    = exynos5440_pcie_deinit_clk_resources,
>>> +};
>>> +
>>>   static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
>>>   {
>>>       writel(val, base + reg);
>>> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>       if (on)
>>>           val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>       else
>>>           val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>   }
>>>
>>>   static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>       if (on)
>>>           val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>       else
>>>           val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>   }
>>>
>>>   static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>       val &= ~PCIE_CORE_RESET_ENABLE;
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>   }
>>>
>>>   static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>       val |= PCIE_CORE_RESET_ENABLE;
>>>
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
>>>   }
>>>
>>>   static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
>>>   {
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>   }
>>>
>>>   static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
>>>   {
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>   }
>>>
>>>   static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>       val &= ~PCIE_PHY_COMMON_PD_CMN;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>       val &= ~PCIE_PHY_TRSV0_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>       val &= ~PCIE_PHY_TRSV1_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>       val &= ~PCIE_PHY_TRSV2_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>       val &= ~PCIE_PHY_TRSV3_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>   }
>>>
>>>   static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>       val |= PCIE_PHY_COMMON_PD_CMN;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>       val |= PCIE_PHY_TRSV0_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>       val |= PCIE_PHY_TRSV1_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>       val |= PCIE_PHY_TRSV2_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>
>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>       val |= PCIE_PHY_TRSV3_PD_TSV;
>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>   }
>>>
>>>   static void exynos_pcie_init_phy(struct exynos_pcie *ep)
>>>   {
>>>       /* DCC feedback control off */
>>> -    exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>
>>>       /* set TX/RX impedance */
>>> -    exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>
>>>       /* set 50Mhz PHY clock */
>>> -    exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>> -    exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>
>>>       /* set TX Differential output for lane 0 */
>>> -    exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>
>>>       /* set TX Pre-emphasis Level Control for lane 0 to minimum */
>>> -    exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>
>>>       /* set RX clock and data recovery bandwidth */
>>> -    exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>
>>>       /* change TX Pre-emphasis Level Control for lanes */
>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>
>>>       /* set LVCC */
>>> -    exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>   }
>>>
>>>   static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
>>> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
>>>       exynos_pcie_init_phy(exynos_pcie);
>>>
>>>       /* pulse for common reset */
>>> -    exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
>>> +                PCIE_PHY_COMMON_RESET);
>>>       udelay(500);
>>> -    exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
>>> +                PCIE_PHY_COMMON_RESET);
>>>
>>>       exynos_pcie_deassert_core_reset(exynos_pcie);
>>>       dw_pcie_setup_rc(pp);
>>>       exynos_pcie_assert_reset(exynos_pcie);
>>>
>>>       /* assert LTSSM enable */
>>> -    exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
>>> -              PCIE_APP_LTSSM_ENABLE);
>>> +    exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
>>> +            PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>>>
>>>       /* check if the link is up or not */
>>>       if (!dw_pcie_wait_for_link(pp))
>>>           return 0;
>>>
>>> -    while (exynos_pcie_readl(exynos_pcie->phy_base,
>>> +    while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
>>>                   PCIE_PHY_PLL_LOCKED) == 0) {
>>> -        val = exynos_pcie_readl(exynos_pcie->block_base,
>>> +        val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
>>>                   PCIE_PHY_PLL_LOCKED);
>>>           dev_info(dev, "PLL Locked: 0x%x\n", val);
>>>       }
>>> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
>>>   {
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
>>>   }
>>>
>>>   static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>       /* enable INTX interrupt */
>>>       val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
>>>           IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>   }
>>>
>>>   static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
>>> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
>>>       dw_pcie_msi_init(pp);
>>>
>>>       /* enable MSI interrupt */
>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>       val |= IRQ_MSI_ENABLE;
>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>   }
>>>
>>>   static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
>>> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
>>>       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>>>       u32 val;
>>>
>>> -    val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
>>> +    val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
>>> +                    PCIE_ELBI_RDLH_LINKUP);
>>>       if (val == PCIE_ELBI_LTSSM_ENABLE)
>>>           return 1;
>>>
>>> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>       struct exynos_pcie *exynos_pcie;
>>>       struct pcie_port *pp;
>>>       struct device_node *np = dev->of_node;
>>> -    struct resource *res;
>>>       int ret;
>>>
>>>       exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
>>> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>       pp = &exynos_pcie->pp;
>>>       pp->dev = dev;
>>>
>>> -    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>> -
>>> -    exynos_pcie->clk = devm_clk_get(dev, "pcie");
>>> -    if (IS_ERR(exynos_pcie->clk)) {
>>> -        dev_err(dev, "Failed to get pcie rc clock\n");
>>> -        return PTR_ERR(exynos_pcie->clk);
>>> -    }
>>> -    ret = clk_prepare_enable(exynos_pcie->clk);
>>> -    if (ret)
>>> -        return ret;
>>> +    exynos_pcie->ops = (const struct exynos_pcie_ops *)
>>> +                of_device_get_match_data(dev);
>>>
>>> -    exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
>>> -    if (IS_ERR(exynos_pcie->bus_clk)) {
>>> -        dev_err(dev, "Failed to get pcie bus clock\n");
>>> -        ret = PTR_ERR(exynos_pcie->bus_clk);
>>> -        goto fail_clk;
>>> -    }
>>> -    ret = clk_prepare_enable(exynos_pcie->bus_clk);
>>> -    if (ret)
>>> -        goto fail_clk;
>>> +    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>
>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> -    exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
>>> -    if (IS_ERR(exynos_pcie->elbi_base)) {
>>> -        ret = PTR_ERR(exynos_pcie->elbi_base);
>>> -        goto fail_bus_clk;
>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
>>> +        ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
>>> +        if (ret)
>>> +            return ret;
>>>       }
>>>
>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> -    exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
>>> -    if (IS_ERR(exynos_pcie->phy_base)) {
>>> -        ret = PTR_ERR(exynos_pcie->phy_base);
>>> -        goto fail_bus_clk;
>>> -    }
>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
>>> +        ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
>>> +        if (ret)
>>> +            return ret;
>>>
>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>> -    exynos_pcie->block_base = devm_ioremap_resource(dev, res);
>>> -    if (IS_ERR(exynos_pcie->block_base)) {
>>> -        ret = PTR_ERR(exynos_pcie->block_base);
>>> -        goto fail_bus_clk;
>>> +        ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
>>> +        if (ret)
>>> +            return ret;
>>>       }
>>>
>>>       ret = exynos_add_pcie_port(exynos_pcie, pdev);
>>>       if (ret < 0)
>>> -        goto fail_bus_clk;
>>> +        goto fail_probe;
>>>
>>>       platform_set_drvdata(pdev, exynos_pcie);
>>>       return 0;
>>>
>>> -fail_bus_clk:
>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>> -fail_clk:
>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>> +fail_probe:
>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>       return ret;
>>>   }
>>>
>>> @@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
>>>   {
>>>       struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>>>
>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>
>>>       return 0;
>>>   }
>>>
>>>   static const struct of_device_id exynos_pcie_of_match[] = {
>>> -    { .compatible = "samsung,exynos5440-pcie", },
>>> +    {    .compatible = "samsung,exynos5440-pcie",
>>> +        .data = &exynos5440_pcie_ops },
>>>       {},
>>>   };
>>>
>>>
>>
>>
>>
> 
> 

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Jaehoon Chung @ 2016-12-27  1:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <000201d25f86$6ee20bd0$4ca62370$@gmail.com>

Dear Jingoo,

On 12/26/2016 11:43 PM, Jingoo Han wrote:
> Jaehoon Chung wtote:
>>
>> Hi Pankaj,
>>
>> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>
>>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>>> This patch does refactoring of Exynos PCIe driver to extend support
>>> for other Exynos SoC.
>>>
>>> Following are the main changes done via this patch:
>>> 1) It adds separate structs for memory, clock resources.
>>> 2) It add exynos_pcie_ops struct which will allow us to support the
>>> differences in resources in different Exynos SoC.
>>
>> It's nice to me for reusing this file.
>> but after considering too many times, i decided not to use this file.
>>
>> I'm not sure what block base is..actually this pci-exynos.c is really
>> black-box.
>> (No one maintains this file, even Samsung didn't care.)
>> Who is using this?
>> If Someone can share the information about exynos5440, i can refactor
>> everything.
>> Otherwise, there are two solution..
>>
>> One is "adding the new pci-exynos.c" likes pci-exynos5433.c
> 
> As Bjorn mentioned earlier, I agree with this option.
> 
>> Other is "refactor this file" under assuming the each register's usage.
> 
> But, if possible, I prefer this option.
> I am not sure that it cannot make the code dirty.
> Maybe, you need to discuss with hardware design engineers.
> 
>>
>> I want to use the PHY generic Framework for EXYNOS PCIe.
>>
>> If you or other guys really want to use the pci-exynos.c for other exynos,
>> I will rework with PHY generic framework. Then i will resend the my
>> patches as V2.
> 
> When I submitted the pci-exynos.c, there was no PHY generic framework.
> But, currently, using PHY generic framework is mandatory, as other PCIe host
> driver did.
> I think that we should use PHY generic framework for new SoCs.
> 
>>
>> One more thing..Does anyone know what the usage of block base is?
>> Can i use that register as "syscon"?
> 
> 'Block' is very specific registers for 5440.
> Other Exynos SoCs do not use that registers.
> Actually, it is not the same with 'syscon'.
> But, you can assume 'block' as 'syscon'.

Great! I want to know it. Then i will refactor this file for all other Exynos SoCs.
And Could you check my RFC patches? Maybe i missed your email in my RFC patches.

https://lkml.org/lkml/2016/12/26/6

Best Regards,
Jaehoon Chung

> 
> Best regards,
> Jingoo Han
> 
>>
>> Best Regards,
>> Jaehoon Chung
>>
> [.....]
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 

^ permalink raw reply

* [PATCH v2] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Chen-Yu Tsai @ 2016-12-27  2:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161226174748.17544-1-manu@bidouilliste.com>

Hi,

On Tue, Dec 27, 2016 at 1:47 AM, Emmanuel Vadot <manu@bidouilliste.com> wrote:
> The spi0 controller on the A20 have up to 4 CS (Chip Select) while the
> others three only have 1.
> Add the num-cs property to each node.
> The current driver doesn't read this property but this is useful for
> downstream user of DTS (FreeBSD for example).
>
> Signed-off-by: Emmanuel Vadot <manu@bidouilliste.com>
> ---
>
> Changes in v2:
>  * Explain that driver doesn't support this but that it is useful
>  for downstream users of DTS.
>
>  arch/arm/boot/dts/sun7i-a20.dtsi | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
> index 94cf5a1c7172..ed21982c81cb 100644
> --- a/arch/arm/boot/dts/sun7i-a20.dtsi
> +++ b/arch/arm/boot/dts/sun7i-a20.dtsi
> @@ -871,6 +871,7 @@
>                         status = "disabled";
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> +                       num-cs = 4;

The numbers need to be enclosed in angle brackets.

ChenYu

>                 };
>
>                 spi1: spi at 01c06000 {
> @@ -885,6 +886,7 @@
>                         status = "disabled";
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> +                       num-cs = 1;
>                 };
>
>                 emac: ethernet at 01c0b000 {
> @@ -1037,6 +1039,7 @@
>                         status = "disabled";
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> +                       num-cs = 1;
>                 };
>
>                 ahci: sata at 01c18000 {
> @@ -1079,6 +1082,7 @@
>                         status = "disabled";
>                         #address-cells = <1>;
>                         #size-cells = <0>;
> +                       num-cs = 1;
>                 };
>
>                 pio: pinctrl at 01c20800 {
> --
> 2.11.0
>

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Alim Akhtar @ 2016-12-27  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <79b89c49-a9de-3daa-9afa-b8f36304a226@samsung.com>



On 12/27/2016 06:39 AM, Jaehoon Chung wrote:
> Dear Alim,
>
> On 12/26/2016 06:46 PM, Alim Akhtar wrote:
>> Hello Jaehoon
>>
>> On 12/26/2016 02:32 PM, Jaehoon Chung wrote:
>>> Hi Pankaj,
>>>
>>> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>>>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>>
>>>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>>>> This patch does refactoring of Exynos PCIe driver to extend support
>>>> for other Exynos SoC.
>>>>
>>>> Following are the main changes done via this patch:
>>>> 1) It adds separate structs for memory, clock resources.
>>>> 2) It add exynos_pcie_ops struct which will allow us to support the
>>>> differences in resources in different Exynos SoC.
>>>
>>> It's nice to me for reusing this file.
>>> but after considering too many times, i decided not to use this file.
>>>
>>> I'm not sure what block base is..actually this pci-exynos.c is really black-box.
>>> (No one maintains this file, even Samsung didn't care.)
>>> Who is using this?
>>> If Someone can share the information about exynos5440, i can refactor everything.
>>> Otherwise, there are two solution..
>>>
>>> One is "adding the new pci-exynos.c" likes pci-exynos5433.c
>>> Other is "refactor this file" under assuming the each register's usage.
>>>
>> Its alway good to reuse code as far as possible.
>> I am yet to see the details of 5440, but since people are now going to support more Exynos variants, in my opinion, instead of adding pci-exynos5433.c, you can rename current pci-exynos.c to something like pci-exynos5440.c (only in case its too much changes needed to support 5433/exynos7) and lets have a common pci-exynos.c where we can add exynos7/5433 and others SoCs, AFAIK at least exynos7 and 5433 has similar pci controller.
>
> Yes, It's always good to reuse. but as you know, current pci-exynos.c file is really specific for only exynos5440.
> I will try to check about combining.
>
>>> I want to use the PHY generic Framework for EXYNOS PCIe.
>>>
>> I don't think you have an option here, you should use generic PHY APIs, otherwise phy drive should go to drivers/misc.
>> Other thoughts are welcome.
>
> Why go to drivers/misc? There is driver/phy/ for PHY generic Framework.
> If i will touch this file, then i will put our phy-exynos-pcie file under driver/phy/ .
> I already sent the patch for this. Could you check them?
>
My point was, if you are not going to use generic PHY APIs, then phy 
driver should go into drivers/misc. Anyway as you said you have already 
posted patches with generic phy framework, I will take a look.
> http://patchwork.ozlabs.org/patch/708738/
> http://patchwork.ozlabs.org/patch/708742/
>
Thanks.

> Best Regards,
> Jaehoon Chung
>
>>
>>> If you or other guys really want to use the pci-exynos.c for other exynos,
>>> I will rework with PHY generic framework. Then i will resend the my patches as V2.
>>>
>>> One more thing..Does anyone know what the usage of block base is?
>>> Can i use that register as "syscon"?
>>>
>>> Best Regards,
>>> Jaehoon Chung
>>>
>>>>
>>>> No functional change intended.
>>>>
>>>> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>>>> ---
>>>> This patch set is prepared on top of Krzysztof's for-next and
>>>> PCIe driver cleanup patch [1] by Jaehoon Chung.
>>>>
>>>> [1]: https://lkml.org/lkml/2016/12/19/44
>>>>
>>>>
>>>>    drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++----------------
>>>>    1 file changed, 217 insertions(+), 129 deletions(-)
>>>>
>>>> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
>>>> index 33562cf..2dc54f7 100644
>>>> --- a/drivers/pci/host/pci-exynos.c
>>>> +++ b/drivers/pci/host/pci-exynos.c
>>>> @@ -17,6 +17,7 @@
>>>>    #include <linux/interrupt.h>
>>>>    #include <linux/kernel.h>
>>>>    #include <linux/init.h>
>>>> +#include <linux/of_device.h>
>>>>    #include <linux/of_gpio.h>
>>>>    #include <linux/pci.h>
>>>>    #include <linux/platform_device.h>
>>>> @@ -28,16 +29,6 @@
>>>>
>>>>    #define to_exynos_pcie(x)    container_of(x, struct exynos_pcie, pp)
>>>>
>>>> -struct exynos_pcie {
>>>> -    struct pcie_port    pp;
>>>> -    void __iomem        *elbi_base;    /* DT 0th resource */
>>>> -    void __iomem        *phy_base;    /* DT 1st resource */
>>>> -    void __iomem        *block_base;    /* DT 2nd resource */
>>>> -    int            reset_gpio;
>>>> -    struct clk        *clk;
>>>> -    struct clk        *bus_clk;
>>>> -};
>>>> -
>>>>    /* PCIe ELBI registers */
>>>>    #define PCIE_IRQ_PULSE            0x000
>>>>    #define IRQ_INTA_ASSERT            BIT(0)
>>>> @@ -102,6 +93,122 @@ struct exynos_pcie {
>>>>    #define PCIE_PHY_TRSV3_PD_TSV        BIT(7)
>>>>    #define PCIE_PHY_TRSV3_LVCC        0x31c
>>>>
>>>> +struct exynos_pcie_mem_res {
>>>> +    void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
>>>> +    void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
>>>> +    void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
>>>> +};
>>>> +
>>>> +struct exynos_pcie_clk_res {
>>>> +    struct clk *clk;
>>>> +    struct clk *bus_clk;
>>>> +};
>>>> +
>>>> +struct exynos_pcie {
>>>> +    struct pcie_port        pp;
>>>> +    struct exynos_pcie_mem_res    *mem_res;
>>>> +    struct exynos_pcie_clk_res    *clk_res;
>>>> +    const struct exynos_pcie_ops    *ops;
>>>> +    int                reset_gpio;
>>>> +};
>>>> +
>>>> +struct exynos_pcie_ops {
>>>> +    int (*get_mem_resources)(struct platform_device *pdev,
>>>> +            struct exynos_pcie *ep);
>>>> +    int (*get_clk_resources)(struct exynos_pcie *ep);
>>>> +    int (*init_clk_resources)(struct exynos_pcie *ep);
>>>> +    void (*deinit_clk_resources)(struct exynos_pcie *ep);
>>>> +};
>>>> +
>>>> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
>>>> +                        struct exynos_pcie *ep)
>>>> +{
>>>> +    struct resource *res;
>>>> +    struct device *dev = ep->pp.dev;
>>>> +
>>>> +    ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
>>>> +    if (!ep->mem_res)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> +    ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
>>>> +    if (IS_ERR(ep->mem_res->elbi_base))
>>>> +        return PTR_ERR(ep->mem_res->elbi_base);
>>>> +
>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>> +    ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
>>>> +    if (IS_ERR(ep->mem_res->phy_base))
>>>> +        return PTR_ERR(ep->mem_res->phy_base);
>>>> +
>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>> +    ep->mem_res->block_base = devm_ioremap_resource(dev, res);
>>>> +    if (IS_ERR(ep->mem_res->block_base))
>>>> +        return PTR_ERR(ep->mem_res->block_base);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
>>>> +{
>>>> +    struct device *dev = ep->pp.dev;
>>>> +
>>>> +    ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
>>>> +    if (!ep->clk_res)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    ep->clk_res->clk = devm_clk_get(dev, "pcie");
>>>> +    if (IS_ERR(ep->clk_res->clk)) {
>>>> +        dev_err(dev, "Failed to get pcie rc clock\n");
>>>> +        return PTR_ERR(ep->clk_res->clk);
>>>> +    }
>>>> +
>>>> +    ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>> +    if (IS_ERR(ep->clk_res->bus_clk)) {
>>>> +        dev_err(dev, "Failed to get pcie bus clock\n");
>>>> +        return PTR_ERR(ep->clk_res->bus_clk);
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
>>>> +{
>>>> +    struct device *dev = ep->pp.dev;
>>>> +    int ret;
>>>> +
>>>> +    ret = clk_prepare_enable(ep->clk_res->clk);
>>>> +    if (ret) {
>>>> +        dev_err(dev, "cannot enable pcie rc clock");
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    ret = clk_prepare_enable(ep->clk_res->bus_clk);
>>>> +    if (ret) {
>>>> +        dev_err(dev, "cannot enable pcie bus clock");
>>>> +        goto err_bus_clk;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +
>>>> +err_bus_clk:
>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
>>>> +{
>>>> +    clk_disable_unprepare(ep->clk_res->bus_clk);
>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>> +}
>>>> +
>>>> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
>>>> +    .get_mem_resources    = exynos5440_pcie_get_mem_resources,
>>>> +    .get_clk_resources    = exynos5440_pcie_get_clk_resources,
>>>> +    .init_clk_resources    = exynos5440_pcie_init_clk_resources,
>>>> +    .deinit_clk_resources    = exynos5440_pcie_deinit_clk_resources,
>>>> +};
>>>> +
>>>>    static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
>>>>    {
>>>>        writel(val, base + reg);
>>>> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>>        if (on)
>>>>            val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>        else
>>>>            val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>>    }
>>>>
>>>>    static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>>        if (on)
>>>>            val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>        else
>>>>            val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>>    }
>>>>
>>>>    static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>        val &= ~PCIE_CORE_RESET_ENABLE;
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>>    }
>>>>
>>>>    static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>        val |= PCIE_CORE_RESET_ENABLE;
>>>>
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
>>>>    }
>>>>
>>>>    static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
>>>>    {
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>>    }
>>>>
>>>>    static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
>>>>    {
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>>    }
>>>>
>>>>    static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>        val &= ~PCIE_PHY_COMMON_PD_CMN;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>        val &= ~PCIE_PHY_TRSV0_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>        val &= ~PCIE_PHY_TRSV1_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>        val &= ~PCIE_PHY_TRSV2_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>        val &= ~PCIE_PHY_TRSV3_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>    }
>>>>
>>>>    static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>        val |= PCIE_PHY_COMMON_PD_CMN;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>        val |= PCIE_PHY_TRSV0_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>        val |= PCIE_PHY_TRSV1_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>        val |= PCIE_PHY_TRSV2_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>
>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>        val |= PCIE_PHY_TRSV3_PD_TSV;
>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>    }
>>>>
>>>>    static void exynos_pcie_init_phy(struct exynos_pcie *ep)
>>>>    {
>>>>        /* DCC feedback control off */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>>
>>>>        /* set TX/RX impedance */
>>>> -    exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>>
>>>>        /* set 50Mhz PHY clock */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>>
>>>>        /* set TX Differential output for lane 0 */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>>
>>>>        /* set TX Pre-emphasis Level Control for lane 0 to minimum */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>>
>>>>        /* set RX clock and data recovery bandwidth */
>>>> -    exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>>
>>>>        /* change TX Pre-emphasis Level Control for lanes */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>>
>>>>        /* set LVCC */
>>>> -    exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>>    }
>>>>
>>>>    static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
>>>> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
>>>>        exynos_pcie_init_phy(exynos_pcie);
>>>>
>>>>        /* pulse for common reset */
>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
>>>> +                PCIE_PHY_COMMON_RESET);
>>>>        udelay(500);
>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
>>>> +                PCIE_PHY_COMMON_RESET);
>>>>
>>>>        exynos_pcie_deassert_core_reset(exynos_pcie);
>>>>        dw_pcie_setup_rc(pp);
>>>>        exynos_pcie_assert_reset(exynos_pcie);
>>>>
>>>>        /* assert LTSSM enable */
>>>> -    exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
>>>> -              PCIE_APP_LTSSM_ENABLE);
>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
>>>> +            PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>>>>
>>>>        /* check if the link is up or not */
>>>>        if (!dw_pcie_wait_for_link(pp))
>>>>            return 0;
>>>>
>>>> -    while (exynos_pcie_readl(exynos_pcie->phy_base,
>>>> +    while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
>>>>                    PCIE_PHY_PLL_LOCKED) == 0) {
>>>> -        val = exynos_pcie_readl(exynos_pcie->block_base,
>>>> +        val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
>>>>                    PCIE_PHY_PLL_LOCKED);
>>>>            dev_info(dev, "PLL Locked: 0x%x\n", val);
>>>>        }
>>>> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
>>>>    {
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
>>>>    }
>>>>
>>>>    static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>>        /* enable INTX interrupt */
>>>>        val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
>>>>            IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>>    }
>>>>
>>>>    static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
>>>> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
>>>>        dw_pcie_msi_init(pp);
>>>>
>>>>        /* enable MSI interrupt */
>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>>        val |= IRQ_MSI_ENABLE;
>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>>    }
>>>>
>>>>    static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
>>>> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
>>>>        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>>>>        u32 val;
>>>>
>>>> -    val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
>>>> +    val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
>>>> +                    PCIE_ELBI_RDLH_LINKUP);
>>>>        if (val == PCIE_ELBI_LTSSM_ENABLE)
>>>>            return 1;
>>>>
>>>> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>        struct exynos_pcie *exynos_pcie;
>>>>        struct pcie_port *pp;
>>>>        struct device_node *np = dev->of_node;
>>>> -    struct resource *res;
>>>>        int ret;
>>>>
>>>>        exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
>>>> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>        pp = &exynos_pcie->pp;
>>>>        pp->dev = dev;
>>>>
>>>> -    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>> -
>>>> -    exynos_pcie->clk = devm_clk_get(dev, "pcie");
>>>> -    if (IS_ERR(exynos_pcie->clk)) {
>>>> -        dev_err(dev, "Failed to get pcie rc clock\n");
>>>> -        return PTR_ERR(exynos_pcie->clk);
>>>> -    }
>>>> -    ret = clk_prepare_enable(exynos_pcie->clk);
>>>> -    if (ret)
>>>> -        return ret;
>>>> +    exynos_pcie->ops = (const struct exynos_pcie_ops *)
>>>> +                of_device_get_match_data(dev);
>>>>
>>>> -    exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>> -    if (IS_ERR(exynos_pcie->bus_clk)) {
>>>> -        dev_err(dev, "Failed to get pcie bus clock\n");
>>>> -        ret = PTR_ERR(exynos_pcie->bus_clk);
>>>> -        goto fail_clk;
>>>> -    }
>>>> -    ret = clk_prepare_enable(exynos_pcie->bus_clk);
>>>> -    if (ret)
>>>> -        goto fail_clk;
>>>> +    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>>
>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> -    exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
>>>> -    if (IS_ERR(exynos_pcie->elbi_base)) {
>>>> -        ret = PTR_ERR(exynos_pcie->elbi_base);
>>>> -        goto fail_bus_clk;
>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
>>>> +        ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
>>>> +        if (ret)
>>>> +            return ret;
>>>>        }
>>>>
>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>> -    exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
>>>> -    if (IS_ERR(exynos_pcie->phy_base)) {
>>>> -        ret = PTR_ERR(exynos_pcie->phy_base);
>>>> -        goto fail_bus_clk;
>>>> -    }
>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
>>>> +        ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
>>>> +        if (ret)
>>>> +            return ret;
>>>>
>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>> -    exynos_pcie->block_base = devm_ioremap_resource(dev, res);
>>>> -    if (IS_ERR(exynos_pcie->block_base)) {
>>>> -        ret = PTR_ERR(exynos_pcie->block_base);
>>>> -        goto fail_bus_clk;
>>>> +        ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
>>>> +        if (ret)
>>>> +            return ret;
>>>>        }
>>>>
>>>>        ret = exynos_add_pcie_port(exynos_pcie, pdev);
>>>>        if (ret < 0)
>>>> -        goto fail_bus_clk;
>>>> +        goto fail_probe;
>>>>
>>>>        platform_set_drvdata(pdev, exynos_pcie);
>>>>        return 0;
>>>>
>>>> -fail_bus_clk:
>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>> -fail_clk:
>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>> +fail_probe:
>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>        return ret;
>>>>    }
>>>>
>>>> @@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
>>>>    {
>>>>        struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>>>>
>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>
>>>>        return 0;
>>>>    }
>>>>
>>>>    static const struct of_device_id exynos_pcie_of_match[] = {
>>>> -    { .compatible = "samsung,exynos5440-pcie", },
>>>> +    {    .compatible = "samsung,exynos5440-pcie",
>>>> +        .data = &exynos5440_pcie_ops },
>>>>        {},
>>>>    };
>>>>
>>>>
>>>
>>>
>>>
>>
>>
>
>
>

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Jaehoon Chung @ 2016-12-27  2:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5861CE23.9060202@samsung.com>

On 12/27/2016 11:12 AM, Alim Akhtar wrote:
> 
> 
> On 12/27/2016 06:39 AM, Jaehoon Chung wrote:
>> Dear Alim,
>>
>> On 12/26/2016 06:46 PM, Alim Akhtar wrote:
>>> Hello Jaehoon
>>>
>>> On 12/26/2016 02:32 PM, Jaehoon Chung wrote:
>>>> Hi Pankaj,
>>>>
>>>> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>>>>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>>>
>>>>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>>>>> This patch does refactoring of Exynos PCIe driver to extend support
>>>>> for other Exynos SoC.
>>>>>
>>>>> Following are the main changes done via this patch:
>>>>> 1) It adds separate structs for memory, clock resources.
>>>>> 2) It add exynos_pcie_ops struct which will allow us to support the
>>>>> differences in resources in different Exynos SoC.
>>>>
>>>> It's nice to me for reusing this file.
>>>> but after considering too many times, i decided not to use this file.
>>>>
>>>> I'm not sure what block base is..actually this pci-exynos.c is really black-box.
>>>> (No one maintains this file, even Samsung didn't care.)
>>>> Who is using this?
>>>> If Someone can share the information about exynos5440, i can refactor everything.
>>>> Otherwise, there are two solution..
>>>>
>>>> One is "adding the new pci-exynos.c" likes pci-exynos5433.c
>>>> Other is "refactor this file" under assuming the each register's usage.
>>>>
>>> Its alway good to reuse code as far as possible.
>>> I am yet to see the details of 5440, but since people are now going to support more Exynos variants, in my opinion, instead of adding pci-exynos5433.c, you can rename current pci-exynos.c to something like pci-exynos5440.c (only in case its too much changes needed to support 5433/exynos7) and lets have a common pci-exynos.c where we can add exynos7/5433 and others SoCs, AFAIK at least exynos7 and 5433 has similar pci controller.
>>
>> Yes, It's always good to reuse. but as you know, current pci-exynos.c file is really specific for only exynos5440.
>> I will try to check about combining.
>>
>>>> I want to use the PHY generic Framework for EXYNOS PCIe.
>>>>
>>> I don't think you have an option here, you should use generic PHY APIs, otherwise phy drive should go to drivers/misc.
>>> Other thoughts are welcome.
>>
>> Why go to drivers/misc? There is driver/phy/ for PHY generic Framework.
>> If i will touch this file, then i will put our phy-exynos-pcie file under driver/phy/ .
>> I already sent the patch for this. Could you check them?
>>
> My point was, if you are not going to use generic PHY APIs, then phy driver should go into drivers/misc. Anyway as you said you have already posted patches with generic phy framework, I will take a look.

Ah. Right..And i'm doing the refactoring to reuse the current pci-exynos.c.
Maybe..Today or Tomorrow..I will send the patches..At that time, could you also check them?
Any comments might be helpful to me! :)

Best Regards,
Jaehoon Chung

>> http://patchwork.ozlabs.org/patch/708738/
>> http://patchwork.ozlabs.org/patch/708742/
>>
> Thanks.
> 
>> Best Regards,
>> Jaehoon Chung
>>
>>>
>>>> If you or other guys really want to use the pci-exynos.c for other exynos,
>>>> I will rework with PHY generic framework. Then i will resend the my patches as V2.
>>>>
>>>> One more thing..Does anyone know what the usage of block base is?
>>>> Can i use that register as "syscon"?
>>>>
>>>> Best Regards,
>>>> Jaehoon Chung
>>>>
>>>>>
>>>>> No functional change intended.
>>>>>
>>>>> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>>>>> ---
>>>>> This patch set is prepared on top of Krzysztof's for-next and
>>>>> PCIe driver cleanup patch [1] by Jaehoon Chung.
>>>>>
>>>>> [1]: https://lkml.org/lkml/2016/12/19/44
>>>>>
>>>>>
>>>>>    drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++----------------
>>>>>    1 file changed, 217 insertions(+), 129 deletions(-)
>>>>>
>>>>> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
>>>>> index 33562cf..2dc54f7 100644
>>>>> --- a/drivers/pci/host/pci-exynos.c
>>>>> +++ b/drivers/pci/host/pci-exynos.c
>>>>> @@ -17,6 +17,7 @@
>>>>>    #include <linux/interrupt.h>
>>>>>    #include <linux/kernel.h>
>>>>>    #include <linux/init.h>
>>>>> +#include <linux/of_device.h>
>>>>>    #include <linux/of_gpio.h>
>>>>>    #include <linux/pci.h>
>>>>>    #include <linux/platform_device.h>
>>>>> @@ -28,16 +29,6 @@
>>>>>
>>>>>    #define to_exynos_pcie(x)    container_of(x, struct exynos_pcie, pp)
>>>>>
>>>>> -struct exynos_pcie {
>>>>> -    struct pcie_port    pp;
>>>>> -    void __iomem        *elbi_base;    /* DT 0th resource */
>>>>> -    void __iomem        *phy_base;    /* DT 1st resource */
>>>>> -    void __iomem        *block_base;    /* DT 2nd resource */
>>>>> -    int            reset_gpio;
>>>>> -    struct clk        *clk;
>>>>> -    struct clk        *bus_clk;
>>>>> -};
>>>>> -
>>>>>    /* PCIe ELBI registers */
>>>>>    #define PCIE_IRQ_PULSE            0x000
>>>>>    #define IRQ_INTA_ASSERT            BIT(0)
>>>>> @@ -102,6 +93,122 @@ struct exynos_pcie {
>>>>>    #define PCIE_PHY_TRSV3_PD_TSV        BIT(7)
>>>>>    #define PCIE_PHY_TRSV3_LVCC        0x31c
>>>>>
>>>>> +struct exynos_pcie_mem_res {
>>>>> +    void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
>>>>> +    void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
>>>>> +    void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
>>>>> +};
>>>>> +
>>>>> +struct exynos_pcie_clk_res {
>>>>> +    struct clk *clk;
>>>>> +    struct clk *bus_clk;
>>>>> +};
>>>>> +
>>>>> +struct exynos_pcie {
>>>>> +    struct pcie_port        pp;
>>>>> +    struct exynos_pcie_mem_res    *mem_res;
>>>>> +    struct exynos_pcie_clk_res    *clk_res;
>>>>> +    const struct exynos_pcie_ops    *ops;
>>>>> +    int                reset_gpio;
>>>>> +};
>>>>> +
>>>>> +struct exynos_pcie_ops {
>>>>> +    int (*get_mem_resources)(struct platform_device *pdev,
>>>>> +            struct exynos_pcie *ep);
>>>>> +    int (*get_clk_resources)(struct exynos_pcie *ep);
>>>>> +    int (*init_clk_resources)(struct exynos_pcie *ep);
>>>>> +    void (*deinit_clk_resources)(struct exynos_pcie *ep);
>>>>> +};
>>>>> +
>>>>> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
>>>>> +                        struct exynos_pcie *ep)
>>>>> +{
>>>>> +    struct resource *res;
>>>>> +    struct device *dev = ep->pp.dev;
>>>>> +
>>>>> +    ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
>>>>> +    if (!ep->mem_res)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>> +    ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
>>>>> +    if (IS_ERR(ep->mem_res->elbi_base))
>>>>> +        return PTR_ERR(ep->mem_res->elbi_base);
>>>>> +
>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>>> +    ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
>>>>> +    if (IS_ERR(ep->mem_res->phy_base))
>>>>> +        return PTR_ERR(ep->mem_res->phy_base);
>>>>> +
>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>>> +    ep->mem_res->block_base = devm_ioremap_resource(dev, res);
>>>>> +    if (IS_ERR(ep->mem_res->block_base))
>>>>> +        return PTR_ERR(ep->mem_res->block_base);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
>>>>> +{
>>>>> +    struct device *dev = ep->pp.dev;
>>>>> +
>>>>> +    ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
>>>>> +    if (!ep->clk_res)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    ep->clk_res->clk = devm_clk_get(dev, "pcie");
>>>>> +    if (IS_ERR(ep->clk_res->clk)) {
>>>>> +        dev_err(dev, "Failed to get pcie rc clock\n");
>>>>> +        return PTR_ERR(ep->clk_res->clk);
>>>>> +    }
>>>>> +
>>>>> +    ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>>> +    if (IS_ERR(ep->clk_res->bus_clk)) {
>>>>> +        dev_err(dev, "Failed to get pcie bus clock\n");
>>>>> +        return PTR_ERR(ep->clk_res->bus_clk);
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
>>>>> +{
>>>>> +    struct device *dev = ep->pp.dev;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = clk_prepare_enable(ep->clk_res->clk);
>>>>> +    if (ret) {
>>>>> +        dev_err(dev, "cannot enable pcie rc clock");
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    ret = clk_prepare_enable(ep->clk_res->bus_clk);
>>>>> +    if (ret) {
>>>>> +        dev_err(dev, "cannot enable pcie bus clock");
>>>>> +        goto err_bus_clk;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +
>>>>> +err_bus_clk:
>>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
>>>>> +{
>>>>> +    clk_disable_unprepare(ep->clk_res->bus_clk);
>>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>>> +}
>>>>> +
>>>>> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
>>>>> +    .get_mem_resources    = exynos5440_pcie_get_mem_resources,
>>>>> +    .get_clk_resources    = exynos5440_pcie_get_clk_resources,
>>>>> +    .init_clk_resources    = exynos5440_pcie_init_clk_resources,
>>>>> +    .deinit_clk_resources    = exynos5440_pcie_deinit_clk_resources,
>>>>> +};
>>>>> +
>>>>>    static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
>>>>>    {
>>>>>        writel(val, base + reg);
>>>>> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>>>        if (on)
>>>>>            val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>        else
>>>>>            val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>>>        if (on)
>>>>>            val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>        else
>>>>>            val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>>        val &= ~PCIE_CORE_RESET_ENABLE;
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>>        val |= PCIE_CORE_RESET_ENABLE;
>>>>>
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
>>>>>    {
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
>>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
>>>>>    {
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>        val &= ~PCIE_PHY_COMMON_PD_CMN;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>        val &= ~PCIE_PHY_TRSV0_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>        val &= ~PCIE_PHY_TRSV1_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>        val &= ~PCIE_PHY_TRSV2_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>        val &= ~PCIE_PHY_TRSV3_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>        val |= PCIE_PHY_COMMON_PD_CMN;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>        val |= PCIE_PHY_TRSV0_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>        val |= PCIE_PHY_TRSV1_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>        val |= PCIE_PHY_TRSV2_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>        val |= PCIE_PHY_TRSV3_PD_TSV;
>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_init_phy(struct exynos_pcie *ep)
>>>>>    {
>>>>>        /* DCC feedback control off */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>>>
>>>>>        /* set TX/RX impedance */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>>>
>>>>>        /* set 50Mhz PHY clock */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>>>
>>>>>        /* set TX Differential output for lane 0 */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>>>
>>>>>        /* set TX Pre-emphasis Level Control for lane 0 to minimum */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>>>
>>>>>        /* set RX clock and data recovery bandwidth */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>>>
>>>>>        /* change TX Pre-emphasis Level Control for lanes */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>>>
>>>>>        /* set LVCC */
>>>>> -    exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
>>>>> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
>>>>>        exynos_pcie_init_phy(exynos_pcie);
>>>>>
>>>>>        /* pulse for common reset */
>>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
>>>>> +                PCIE_PHY_COMMON_RESET);
>>>>>        udelay(500);
>>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
>>>>> +                PCIE_PHY_COMMON_RESET);
>>>>>
>>>>>        exynos_pcie_deassert_core_reset(exynos_pcie);
>>>>>        dw_pcie_setup_rc(pp);
>>>>>        exynos_pcie_assert_reset(exynos_pcie);
>>>>>
>>>>>        /* assert LTSSM enable */
>>>>> -    exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
>>>>> -              PCIE_APP_LTSSM_ENABLE);
>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
>>>>> +            PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>>>>>
>>>>>        /* check if the link is up or not */
>>>>>        if (!dw_pcie_wait_for_link(pp))
>>>>>            return 0;
>>>>>
>>>>> -    while (exynos_pcie_readl(exynos_pcie->phy_base,
>>>>> +    while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
>>>>>                    PCIE_PHY_PLL_LOCKED) == 0) {
>>>>> -        val = exynos_pcie_readl(exynos_pcie->block_base,
>>>>> +        val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
>>>>>                    PCIE_PHY_PLL_LOCKED);
>>>>>            dev_info(dev, "PLL Locked: 0x%x\n", val);
>>>>>        }
>>>>> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
>>>>>    {
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>>> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>>>        /* enable INTX interrupt */
>>>>>        val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
>>>>>            IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>>>    }
>>>>>
>>>>>    static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
>>>>> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
>>>>>        dw_pcie_msi_init(pp);
>>>>>
>>>>>        /* enable MSI interrupt */
>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>>>        val |= IRQ_MSI_ENABLE;
>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>>>    }
>>>>>
>>>>>    static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
>>>>> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
>>>>>        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>>>>>        u32 val;
>>>>>
>>>>> -    val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
>>>>> +    val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
>>>>> +                    PCIE_ELBI_RDLH_LINKUP);
>>>>>        if (val == PCIE_ELBI_LTSSM_ENABLE)
>>>>>            return 1;
>>>>>
>>>>> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>>        struct exynos_pcie *exynos_pcie;
>>>>>        struct pcie_port *pp;
>>>>>        struct device_node *np = dev->of_node;
>>>>> -    struct resource *res;
>>>>>        int ret;
>>>>>
>>>>>        exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
>>>>> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>>        pp = &exynos_pcie->pp;
>>>>>        pp->dev = dev;
>>>>>
>>>>> -    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>>> -
>>>>> -    exynos_pcie->clk = devm_clk_get(dev, "pcie");
>>>>> -    if (IS_ERR(exynos_pcie->clk)) {
>>>>> -        dev_err(dev, "Failed to get pcie rc clock\n");
>>>>> -        return PTR_ERR(exynos_pcie->clk);
>>>>> -    }
>>>>> -    ret = clk_prepare_enable(exynos_pcie->clk);
>>>>> -    if (ret)
>>>>> -        return ret;
>>>>> +    exynos_pcie->ops = (const struct exynos_pcie_ops *)
>>>>> +                of_device_get_match_data(dev);
>>>>>
>>>>> -    exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>>> -    if (IS_ERR(exynos_pcie->bus_clk)) {
>>>>> -        dev_err(dev, "Failed to get pcie bus clock\n");
>>>>> -        ret = PTR_ERR(exynos_pcie->bus_clk);
>>>>> -        goto fail_clk;
>>>>> -    }
>>>>> -    ret = clk_prepare_enable(exynos_pcie->bus_clk);
>>>>> -    if (ret)
>>>>> -        goto fail_clk;
>>>>> +    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>>>
>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>> -    exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
>>>>> -    if (IS_ERR(exynos_pcie->elbi_base)) {
>>>>> -        ret = PTR_ERR(exynos_pcie->elbi_base);
>>>>> -        goto fail_bus_clk;
>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
>>>>> +        ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
>>>>> +        if (ret)
>>>>> +            return ret;
>>>>>        }
>>>>>
>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>>> -    exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
>>>>> -    if (IS_ERR(exynos_pcie->phy_base)) {
>>>>> -        ret = PTR_ERR(exynos_pcie->phy_base);
>>>>> -        goto fail_bus_clk;
>>>>> -    }
>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
>>>>> +        ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
>>>>> +        if (ret)
>>>>> +            return ret;
>>>>>
>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>>> -    exynos_pcie->block_base = devm_ioremap_resource(dev, res);
>>>>> -    if (IS_ERR(exynos_pcie->block_base)) {
>>>>> -        ret = PTR_ERR(exynos_pcie->block_base);
>>>>> -        goto fail_bus_clk;
>>>>> +        ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
>>>>> +        if (ret)
>>>>> +            return ret;
>>>>>        }
>>>>>
>>>>>        ret = exynos_add_pcie_port(exynos_pcie, pdev);
>>>>>        if (ret < 0)
>>>>> -        goto fail_bus_clk;
>>>>> +        goto fail_probe;
>>>>>
>>>>>        platform_set_drvdata(pdev, exynos_pcie);
>>>>>        return 0;
>>>>>
>>>>> -fail_bus_clk:
>>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>>> -fail_clk:
>>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>>> +fail_probe:
>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>>        return ret;
>>>>>    }
>>>>>
>>>>> @@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
>>>>>    {
>>>>>        struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>>>>>
>>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>>
>>>>>        return 0;
>>>>>    }
>>>>>
>>>>>    static const struct of_device_id exynos_pcie_of_match[] = {
>>>>> -    { .compatible = "samsung,exynos5440-pcie", },
>>>>> +    {    .compatible = "samsung,exynos5440-pcie",
>>>>> +        .data = &exynos5440_pcie_ops },
>>>>>        {},
>>>>>    };
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>>
> 
> 

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Pankaj Dubey @ 2016-12-27  3:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <003f01d25d4b$96875e70$c3961b50$@gmail.com>

Hi Jingoo,

On 24 December 2016 at 00:07, Jingoo Han <jingoohan1@gmail.com> wrote:
> On Friday, December 23, 2016 5:56 AM, Pankaj Dubey wrote:
>>
>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>
>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>> This patch does refactoring of Exynos PCIe driver to extend support
>> for other Exynos SoC.
>>
>> Following are the main changes done via this patch:
>> 1) It adds separate structs for memory, clock resources.
>
> What is the reason to separate structs for these?
> Please add the reason to this commit message.
> It will be helpful.
>

As we know current driver only supports exynos5440 specific PCIe controller,
We also know for sure that different variant of Exynos SoC will have different
hardware resources such as iomem, clks, regmap handles etc. for PCIe
controller, so our intention behind separating these resources was to make
exynos_pcie struct simple instead of making it complex. All data structs will be
shared by all Exynos SoCs but different exynos_pcie_ops will give them freedom
to use them as per SoC need.

We have refactored current driver in such a way that, even with very minimal
information about exynos5440 we can add support for new SoC's PCIe controller
with maximum core reuse.

>> 2) It add exynos_pcie_ops struct which will allow us to support the
>> differences in resources in different Exynos SoC.
>

Thanks for review. Please let us know if you have any other concern, and it
would be great if you can review this patch more thoroughly.

Thanks,
Pankaj

> Good. I have no objection.
>
> Best regards,
> Jingoo Han
>
>>
>> No functional change intended.
>>
>> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>> ---
>> This patch set is prepared on top of Krzysztof's for-next and
>> PCIe driver cleanup patch [1] by Jaehoon Chung.
>>
>> [1]: https://lkml.org/lkml/2016/12/19/44
>>
>>
>>  drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++-----------
>> -----
>>  1 file changed, 217 insertions(+), 129 deletions(-)
>>
>> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
>> index 33562cf..2dc54f7 100644
>> --- a/drivers/pci/host/pci-exynos.c
>> +++ b/drivers/pci/host/pci-exynos.c
>> @@ -17,6 +17,7 @@
>>  #include <linux/interrupt.h>
>>  #include <linux/kernel.h>
>>  #include <linux/init.h>
>> +#include <linux/of_device.h>
>>  #include <linux/of_gpio.h>
>>  #include <linux/pci.h>
>>  #include <linux/platform_device.h>
>> @@ -28,16 +29,6 @@
>>
>>  #define to_exynos_pcie(x)    container_of(x, struct exynos_pcie, pp)
>>
>> -struct exynos_pcie {
>> -     struct pcie_port        pp;
>> -     void __iomem            *elbi_base;     /* DT 0th resource */
>> -     void __iomem            *phy_base;      /* DT 1st resource */
>> -     void __iomem            *block_base;    /* DT 2nd resource */
>> -     int                     reset_gpio;
>> -     struct clk              *clk;
>> -     struct clk              *bus_clk;
>> -};
>> -
>>  /* PCIe ELBI registers */
>>  #define PCIE_IRQ_PULSE                       0x000
>>  #define IRQ_INTA_ASSERT                      BIT(0)
>> @@ -102,6 +93,122 @@ struct exynos_pcie {
>>  #define PCIE_PHY_TRSV3_PD_TSV                BIT(7)
>>  #define PCIE_PHY_TRSV3_LVCC          0x31c
>>
>> +struct exynos_pcie_mem_res {
>> +     void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
>> +     void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
>> +     void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL
>> */
>> +};
>> +
>> +struct exynos_pcie_clk_res {
>> +     struct clk *clk;
>> +     struct clk *bus_clk;
>> +};
>> +
>> +struct exynos_pcie {
>> +     struct pcie_port                pp;
>> +     struct exynos_pcie_mem_res      *mem_res;
>> +     struct exynos_pcie_clk_res      *clk_res;
>> +     const struct exynos_pcie_ops    *ops;
>> +     int                             reset_gpio;
>> +};
>> +
>> +struct exynos_pcie_ops {
>> +     int (*get_mem_resources)(struct platform_device *pdev,
>> +                     struct exynos_pcie *ep);
>> +     int (*get_clk_resources)(struct exynos_pcie *ep);
>> +     int (*init_clk_resources)(struct exynos_pcie *ep);
>> +     void (*deinit_clk_resources)(struct exynos_pcie *ep);
>> +};
>> +
>> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
>> +                                             struct exynos_pcie *ep)
>> +{
>> +     struct resource *res;
>> +     struct device *dev = ep->pp.dev;
>> +
>> +     ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
>> +     if (!ep->mem_res)
>> +             return -ENOMEM;
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
>> +     if (IS_ERR(ep->mem_res->elbi_base))
>> +             return PTR_ERR(ep->mem_res->elbi_base);
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +     ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
>> +     if (IS_ERR(ep->mem_res->phy_base))
>> +             return PTR_ERR(ep->mem_res->phy_base);
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +     ep->mem_res->block_base = devm_ioremap_resource(dev, res);
>> +     if (IS_ERR(ep->mem_res->block_base))
>> +             return PTR_ERR(ep->mem_res->block_base);
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
>> +{
>> +     struct device *dev = ep->pp.dev;
>> +
>> +     ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
>> +     if (!ep->clk_res)
>> +             return -ENOMEM;
>> +
>> +     ep->clk_res->clk = devm_clk_get(dev, "pcie");
>> +     if (IS_ERR(ep->clk_res->clk)) {
>> +             dev_err(dev, "Failed to get pcie rc clock\n");
>> +             return PTR_ERR(ep->clk_res->clk);
>> +     }
>> +
>> +     ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
>> +     if (IS_ERR(ep->clk_res->bus_clk)) {
>> +             dev_err(dev, "Failed to get pcie bus clock\n");
>> +             return PTR_ERR(ep->clk_res->bus_clk);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
>> +{
>> +     struct device *dev = ep->pp.dev;
>> +     int ret;
>> +
>> +     ret = clk_prepare_enable(ep->clk_res->clk);
>> +     if (ret) {
>> +             dev_err(dev, "cannot enable pcie rc clock");
>> +             return ret;
>> +     }
>> +
>> +     ret = clk_prepare_enable(ep->clk_res->bus_clk);
>> +     if (ret) {
>> +             dev_err(dev, "cannot enable pcie bus clock");
>> +             goto err_bus_clk;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_bus_clk:
>> +     clk_disable_unprepare(ep->clk_res->clk);
>> +
>> +     return ret;
>> +}
>> +
>> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
>> +{
>> +     clk_disable_unprepare(ep->clk_res->bus_clk);
>> +     clk_disable_unprepare(ep->clk_res->clk);
>> +}
>> +
>> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
>> +     .get_mem_resources      = exynos5440_pcie_get_mem_resources,
>> +     .get_clk_resources      = exynos5440_pcie_get_clk_resources,
>> +     .init_clk_resources     = exynos5440_pcie_init_clk_resources,
>> +     .deinit_clk_resources   = exynos5440_pcie_deinit_clk_resources,
>> +};
>> +
>>  static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
>>  {
>>       writel(val, base + reg);
>> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct
>> exynos_pcie *ep, bool on)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base,
>> PCIE_ELBI_SLV_AWMISC);
>>       if (on)
>>               val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>       else
>>               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val,
>> PCIE_ELBI_SLV_AWMISC);
>>  }
>>
>>  static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool
>> on)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base,
>> PCIE_ELBI_SLV_ARMISC);
>>       if (on)
>>               val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>       else
>>               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val,
>> PCIE_ELBI_SLV_ARMISC);
>>  }
>>
>>  static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>       val &= ~PCIE_CORE_RESET_ENABLE;
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>  }
>>
>>  static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>       val |= PCIE_CORE_RESET_ENABLE;
>>
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
>> -     exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
>>  }
>>
>>  static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
>>  {
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
>> -     exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 1,
>> PCIE_PHY_GLOBAL_RESET);
>>  }
>>
>>  static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
>>  {
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>> -     exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>> -     exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0,
>> PCIE_PHY_GLOBAL_RESET);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0,
>> PCIE_PHY_COMMON_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0,
>> PCIE_PHY_TRSVREG_RESET);
>> +     exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
>>  }
>>
>>  static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_COMMON_POWER);
>>       val &= ~PCIE_PHY_COMMON_PD_CMN;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_COMMON_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV0_POWER);
>>       val &= ~PCIE_PHY_TRSV0_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV0_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV1_POWER);
>>       val &= ~PCIE_PHY_TRSV1_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV1_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV2_POWER);
>>       val &= ~PCIE_PHY_TRSV2_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV2_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV3_POWER);
>>       val &= ~PCIE_PHY_TRSV3_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV3_POWER);
>>  }
>>
>>  static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_COMMON_POWER);
>>       val |= PCIE_PHY_COMMON_PD_CMN;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_COMMON_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV0_POWER);
>>       val |= PCIE_PHY_TRSV0_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV0_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV1_POWER);
>>       val |= PCIE_PHY_TRSV1_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV1_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV2_POWER);
>>       val |= PCIE_PHY_TRSV2_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV2_POWER);
>>
>> -     val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>> +     val = exynos_pcie_readl(ep->mem_res->phy_base,
>> PCIE_PHY_TRSV3_POWER);
>>       val |= PCIE_PHY_TRSV3_PD_TSV;
>> -     exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, val,
>> PCIE_PHY_TRSV3_POWER);
>>  }
>>
>>  static void exynos_pcie_init_phy(struct exynos_pcie *ep)
>>  {
>>       /* DCC feedback control off */
>> -     exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x29,
>> PCIE_PHY_DCC_FEEDBACK);
>>
>>       /* set TX/RX impedance */
>> -     exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>
>>       /* set 50Mhz PHY clock */
>> -     exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>> -     exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>
>>       /* set TX Differential output for lane 0 */
>> -     exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x7f,
>> PCIE_PHY_TRSV0_DRV_LVL);
>>
>>       /* set TX Pre-emphasis Level Control for lane 0 to minimum */
>> -     exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x0,
>> PCIE_PHY_TRSV0_EMP_LVL);
>>
>>       /* set RX clock and data recovery bandwidth */
>> -     exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>> -     exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>> -     exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>> -     exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>> -     exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
>> PCIE_PHY_TRSV0_RXCDR);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
>> PCIE_PHY_TRSV1_RXCDR);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
>> PCIE_PHY_TRSV2_RXCDR);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x82,
>> PCIE_PHY_TRSV3_RXCDR);
>>
>>       /* change TX Pre-emphasis Level Control for lanes */
>> -     exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>> -     exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>> -     exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>> -     exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
>> PCIE_PHY_TRSV0_EMP_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
>> PCIE_PHY_TRSV1_EMP_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
>> PCIE_PHY_TRSV2_EMP_LVL);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x39,
>> PCIE_PHY_TRSV3_EMP_LVL);
>>
>>       /* set LVCC */
>> -     exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>> -     exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>> -     exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>> -     exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0x20,
>> PCIE_PHY_TRSV0_LVCC);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
>> PCIE_PHY_TRSV1_LVCC);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
>> PCIE_PHY_TRSV2_LVCC);
>> +     exynos_pcie_writel(ep->mem_res->phy_base, 0xa0,
>> PCIE_PHY_TRSV3_LVCC);
>>  }
>>
>>  static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
>> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct
>> exynos_pcie *exynos_pcie)
>>       exynos_pcie_init_phy(exynos_pcie);
>>
>>       /* pulse for common reset */
>> -     exynos_pcie_writel(exynos_pcie->block_base, 1,
>> PCIE_PHY_COMMON_RESET);
>> +     exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
>> +                             PCIE_PHY_COMMON_RESET);
>>       udelay(500);
>> -     exynos_pcie_writel(exynos_pcie->block_base, 0,
>> PCIE_PHY_COMMON_RESET);
>> +     exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
>> +                             PCIE_PHY_COMMON_RESET);
>>
>>       exynos_pcie_deassert_core_reset(exynos_pcie);
>>       dw_pcie_setup_rc(pp);
>>       exynos_pcie_assert_reset(exynos_pcie);
>>
>>       /* assert LTSSM enable */
>> -     exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
>> -                       PCIE_APP_LTSSM_ENABLE);
>> +     exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
>> +                     PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>>
>>       /* check if the link is up or not */
>>       if (!dw_pcie_wait_for_link(pp))
>>               return 0;
>>
>> -     while (exynos_pcie_readl(exynos_pcie->phy_base,
>> +     while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
>>                               PCIE_PHY_PLL_LOCKED) == 0) {
>> -             val = exynos_pcie_readl(exynos_pcie->block_base,
>> +             val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
>>                               PCIE_PHY_PLL_LOCKED);
>>               dev_info(dev, "PLL Locked: 0x%x\n", val);
>>       }
>> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct
>> exynos_pcie *ep)
>>  {
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
>>  }
>>
>>  static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct
>> exynos_pcie *ep)
>>       /* enable INTX interrupt */
>>       val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
>>               IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>  }
>>
>>  static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
>> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie
>> *ep)
>>       dw_pcie_msi_init(pp);
>>
>>       /* enable MSI interrupt */
>> -     val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
>> +     val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
>>       val |= IRQ_MSI_ENABLE;
>> -     exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>> +     exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>  }
>>
>>  static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
>> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
>>       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>>       u32 val;
>>
>> -     val = exynos_pcie_readl(exynos_pcie->elbi_base,
>> PCIE_ELBI_RDLH_LINKUP);
>> +     val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
>> +                                     PCIE_ELBI_RDLH_LINKUP);
>>       if (val == PCIE_ELBI_LTSSM_ENABLE)
>>               return 1;
>>
>> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct
>> platform_device *pdev)
>>       struct exynos_pcie *exynos_pcie;
>>       struct pcie_port *pp;
>>       struct device_node *np = dev->of_node;
>> -     struct resource *res;
>>       int ret;
>>
>>       exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
>> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct
>> platform_device *pdev)
>>       pp = &exynos_pcie->pp;
>>       pp->dev = dev;
>>
>> -     exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>> -
>> -     exynos_pcie->clk = devm_clk_get(dev, "pcie");
>> -     if (IS_ERR(exynos_pcie->clk)) {
>> -             dev_err(dev, "Failed to get pcie rc clock\n");
>> -             return PTR_ERR(exynos_pcie->clk);
>> -     }
>> -     ret = clk_prepare_enable(exynos_pcie->clk);
>> -     if (ret)
>> -             return ret;
>> +     exynos_pcie->ops = (const struct exynos_pcie_ops *)
>> +                             of_device_get_match_data(dev);
>>
>> -     exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
>> -     if (IS_ERR(exynos_pcie->bus_clk)) {
>> -             dev_err(dev, "Failed to get pcie bus clock\n");
>> -             ret = PTR_ERR(exynos_pcie->bus_clk);
>> -             goto fail_clk;
>> -     }
>> -     ret = clk_prepare_enable(exynos_pcie->bus_clk);
>> -     if (ret)
>> -             goto fail_clk;
>> +     exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>
>> -     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> -     exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
>> -     if (IS_ERR(exynos_pcie->elbi_base)) {
>> -             ret = PTR_ERR(exynos_pcie->elbi_base);
>> -             goto fail_bus_clk;
>> +     if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
>> +             ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
>> +             if (ret)
>> +                     return ret;
>>       }
>>
>> -     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> -     exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
>> -     if (IS_ERR(exynos_pcie->phy_base)) {
>> -             ret = PTR_ERR(exynos_pcie->phy_base);
>> -             goto fail_bus_clk;
>> -     }
>> +     if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
>> +             ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
>> +             if (ret)
>> +                     return ret;
>>
>> -     res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> -     exynos_pcie->block_base = devm_ioremap_resource(dev, res);
>> -     if (IS_ERR(exynos_pcie->block_base)) {
>> -             ret = PTR_ERR(exynos_pcie->block_base);
>> -             goto fail_bus_clk;
>> +             ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
>> +             if (ret)
>> +                     return ret;
>>       }
>>
>>       ret = exynos_add_pcie_port(exynos_pcie, pdev);
>>       if (ret < 0)
>> -             goto fail_bus_clk;
>> +             goto fail_probe;
>>
>>       platform_set_drvdata(pdev, exynos_pcie);
>>       return 0;
>>
>> -fail_bus_clk:
>> -     clk_disable_unprepare(exynos_pcie->bus_clk);
>> -fail_clk:
>> -     clk_disable_unprepare(exynos_pcie->clk);
>> +fail_probe:
>> +     if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>> +             exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>       return ret;
>>  }
>>
>> @@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct
>> platform_device *pdev)
>>  {
>>       struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>>
>> -     clk_disable_unprepare(exynos_pcie->bus_clk);
>> -     clk_disable_unprepare(exynos_pcie->clk);
>> +     if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>> +             exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>
>>       return 0;
>>  }
>>
>>  static const struct of_device_id exynos_pcie_of_match[] = {
>> -     { .compatible = "samsung,exynos5440-pcie", },
>> +     {       .compatible = "samsung,exynos5440-pcie",
>> +             .data = &exynos5440_pcie_ops },
>>       {},
>>  };
>>
>> --
>> 2.7.4
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v18 0/4] Mediatek MT8173 CMDQ support
From: HS Liao @ 2016-12-27  3:39 UTC (permalink / raw)
  To: linux-arm-kernel


Hi,

This is Mediatek MT8173 Command Queue(CMDQ) driver. The CMDQ is used
to help write registers with critical time limitation, such as
updating display configuration during the vblank. It controls Global
Command Engine (GCE) hardware to achieve this requirement.

These patches have a build dependency on top of v4.10-rc1.

Changes since v17:
 - rebase to v4.10-rc1

Best regards,
HS Liao

HS Liao (4):
  dt-bindings: soc: Add documentation for the MediaTek GCE unit
  mailbox: mediatek: Add Mediatek CMDQ driver
  arm64: dts: mt8173: Add GCE node
  soc: mediatek: Add Mediatek CMDQ helper

 .../devicetree/bindings/mailbox/mtk-gce.txt        |  43 ++
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |  10 +
 drivers/mailbox/Kconfig                            |  10 +
 drivers/mailbox/Makefile                           |   2 +
 drivers/mailbox/mtk-cmdq-mailbox.c                 | 632 +++++++++++++++++++++
 drivers/soc/mediatek/Kconfig                       |  11 +
 drivers/soc/mediatek/Makefile                      |   1 +
 drivers/soc/mediatek/mtk-cmdq-helper.c             | 310 ++++++++++
 include/linux/mailbox/mtk-cmdq-mailbox.h           |  75 +++
 include/linux/soc/mediatek/mtk-cmdq.h              | 174 ++++++
 10 files changed, 1268 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/mtk-gce.txt
 create mode 100644 drivers/mailbox/mtk-cmdq-mailbox.c
 create mode 100644 drivers/soc/mediatek/mtk-cmdq-helper.c
 create mode 100644 include/linux/mailbox/mtk-cmdq-mailbox.h
 create mode 100644 include/linux/soc/mediatek/mtk-cmdq.h

-- 
1.9.1

^ permalink raw reply

* [PATCH v18 1/4] dt-bindings: soc: Add documentation for the MediaTek GCE unit
From: HS Liao @ 2016-12-27  3:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482809961-31930-1-git-send-email-hs.liao@mediatek.com>

This adds documentation for the MediaTek Global Command Engine (GCE) unit
found in MT8173 SoCs.

Signed-off-by: HS Liao <hs.liao@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/mailbox/mtk-gce.txt        | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/mtk-gce.txt

diff --git a/Documentation/devicetree/bindings/mailbox/mtk-gce.txt b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt
new file mode 100644
index 0000000..d2d3ccb
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt
@@ -0,0 +1,43 @@
+MediaTek GCE
+===============
+
+The Global Command Engine (GCE) is used to help read/write registers with
+critical time limitation, such as updating display configuration during the
+vblank. The GCE can be used to implement the Command Queue (CMDQ) driver.
+
+CMDQ driver uses mailbox framework for communication. Please refer to
+mailbox.txt for generic information about mailbox device-tree bindings.
+
+Required properties:
+- compatible: Must be "mediatek,mt8173-gce"
+- reg: Address range of the GCE unit
+- interrupts: The interrupt signal from the GCE block
+- clock: Clocks according to the common clock binding
+- clock-names: Must be "gce" to stand for GCE clock
+- #mbox-cells: Should be 2
+
+Required properties for a client device:
+- mboxes: client use mailbox to communicate with GCE, it should have this
+  property and list of phandle, mailbox channel specifiers, and atomic
+  execution flag.
+
+Example:
+
+	gce: gce at 10212000 {
+		compatible = "mediatek,mt8173-gce";
+		reg = <0 0x10212000 0 0x1000>;
+		interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_GCE>;
+		clock-names = "gce";
+
+		#mbox-cells = <2>;
+	};
+
+Example for a client device:
+
+	mmsys: clock-controller at 14000000 {
+		compatible = "mediatek,mt8173-mmsys";
+		mboxes = <&gce 0 1 /* main display with atomic execution */
+			  &gce 1 1>; /* sub display with atomic execution */
+		...
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v18 2/4] mailbox: mediatek: Add Mediatek CMDQ driver
From: HS Liao @ 2016-12-27  3:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482809961-31930-1-git-send-email-hs.liao@mediatek.com>

This patch is first version of Mediatek Command Queue(CMDQ) driver. The
CMDQ is used to help write registers with critical time limitation,
such as updating display configuration during the vblank. It controls
Global Command Engine (GCE) hardware to achieve this requirement.
Currently, CMDQ only supports display related hardwares, but we expect
it can be extended to other hardwares for future requirements.

Signed-off-by: HS Liao <hs.liao@mediatek.com>
Signed-off-by: CK Hu <ck.hu@mediatek.com>
---
 drivers/mailbox/Kconfig                  |  10 +
 drivers/mailbox/Makefile                 |   2 +
 drivers/mailbox/mtk-cmdq-mailbox.c       | 632 +++++++++++++++++++++++++++++++
 include/linux/mailbox/mtk-cmdq-mailbox.h |  75 ++++
 4 files changed, 719 insertions(+)
 create mode 100644 drivers/mailbox/mtk-cmdq-mailbox.c
 create mode 100644 include/linux/mailbox/mtk-cmdq-mailbox.h

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ceff415..9108dd4 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -152,4 +152,14 @@ config BCM_PDC_MBOX
 	  Mailbox implementation for the Broadcom PDC ring manager,
 	  which provides access to various offload engines on Broadcom
 	  SoCs. Say Y here if you want to use the Broadcom PDC.
+
+config MTK_CMDQ_MBOX
+	bool "MediaTek CMDQ Mailbox Support"
+	depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
+	select MTK_INFRACFG
+	help
+	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
+	  mailbox driver. The CMDQ is used to help read/write registers with
+	  critical time limitation, such as updating display configuration
+	  during the vblank.
 endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 7dde4f6..fad8965 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -31,3 +31,5 @@ obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
 obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
 
 obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
+
+obj-$(CONFIG_MTK_CMDQ_MBOX)	+= mtk-cmdq-mailbox.o
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
new file mode 100644
index 0000000..8771e57
--- /dev/null
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * 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.
+ *
+ * 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/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox/mtk-cmdq-mailbox.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#define CMDQ_THR_MAX_COUNT		3 /* main, sub, general(misc) */
+#define CMDQ_OP_CODE_MASK		(0xff << CMDQ_OP_CODE_SHIFT)
+#define CMDQ_TIMEOUT_MS			1000
+#define CMDQ_IRQ_MASK			0xffff
+#define CMDQ_NUM_CMD(t)			(t->cmd_buf_size / CMDQ_INST_SIZE)
+
+#define CMDQ_CURR_IRQ_STATUS		0x10
+#define CMDQ_THR_SLOT_CYCLES		0x30
+
+#define CMDQ_THR_BASE			0x100
+#define CMDQ_THR_SIZE			0x80
+#define CMDQ_THR_WARM_RESET		0x00
+#define CMDQ_THR_ENABLE_TASK		0x04
+#define CMDQ_THR_SUSPEND_TASK		0x08
+#define CMDQ_THR_CURR_STATUS		0x0c
+#define CMDQ_THR_IRQ_STATUS		0x10
+#define CMDQ_THR_IRQ_ENABLE		0x14
+#define CMDQ_THR_CURR_ADDR		0x20
+#define CMDQ_THR_END_ADDR		0x24
+#define CMDQ_THR_WAIT_TOKEN		0x30
+
+#define CMDQ_THR_ENABLED		0x1
+#define CMDQ_THR_DISABLED		0x0
+#define CMDQ_THR_SUSPEND		0x1
+#define CMDQ_THR_RESUME			0x0
+#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
+#define CMDQ_THR_DO_WARM_RESET		BIT(0)
+#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
+#define CMDQ_THR_IRQ_DONE		0x1
+#define CMDQ_THR_IRQ_ERROR		0x12
+#define CMDQ_THR_IRQ_EN			(CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
+#define CMDQ_THR_IS_WAITING		BIT(31)
+
+#define CMDQ_JUMP_BY_OFFSET		0x10000000
+#define CMDQ_JUMP_BY_PA			0x10000001
+
+struct cmdq_thread {
+	struct mbox_chan	*chan;
+	void __iomem		*base;
+	struct list_head	task_busy_list;
+	struct timer_list	timeout;
+	bool			atomic_exec;
+};
+
+struct cmdq_task {
+	struct cmdq		*cmdq;
+	struct list_head	list_entry;
+	dma_addr_t		pa_base;
+	struct cmdq_thread	*thread;
+	struct cmdq_pkt		*pkt; /* the packet sent from mailbox client */
+};
+
+struct cmdq_clk_release {
+	struct cmdq		*cmdq;
+	struct work_struct	release_work;
+};
+
+struct cmdq {
+	struct mbox_controller	mbox;
+	void __iomem		*base;
+	u32			irq;
+	struct workqueue_struct	*clk_release_wq;
+	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
+	struct clk		*clock;
+	bool			suspended;
+};
+
+static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	u32 status;
+
+	writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);
+
+	/* If already disabled, treat as suspended successful. */
+	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+		return 0;
+
+	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
+			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
+		dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",
+			(u32)(thread->base - cmdq->base));
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void cmdq_thread_resume(struct cmdq_thread *thread)
+{
+	writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);
+}
+
+static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	u32 warm_reset;
+
+	writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);
+	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
+			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
+			0, 10)) {
+		dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",
+			(u32)(thread->base - cmdq->base));
+		return -EFAULT;
+	}
+	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
+	return 0;
+}
+
+static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	cmdq_thread_reset(cmdq, thread);
+	writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+}
+
+/* notify GCE to re-fetch commands by setting GCE thread PC */
+static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
+{
+	writel(readl(thread->base + CMDQ_THR_CURR_ADDR),
+	       thread->base + CMDQ_THR_CURR_ADDR);
+}
+
+static void cmdq_task_insert_into_thread(struct cmdq_task *task)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	struct cmdq_thread *thread = task->thread;
+	struct cmdq_task *prev_task = list_last_entry(
+			&thread->task_busy_list, typeof(*task), list_entry);
+	u64 *prev_task_base = prev_task->pkt->va_base;
+
+	/* let previous task jump to this task */
+	dma_sync_single_for_cpu(dev, prev_task->pa_base,
+				prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
+	prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
+		(u64)CMDQ_JUMP_BY_PA << 32 | task->pa_base;
+	dma_sync_single_for_device(dev, prev_task->pa_base,
+				   prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
+
+	cmdq_thread_invalidate_fetched_data(thread);
+}
+
+static bool cmdq_command_is_wfe(u64 cmd)
+{
+	u64 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+	u64 wfe_op = (u64)(CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT) << 32;
+	u64 wfe_mask = (u64)CMDQ_OP_CODE_MASK << 32 | 0xffffffff;
+
+	return ((cmd & wfe_mask) == (wfe_op | wfe_option));
+}
+
+/* we assume tasks in the same display GCE thread are waiting the same event. */
+static void cmdq_task_remove_wfe(struct cmdq_task *task)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	u64 *base = task->pkt->va_base;
+	int i;
+
+	dma_sync_single_for_cpu(dev, task->pa_base, task->pkt->cmd_buf_size,
+				DMA_TO_DEVICE);
+	for (i = 0; i < CMDQ_NUM_CMD(task->pkt); i++)
+		if (cmdq_command_is_wfe(base[i]))
+			base[i] = (u64)CMDQ_JUMP_BY_OFFSET << 32 |
+				  CMDQ_JUMP_PASS;
+	dma_sync_single_for_device(dev, task->pa_base, task->pkt->cmd_buf_size,
+				   DMA_TO_DEVICE);
+}
+
+static bool cmdq_thread_is_in_wfe(struct cmdq_thread *thread)
+{
+	return readl(thread->base + CMDQ_THR_WAIT_TOKEN) & CMDQ_THR_IS_WAITING;
+}
+
+static void cmdq_thread_wait_end(struct cmdq_thread *thread,
+				 unsigned long end_pa)
+{
+	struct device *dev = thread->chan->mbox->dev;
+	unsigned long curr_pa;
+
+	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_ADDR,
+			curr_pa, curr_pa == end_pa, 1, 20))
+		dev_err(dev, "GCE thread cannot run to end.\n");
+}
+
+static void cmdq_task_exec(struct cmdq_pkt *pkt, struct cmdq_thread *thread)
+{
+	struct cmdq *cmdq;
+	struct cmdq_task *task;
+	unsigned long curr_pa, end_pa, flags;
+
+	cmdq = dev_get_drvdata(thread->chan->mbox->dev);
+
+	/* Client should not flush new tasks if suspended. */
+	WARN_ON(cmdq->suspended);
+
+	task = kzalloc(sizeof(*task), GFP_ATOMIC);
+	task->cmdq = cmdq;
+	INIT_LIST_HEAD(&task->list_entry);
+	task->pa_base = dma_map_single(cmdq->mbox.dev, pkt->va_base,
+				       pkt->cmd_buf_size, DMA_TO_DEVICE);
+	task->thread = thread;
+	task->pkt = pkt;
+
+	if (list_empty(&thread->task_busy_list)) {
+		/*
+		 * Unlock for clk prepare (sleeping function).
+		 * This is safe since clk_prepare_enable has internal locks.
+		 */
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+		WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
+		spin_lock_irqsave(&thread->chan->lock, flags);
+
+		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
+
+		writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+		writel(task->pa_base + pkt->cmd_buf_size,
+		       thread->base + CMDQ_THR_END_ADDR);
+		writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
+		writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+
+		mod_timer(&thread->timeout,
+			  jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+	} else {
+		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+		curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+		end_pa = readl(thread->base + CMDQ_THR_END_ADDR);
+
+		/*
+		 * Atomic execution should remove the following wfe, i.e. only
+		 * wait event at first task, and prevent to pause when running.
+		 */
+		if (thread->atomic_exec) {
+			/* GCE is executing if command is not WFE */
+			if (!cmdq_thread_is_in_wfe(thread)) {
+				cmdq_thread_resume(thread);
+				cmdq_thread_wait_end(thread, end_pa);
+				WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+				/* set to this task directly */
+				writel(task->pa_base,
+				       thread->base + CMDQ_THR_CURR_ADDR);
+			} else {
+				cmdq_task_insert_into_thread(task);
+				cmdq_task_remove_wfe(task);
+				smp_mb(); /* modify jump before enable thread */
+			}
+		} else {
+			/* check boundary */
+			if (curr_pa == end_pa - CMDQ_INST_SIZE ||
+			    curr_pa == end_pa) {
+				/* set to this task directly */
+				writel(task->pa_base,
+				       thread->base + CMDQ_THR_CURR_ADDR);
+			} else {
+				cmdq_task_insert_into_thread(task);
+				smp_mb(); /* modify jump before enable thread */
+			}
+		}
+		writel(task->pa_base + pkt->cmd_buf_size,
+		       thread->base + CMDQ_THR_END_ADDR);
+		cmdq_thread_resume(thread);
+	}
+	list_move_tail(&task->list_entry, &thread->task_busy_list);
+}
+
+static void cmdq_task_exec_done(struct cmdq_task *task, bool err)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	struct cmdq_cb_data cmdq_cb_data;
+
+	dma_unmap_single(dev, task->pa_base, task->pkt->cmd_buf_size,
+			 DMA_TO_DEVICE);
+	if (task->pkt->cb.cb) {
+		cmdq_cb_data.err = err;
+		cmdq_cb_data.data = task->pkt->cb.data;
+		task->pkt->cb.cb(cmdq_cb_data);
+	}
+	list_del(&task->list_entry);
+}
+
+static void cmdq_task_handle_error(struct cmdq_task *task)
+{
+	struct cmdq_thread *thread = task->thread;
+	struct cmdq_task *next_task;
+
+	dev_err(task->cmdq->mbox.dev, "task 0x%p error\n", task);
+	WARN_ON(cmdq_thread_suspend(task->cmdq, thread) < 0);
+	next_task = list_first_entry_or_null(&thread->task_busy_list,
+			struct cmdq_task, list_entry);
+	if (next_task)
+		writel(next_task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+	cmdq_thread_resume(thread);
+}
+
+static void cmdq_clk_release_work(struct work_struct *work_item)
+{
+	struct cmdq_clk_release *clk_release = container_of(work_item,
+			struct cmdq_clk_release, release_work);
+	struct cmdq *cmdq = clk_release->cmdq;
+
+	clk_disable_unprepare(cmdq->clock);
+	kfree(clk_release);
+}
+
+static void cmdq_clk_release_schedule(struct cmdq *cmdq)
+{
+	struct cmdq_clk_release *clk_release;
+
+	clk_release = kmalloc(sizeof(*clk_release), GFP_ATOMIC);
+	clk_release->cmdq = cmdq;
+	INIT_WORK(&clk_release->release_work, cmdq_clk_release_work);
+	queue_work(cmdq->clk_release_wq, &clk_release->release_work);
+}
+
+static void cmdq_thread_irq_handler(struct cmdq *cmdq,
+				    struct cmdq_thread *thread)
+{
+	struct cmdq_task *task, *tmp, *curr_task = NULL;
+	u32 curr_pa, irq_flag, task_end_pa;
+	bool err;
+
+	irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
+	writel(~irq_flag, thread->base + CMDQ_THR_IRQ_STATUS);
+
+	/*
+	 * When ISR call this function, another CPU core could run
+	 * "release task" right before we acquire the spin lock, and thus
+	 * reset / disable this GCE thread, so we need to check the enable
+	 * bit of this GCE thread.
+	 */
+	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+		return;
+
+	if (irq_flag & CMDQ_THR_IRQ_ERROR)
+		err = true;
+	else if (irq_flag & CMDQ_THR_IRQ_DONE)
+		err = false;
+	else
+		return;
+
+	curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+
+	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+				 list_entry) {
+		task_end_pa = task->pa_base + task->pkt->cmd_buf_size;
+		if (curr_pa >= task->pa_base && curr_pa < task_end_pa)
+			curr_task = task;
+
+		if (!curr_task || curr_pa == task_end_pa - CMDQ_INST_SIZE) {
+			cmdq_task_exec_done(task, false);
+			kfree(task);
+		} else if (err) {
+			cmdq_task_exec_done(task, true);
+			cmdq_task_handle_error(curr_task);
+			kfree(task);
+		}
+
+		if (curr_task)
+			break;
+	}
+
+	if (list_empty(&thread->task_busy_list)) {
+		cmdq_thread_disable(cmdq, thread);
+		cmdq_clk_release_schedule(cmdq);
+	} else {
+		mod_timer(&thread->timeout,
+			  jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+	}
+}
+
+static irqreturn_t cmdq_irq_handler(int irq, void *dev)
+{
+	struct cmdq *cmdq = dev;
+	unsigned long irq_status, flags = 0L;
+	int bit;
+
+	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS) & CMDQ_IRQ_MASK;
+	if (!(irq_status ^ CMDQ_IRQ_MASK))
+		return IRQ_NONE;
+
+	for_each_clear_bit(bit, &irq_status, fls(CMDQ_IRQ_MASK)) {
+		struct cmdq_thread *thread = &cmdq->thread[bit];
+
+		spin_lock_irqsave(&thread->chan->lock, flags);
+		cmdq_thread_irq_handler(cmdq, thread);
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+	}
+	return IRQ_HANDLED;
+}
+
+static void cmdq_thread_handle_timeout(unsigned long data)
+{
+	struct cmdq_thread *thread = (struct cmdq_thread *)data;
+	struct cmdq *cmdq = container_of(thread->chan->mbox, struct cmdq, mbox);
+	struct cmdq_task *task, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&thread->chan->lock, flags);
+	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+
+	/*
+	 * Although IRQ is disabled, GCE continues to execute.
+	 * It may have pending IRQ before GCE thread is suspended,
+	 * so check this condition again.
+	 */
+	cmdq_thread_irq_handler(cmdq, thread);
+
+	if (list_empty(&thread->task_busy_list)) {
+		cmdq_thread_resume(thread);
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+		return;
+	}
+
+	dev_err(cmdq->mbox.dev, "timeout\n");
+	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+				 list_entry) {
+		cmdq_task_exec_done(task, true);
+		kfree(task);
+	}
+
+	cmdq_thread_resume(thread);
+	cmdq_thread_disable(cmdq, thread);
+	cmdq_clk_release_schedule(cmdq);
+	spin_unlock_irqrestore(&thread->chan->lock, flags);
+}
+
+static int cmdq_suspend(struct device *dev)
+{
+	struct cmdq *cmdq = dev_get_drvdata(dev);
+	struct cmdq_thread *thread;
+	int i;
+	bool task_running = false;
+
+	cmdq->suspended = true;
+
+	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+		thread = &cmdq->thread[i];
+		if (!list_empty(&thread->task_busy_list)) {
+			task_running = true;
+			break;
+		}
+	}
+
+	if (task_running)
+		dev_warn(dev, "exist running task(s) in suspend\n");
+
+	flush_workqueue(cmdq->clk_release_wq);
+	return 0;
+}
+
+static int cmdq_resume(struct device *dev)
+{
+	struct cmdq *cmdq = dev_get_drvdata(dev);
+
+	cmdq->suspended = false;
+	return 0;
+}
+
+static int cmdq_remove(struct platform_device *pdev)
+{
+	struct cmdq *cmdq = platform_get_drvdata(pdev);
+
+	destroy_workqueue(cmdq->clk_release_wq);
+	mbox_controller_unregister(&cmdq->mbox);
+	return 0;
+}
+
+static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+	cmdq_task_exec(data, chan->con_priv);
+	return 0;
+}
+
+static int cmdq_mbox_startup(struct mbox_chan *chan)
+{
+	return 0;
+}
+
+static void cmdq_mbox_shutdown(struct mbox_chan *chan)
+{
+}
+
+static bool cmdq_mbox_last_tx_done(struct mbox_chan *chan)
+{
+	return true;
+}
+
+static const struct mbox_chan_ops cmdq_mbox_chan_ops = {
+	.send_data = cmdq_mbox_send_data,
+	.startup = cmdq_mbox_startup,
+	.shutdown = cmdq_mbox_shutdown,
+	.last_tx_done = cmdq_mbox_last_tx_done,
+};
+
+static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
+		const struct of_phandle_args *sp)
+{
+	int ind = sp->args[0];
+	struct cmdq_thread *thread;
+
+	if (ind >= mbox->num_chans)
+		return ERR_PTR(-EINVAL);
+
+	thread = mbox->chans[ind].con_priv;
+	thread->atomic_exec = (sp->args[1] != 0);
+	thread->chan = &mbox->chans[ind];
+
+	return &mbox->chans[ind];
+}
+
+static int cmdq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct cmdq *cmdq;
+	int err, i;
+
+	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
+	if (!cmdq)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cmdq->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cmdq->base)) {
+		dev_err(dev, "failed to ioremap gce\n");
+		return PTR_ERR(cmdq->base);
+	}
+
+	cmdq->irq = platform_get_irq(pdev, 0);
+	if (!cmdq->irq) {
+		dev_err(dev, "failed to get irq\n");
+		return -EINVAL;
+	}
+	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
+			       "mtk_cmdq", cmdq);
+	if (err < 0) {
+		dev_err(dev, "failed to register ISR (%d)\n", err);
+		return err;
+	}
+
+	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
+		dev, cmdq->base, cmdq->irq);
+
+	cmdq->clock = devm_clk_get(dev, "gce");
+	if (IS_ERR(cmdq->clock)) {
+		dev_err(dev, "failed to get gce clk\n");
+		return PTR_ERR(cmdq->clock);
+	}
+
+	cmdq->mbox.dev = dev;
+	cmdq->mbox.chans = devm_kcalloc(dev, CMDQ_THR_MAX_COUNT,
+					sizeof(*cmdq->mbox.chans), GFP_KERNEL);
+	if (!cmdq->mbox.chans)
+		return -ENOMEM;
+
+	cmdq->mbox.num_chans = CMDQ_THR_MAX_COUNT;
+	cmdq->mbox.ops = &cmdq_mbox_chan_ops;
+	cmdq->mbox.of_xlate = cmdq_xlate;
+
+	/* make use of TXDONE_BY_ACK */
+	cmdq->mbox.txdone_irq = false;
+	cmdq->mbox.txdone_poll = false;
+
+	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
+				CMDQ_THR_SIZE * i;
+		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
+		init_timer(&cmdq->thread[i].timeout);
+		cmdq->thread[i].timeout.function = cmdq_thread_handle_timeout;
+		cmdq->thread[i].timeout.data = (unsigned long)&cmdq->thread[i];
+		cmdq->mbox.chans[i].con_priv = &cmdq->thread[i];
+	}
+
+	err = mbox_controller_register(&cmdq->mbox);
+	if (err < 0) {
+		dev_err(dev, "failed to register mailbox: %d\n", err);
+		return err;
+	}
+
+	cmdq->clk_release_wq = alloc_ordered_workqueue(
+			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
+			"cmdq_clk_release");
+
+	platform_set_drvdata(pdev, cmdq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops cmdq_pm_ops = {
+	.suspend = cmdq_suspend,
+	.resume = cmdq_resume,
+};
+
+static const struct of_device_id cmdq_of_ids[] = {
+	{.compatible = "mediatek,mt8173-gce",},
+	{}
+};
+
+static struct platform_driver cmdq_drv = {
+	.probe = cmdq_probe,
+	.remove = cmdq_remove,
+	.driver = {
+		.name = "mtk_cmdq",
+		.pm = &cmdq_pm_ops,
+		.of_match_table = cmdq_of_ids,
+	}
+};
+
+builtin_platform_driver(cmdq_drv);
diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h
new file mode 100644
index 0000000..3433c64
--- /dev/null
+++ b/include/linux/mailbox/mtk-cmdq-mailbox.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef __MTK_CMDQ_MAILBOX_H__
+#define __MTK_CMDQ_MAILBOX_H__
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
+#define CMDQ_OP_CODE_SHIFT		24
+#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
+
+#define CMDQ_WFE_UPDATE			BIT(31)
+#define CMDQ_WFE_WAIT			BIT(15)
+#define CMDQ_WFE_WAIT_VALUE		0x1
+
+/*
+ * CMDQ_CODE_MASK:
+ *   set write mask
+ *   format: op mask
+ * CMDQ_CODE_WRITE:
+ *   write value into target register
+ *   format: op subsys address value
+ * CMDQ_CODE_JUMP:
+ *   jump by offset
+ *   format: op offset
+ * CMDQ_CODE_WFE:
+ *   wait for event and clear
+ *   it is just clear if no wait
+ *   format: [wait]  op event update:1 to_wait:1 wait:1
+ *           [clear] op event update:1 to_wait:0 wait:0
+ * CMDQ_CODE_EOC:
+ *   end of command
+ *   format: op irq_flag
+ */
+enum cmdq_code {
+	CMDQ_CODE_MASK = 0x02,
+	CMDQ_CODE_WRITE = 0x04,
+	CMDQ_CODE_JUMP = 0x10,
+	CMDQ_CODE_WFE = 0x20,
+	CMDQ_CODE_EOC = 0x40,
+};
+
+struct cmdq_cb_data {
+	bool	err;
+	void	*data;
+};
+
+typedef void (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
+
+struct cmdq_task_cb {
+	cmdq_async_flush_cb	cb;
+	void			*data;
+};
+
+struct cmdq_pkt {
+	void			*va_base;
+	size_t			cmd_buf_size; /* command occupied size */
+	size_t			buf_size; /* real buffer size */
+	struct cmdq_task_cb	cb;
+};
+
+#endif /* __MTK_CMDQ_MAILBOX_H__ */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v18 3/4] arm64: dts: mt8173: Add GCE node
From: HS Liao @ 2016-12-27  3:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482809961-31930-1-git-send-email-hs.liao@mediatek.com>

This patch adds the device node of the GCE hardware for CMDQ module.

Signed-off-by: HS Liao <hs.liao@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8173.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 12e7027..9f93447 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -422,6 +422,16 @@
 			status = "disabled";
 		};
 
+		gce: gce at 10212000 {
+			compatible = "mediatek,mt8173-gce";
+			reg = <0 0x10212000 0 0x1000>;
+			interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+			clocks = <&infracfg CLK_INFRA_GCE>;
+			clock-names = "gce";
+
+			#mbox-cells = <2>;
+		};
+
 		mipi_tx0: mipi-dphy at 10215000 {
 			compatible = "mediatek,mt8173-mipi-tx";
 			reg = <0 0x10215000 0 0x1000>;
-- 
1.9.1

^ permalink raw reply related

* [PATCH v18 4/4] soc: mediatek: Add Mediatek CMDQ helper
From: HS Liao @ 2016-12-27  3:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482809961-31930-1-git-send-email-hs.liao@mediatek.com>

Add Mediatek CMDQ helper to create CMDQ packet and assemble GCE op code.

Signed-off-by: HS Liao <hs.liao@mediatek.com>
---
 drivers/soc/mediatek/Kconfig           |  11 ++
 drivers/soc/mediatek/Makefile          |   1 +
 drivers/soc/mediatek/mtk-cmdq-helper.c | 310 +++++++++++++++++++++++++++++++++
 include/linux/soc/mediatek/mtk-cmdq.h  | 174 ++++++++++++++++++
 4 files changed, 496 insertions(+)
 create mode 100644 drivers/soc/mediatek/mtk-cmdq-helper.c
 create mode 100644 include/linux/soc/mediatek/mtk-cmdq.h

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index 609bb34..726c09a 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -1,6 +1,17 @@
 #
 # MediaTek SoC drivers
 #
+config MTK_CMDQ
+	bool "MediaTek CMDQ Support"
+	depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
+	select MTK_CMDQ_MBOX
+	select MTK_INFRACFG
+	help
+	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
+	  driver. The CMDQ is used to help read/write registers with critical
+	  time limitation, such as updating display configuration during the
+	  vblank.
+
 config MTK_INFRACFG
 	bool "MediaTek INFRACFG Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 12998b0..64ce5ee 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
 obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
 obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c
new file mode 100644
index 0000000..7809e65
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-cmdq-helper.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * 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.
+ *
+ * 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/completion.h>
+#include <linux/errno.h>
+#include <linux/of_address.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+
+#define CMDQ_SUBSYS_SHIFT	16
+#define CMDQ_ARG_A_WRITE_MASK	0xffff
+#define CMDQ_WRITE_ENABLE_MASK	BIT(0)
+#define CMDQ_EOC_IRQ_EN		BIT(0)
+#define CMDQ_EOC_CMD		((u64)((CMDQ_CODE_EOC << CMDQ_OP_CODE_SHIFT)) \
+				<< 32 | CMDQ_EOC_IRQ_EN)
+
+struct cmdq_subsys {
+	u32	base;
+	int	id;
+};
+
+static const struct cmdq_subsys gce_subsys[] = {
+	{0x1400, 1},
+	{0x1401, 2},
+	{0x1402, 3},
+};
+
+static int cmdq_subsys_base_to_id(u32 base)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
+		if (gce_subsys[i].base == base)
+			return gce_subsys[i].id;
+	return -EFAULT;
+}
+
+static int cmdq_pkt_realloc_cmd_buffer(struct cmdq_pkt *pkt, size_t size)
+{
+	void *new_buf;
+
+	new_buf = krealloc(pkt->va_base, size, GFP_KERNEL | __GFP_ZERO);
+	if (!new_buf)
+		return -ENOMEM;
+	pkt->va_base = new_buf;
+	pkt->buf_size = size;
+	return 0;
+}
+
+struct cmdq_base *cmdq_register_device(struct device *dev)
+{
+	struct cmdq_base *cmdq_base;
+	struct resource res;
+	int subsys;
+	u32 base;
+
+	if (of_address_to_resource(dev->of_node, 0, &res))
+		return NULL;
+	base = (u32)res.start;
+
+	subsys = cmdq_subsys_base_to_id(base >> 16);
+	if (subsys < 0)
+		return NULL;
+
+	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
+	if (!cmdq_base)
+		return NULL;
+	cmdq_base->subsys = subsys;
+	cmdq_base->base = base;
+
+	return cmdq_base;
+}
+EXPORT_SYMBOL(cmdq_register_device);
+
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index)
+{
+	struct cmdq_client *client;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	client->client.dev = dev;
+	client->client.tx_block = false;
+	client->chan = mbox_request_channel(&client->client, index);
+	return client;
+}
+EXPORT_SYMBOL(cmdq_mbox_create);
+
+void cmdq_mbox_destroy(struct cmdq_client *client)
+{
+	mbox_free_channel(client->chan);
+	kfree(client);
+}
+EXPORT_SYMBOL(cmdq_mbox_destroy);
+
+int cmdq_pkt_create(struct cmdq_pkt **pkt_ptr)
+{
+	struct cmdq_pkt *pkt;
+	int err;
+
+	pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+	if (!pkt)
+		return -ENOMEM;
+	err = cmdq_pkt_realloc_cmd_buffer(pkt, PAGE_SIZE);
+	if (err < 0) {
+		kfree(pkt);
+		return err;
+	}
+	*pkt_ptr = pkt;
+	return 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_create);
+
+void cmdq_pkt_destroy(struct cmdq_pkt *pkt)
+{
+	kfree(pkt->va_base);
+	kfree(pkt);
+}
+EXPORT_SYMBOL(cmdq_pkt_destroy);
+
+static bool cmdq_pkt_is_finalized(struct cmdq_pkt *pkt)
+{
+	u64 *expect_eoc;
+
+	if (pkt->cmd_buf_size < CMDQ_INST_SIZE << 1)
+		return false;
+
+	expect_eoc = pkt->va_base + pkt->cmd_buf_size - (CMDQ_INST_SIZE << 1);
+	if (*expect_eoc == CMDQ_EOC_CMD)
+		return true;
+
+	return false;
+}
+
+static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
+				   u32 arg_a, u32 arg_b)
+{
+	u64 *cmd_ptr;
+	int err;
+
+	if (WARN_ON(cmdq_pkt_is_finalized(pkt)))
+		return -EBUSY;
+	if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) {
+		err = cmdq_pkt_realloc_cmd_buffer(pkt, pkt->buf_size << 1);
+		if (err < 0)
+			return err;
+	}
+	cmd_ptr = pkt->va_base + pkt->cmd_buf_size;
+	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
+	pkt->cmd_buf_size += CMDQ_INST_SIZE;
+	return 0;
+}
+
+int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, struct cmdq_base *base,
+		   u32 offset)
+{
+	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
+		    (base->subsys << CMDQ_SUBSYS_SHIFT);
+	return cmdq_pkt_append_command(pkt, CMDQ_CODE_WRITE, arg_a, value);
+}
+EXPORT_SYMBOL(cmdq_pkt_write);
+
+int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
+			struct cmdq_base *base, u32 offset, u32 mask)
+{
+	u32 offset_mask = offset;
+	int err;
+
+	if (mask != 0xffffffff) {
+		err = cmdq_pkt_append_command(pkt, CMDQ_CODE_MASK, 0, ~mask);
+		if (err < 0)
+			return err;
+		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
+	}
+	return cmdq_pkt_write(pkt, value, base, offset_mask);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_mask);
+
+static const u32 cmdq_event_value[CMDQ_MAX_EVENT] = {
+	/* Display start of frame(SOF) events */
+	[CMDQ_EVENT_DISP_OVL0_SOF] = 11,
+	[CMDQ_EVENT_DISP_OVL1_SOF] = 12,
+	[CMDQ_EVENT_DISP_RDMA0_SOF] = 13,
+	[CMDQ_EVENT_DISP_RDMA1_SOF] = 14,
+	[CMDQ_EVENT_DISP_RDMA2_SOF] = 15,
+	[CMDQ_EVENT_DISP_WDMA0_SOF] = 16,
+	[CMDQ_EVENT_DISP_WDMA1_SOF] = 17,
+	/* Display end of frame(EOF) events */
+	[CMDQ_EVENT_DISP_OVL0_EOF] = 39,
+	[CMDQ_EVENT_DISP_OVL1_EOF] = 40,
+	[CMDQ_EVENT_DISP_RDMA0_EOF] = 41,
+	[CMDQ_EVENT_DISP_RDMA1_EOF] = 42,
+	[CMDQ_EVENT_DISP_RDMA2_EOF] = 43,
+	[CMDQ_EVENT_DISP_WDMA0_EOF] = 44,
+	[CMDQ_EVENT_DISP_WDMA1_EOF] = 45,
+	/* Mutex end of frame(EOF) events */
+	[CMDQ_EVENT_MUTEX0_STREAM_EOF] = 53,
+	[CMDQ_EVENT_MUTEX1_STREAM_EOF] = 54,
+	[CMDQ_EVENT_MUTEX2_STREAM_EOF] = 55,
+	[CMDQ_EVENT_MUTEX3_STREAM_EOF] = 56,
+	[CMDQ_EVENT_MUTEX4_STREAM_EOF] = 57,
+	/* Display underrun events */
+	[CMDQ_EVENT_DISP_RDMA0_UNDERRUN] = 63,
+	[CMDQ_EVENT_DISP_RDMA1_UNDERRUN] = 64,
+	[CMDQ_EVENT_DISP_RDMA2_UNDERRUN] = 65,
+};
+
+int cmdq_pkt_wfe(struct cmdq_pkt *pkt, enum cmdq_event event)
+{
+	u32 arg_b;
+
+	if (event >= CMDQ_MAX_EVENT || event < 0)
+		return -EINVAL;
+
+	/*
+	 * WFE arg_b
+	 * bit 0-11: wait value
+	 * bit 15: 1 - wait, 0 - no wait
+	 * bit 16-27: update value
+	 * bit 31: 1 - update, 0 - no update
+	 */
+	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+	return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE,
+			cmdq_event_value[event], arg_b);
+}
+EXPORT_SYMBOL(cmdq_pkt_wfe);
+
+int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, enum cmdq_event event)
+{
+	if (event >= CMDQ_MAX_EVENT || event < 0)
+		return -EINVAL;
+
+	return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE,
+			cmdq_event_value[event], CMDQ_WFE_UPDATE);
+}
+EXPORT_SYMBOL(cmdq_pkt_clear_event);
+
+static int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
+{
+	int err;
+
+	if (cmdq_pkt_is_finalized(pkt))
+		return 0;
+
+	/* insert EOC and generate IRQ for each command iteration */
+	err = cmdq_pkt_append_command(pkt, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
+	if (err < 0)
+		return err;
+
+	/* JUMP to end */
+	err = cmdq_pkt_append_command(pkt, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+int cmdq_pkt_flush_async(struct cmdq_client *client, struct cmdq_pkt *pkt,
+			 cmdq_async_flush_cb cb, void *data)
+{
+	int err;
+
+	err = cmdq_pkt_finalize(pkt);
+	if (err < 0)
+		return err;
+
+	pkt->cb.cb = cb;
+	pkt->cb.data = data;
+
+	mbox_send_message(client->chan, pkt);
+	/* We can send next packet immediately, so just call txdone. */
+	mbox_client_txdone(client->chan, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_flush_async);
+
+struct cmdq_flush_completion {
+	struct completion cmplt;
+	bool err;
+};
+
+static void cmdq_pkt_flush_cb(struct cmdq_cb_data data)
+{
+	struct cmdq_flush_completion *cmplt = data.data;
+
+	cmplt->err = data.err;
+	complete(&cmplt->cmplt);
+}
+
+int cmdq_pkt_flush(struct cmdq_client *client, struct cmdq_pkt *pkt)
+{
+	struct cmdq_flush_completion cmplt;
+	int err;
+
+	init_completion(&cmplt.cmplt);
+	err = cmdq_pkt_flush_async(client, pkt, cmdq_pkt_flush_cb, &cmplt);
+	if (err < 0)
+		return err;
+	wait_for_completion(&cmplt.cmplt);
+	return cmplt.err ? -EFAULT : 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_flush);
diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h
new file mode 100644
index 0000000..5b35d73
--- /dev/null
+++ b/include/linux/soc/mediatek/mtk-cmdq.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef __MTK_CMDQ_H__
+#define __MTK_CMDQ_H__
+
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/mtk-cmdq-mailbox.h>
+
+/* display events in command queue(CMDQ) */
+enum cmdq_event {
+	/* Display start of frame(SOF) events */
+	CMDQ_EVENT_DISP_OVL0_SOF,
+	CMDQ_EVENT_DISP_OVL1_SOF,
+	CMDQ_EVENT_DISP_RDMA0_SOF,
+	CMDQ_EVENT_DISP_RDMA1_SOF,
+	CMDQ_EVENT_DISP_RDMA2_SOF,
+	CMDQ_EVENT_DISP_WDMA0_SOF,
+	CMDQ_EVENT_DISP_WDMA1_SOF,
+	/* Display end of frame(EOF) events */
+	CMDQ_EVENT_DISP_OVL0_EOF,
+	CMDQ_EVENT_DISP_OVL1_EOF,
+	CMDQ_EVENT_DISP_RDMA0_EOF,
+	CMDQ_EVENT_DISP_RDMA1_EOF,
+	CMDQ_EVENT_DISP_RDMA2_EOF,
+	CMDQ_EVENT_DISP_WDMA0_EOF,
+	CMDQ_EVENT_DISP_WDMA1_EOF,
+	/* Mutex end of frame(EOF) events */
+	CMDQ_EVENT_MUTEX0_STREAM_EOF,
+	CMDQ_EVENT_MUTEX1_STREAM_EOF,
+	CMDQ_EVENT_MUTEX2_STREAM_EOF,
+	CMDQ_EVENT_MUTEX3_STREAM_EOF,
+	CMDQ_EVENT_MUTEX4_STREAM_EOF,
+	/* Display underrun events */
+	CMDQ_EVENT_DISP_RDMA0_UNDERRUN,
+	CMDQ_EVENT_DISP_RDMA1_UNDERRUN,
+	CMDQ_EVENT_DISP_RDMA2_UNDERRUN,
+	/* Keep this at the end */
+	CMDQ_MAX_EVENT,
+};
+
+struct cmdq_pkt;
+
+struct cmdq_base {
+	int	subsys;
+	u32	base;
+};
+
+struct cmdq_client {
+	struct mbox_client client;
+	struct mbox_chan *chan;
+};
+
+/**
+ * cmdq_register_device() - register device which needs CMDQ
+ * @dev:	device for CMDQ to access its registers
+ *
+ * Return: cmdq_base pointer or NULL for failed
+ */
+struct cmdq_base *cmdq_register_device(struct device *dev);
+
+/**
+ * cmdq_mbox_create() - create CMDQ mailbox client and channel
+ * @dev:	device of CMDQ mailbox client
+ * @index:	index of CMDQ mailbox channel
+ *
+ * Return: CMDQ mailbox client pointer
+ */
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index);
+
+/**
+ * cmdq_mbox_destroy() - destroy CMDQ mailbox client and channel
+ * @client:	the CMDQ mailbox client
+ */
+void cmdq_mbox_destroy(struct cmdq_client *client);
+
+/**
+ * cmdq_pkt_create() - create a CMDQ packet
+ * @pkt_ptr:	CMDQ packet pointer to retrieve cmdq_pkt
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_create(struct cmdq_pkt **pkt_ptr);
+
+/**
+ * cmdq_pkt_destroy() - destroy the CMDQ packet
+ * @pkt:	the CMDQ packet
+ */
+void cmdq_pkt_destroy(struct cmdq_pkt *pkt);
+
+/**
+ * cmdq_pkt_write() - append write command to the CMDQ packet
+ * @pkt:	the CMDQ packet
+ * @value:	the specified target register value
+ * @base:	the CMDQ base
+ * @offset:	register offset from module base
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value,
+		   struct cmdq_base *base, u32 offset);
+
+/**
+ * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet
+ * @pkt:	the CMDQ packet
+ * @value:	the specified target register value
+ * @base:	the CMDQ base
+ * @offset:	register offset from module base
+ * @mask:	the specified target register mask
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
+			struct cmdq_base *base, u32 offset, u32 mask);
+
+/**
+ * cmdq_pkt_wfe() - append wait for event command to the CMDQ packet
+ * @pkt:	the CMDQ packet
+ * @event:	the desired event type to "wait and CLEAR"
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_wfe(struct cmdq_pkt *pkt, enum cmdq_event event);
+
+/**
+ * cmdq_pkt_clear_event() - append clear event command to the CMDQ packet
+ * @pkt:	the CMDQ packet
+ * @event:	the desired event to be cleared
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, enum cmdq_event event);
+
+/**
+ * cmdq_pkt_flush() - trigger CMDQ to execute the CMDQ packet
+ * @client:	the CMDQ mailbox client
+ * @pkt:	the CMDQ packet
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to execute the CMDQ packet. Note that this is a
+ * synchronous flush function. When the function returned, the recorded
+ * commands have been done.
+ */
+int cmdq_pkt_flush(struct cmdq_client *client, struct cmdq_pkt *pkt);
+
+/**
+ * cmdq_pkt_flush_async() - trigger CMDQ to asynchronously execute the CMDQ
+ *                          packet and call back at the end of done packet
+ * @client:	the CMDQ mailbox client
+ * @pkt:	the CMDQ packet
+ * @cb:		called at the end of done packet
+ * @data:	this data will pass back to cb
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to asynchronously execute the CMDQ packet and call back
+ * at the end of done packet. Note that this is an ASYNC function. When the
+ * function returned, it may or may not be finished.
+ */
+int cmdq_pkt_flush_async(struct cmdq_client *client, struct cmdq_pkt *pkt,
+			 cmdq_async_flush_cb cb, void *data);
+
+#endif	/* __MTK_CMDQ_H__ */
-- 
1.9.1

^ permalink raw reply related

* [RFC PATCH 1/7] PF driver modified to enable HW filter support, changes works in backward compatibility mode Enable required things in Makefile Enable LZ4 dependecy inside config file
From: Sunil Kovvuri @ 2016-12-27  4:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <DM5PR07MB28428EE7B1679FAEC78103AD8D960@DM5PR07MB2842.namprd07.prod.outlook.com>

>>  #define NIC_MAX_RSS_HASH_BITS          8
>>  #define NIC_MAX_RSS_IDR_TBL_SIZE       (1 << NIC_MAX_RSS_HASH_BITS)
>> +#define NIC_TNS_RSS_IDR_TBL_SIZE       5
>
> So you want to use only 5 queues per VF when TNS is enabled, is it ??
> There are 4096 RSS indices in total, for each VF you can use max 32.
> I guess you wanted to set no of hash bits to 5 instead of table size.
>
> SATHA>>> We enabled 8 queues for VF. Yes Macro name misleads it has to be hash bits, will change this in next version

No, I am not referring to any discrepancy in naming the macro.
If you check your code

- hw->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE;
+ hw->rss_ind_tbl_size = veb_enabled ? NIC_TNS_RSS_IDR_TBL_SIZE :
+     NIC_MAX_RSS_IDR_TBL_SIZE;

You are setting RSS table size to 5, i.e RSS hash bits will be set to 2.
Hence only 4 queues (not even 5 as i mentioned earlier). Please check
'nicvf_rss_init' you will understand what i am saying.
Have you tested and observed pkts in all 8 queues ?

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Pankaj Dubey @ 2016-12-27  4:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e1f37a59-b633-7b9d-cc79-dbbb00a24cda@samsung.com>

Hi Jaehoon,

On 26 December 2016 at 14:32, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Pankaj,
>
> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>
>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>> This patch does refactoring of Exynos PCIe driver to extend support
>> for other Exynos SoC.
>>
>> Following are the main changes done via this patch:
>> 1) It adds separate structs for memory, clock resources.
>> 2) It add exynos_pcie_ops struct which will allow us to support the
>> differences in resources in different Exynos SoC.
>
> It's nice to me for reusing this file.
> but after considering too many times, i decided not to use this file.
>

It would be better if we redesign/modify existing driver to support new single
driver for all Exynos SoC.

For this we can have internal discussion and design it so that it
becomes suitable
for other Exynos SoC PCIe controllers.

> I'm not sure what block base is..actually this pci-exynos.c is really black-box.

I saw Jingoo already explained about block base, so it may or may not be used in
other exynos. If it is used they can reuse this variable and related
code, if not the
implementation of exynos_pcie_ops hooks should be written in such a way that
ignores (do not initializes/uses) block_base.

> (No one maintains this file, even Samsung didn't care.)
> Who is using this?

I feel now we we (you and me) will care for it, at least we have use
case for this now :)

> If Someone can share the information about exynos5440, i can refactor everything.
> Otherwise, there are two solution..
>

We also do not have access to exynos5440 hardware, but we do have access
to exynos5440 UM, so we can see some details of these SFRs. Based on
our understanding
we refactored this code for making it more suitable for other SoC.

Regarding phy_base and block_base as they are phy related it need to
be replaced with phy driver, so for this we can reuse your RFC patch
of pcie-phy. Even phy driver design should be
such that it can support multiple Exynos SoC PCIe-Phy.

> One is "adding the new pci-exynos.c" likes pci-exynos5433.c

I know exynos5433 and exynos7 SoC have similar PCIe controller, although they
may share comparatively less similarity with exynos5440. If start
adding new file for
each SoC we may end of lot of code duplication.

> Other is "refactor this file" under assuming the each register's usage.
>

IMHO, this would be good design decision.

We have separate various resources of exynos_pcie struct such as iomem
and clks for
the timebeing, and these resources can be managed via exynos_pcie_ops
which can have different implementation for different SoCs, we have
freedom to handle these resources per SoC. If some SoCs share similar
PCIe controller h/w design, code will be reused.

> I want to use the PHY generic Framework for EXYNOS PCIe.
>
> If you or other guys really want to use the pci-exynos.c for other exynos,
> I will rework with PHY generic framework. Then i will resend the my patches as V2.
>
> One more thing..Does anyone know what the usage of block base is?
> Can i use that register as "syscon"?
>

I think this is not exactly syscon, as it only be used in pcie-phy,
so we can use this as child device of phy-device.

> Best Regards,
> Jaehoon Chung
>

Thanks,
Pankaj Dubey

^ permalink raw reply

* [PATCH v2] watchdog: constify watchdog_info structures
From: Baruch Siach @ 2016-12-27  5:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482771911-13548-1-git-send-email-bhumirks@gmail.com>

Hi Bhumika Goyal,

On Mon, Dec 26, 2016 at 10:35:11PM +0530, Bhumika Goyal wrote:
> Declare watchdog_info structures as const as they are only stored in the
> info field of watchdog_device structures. This field is of type const
> struct watchdog_info *, so watchdog_info structures having this property
> can be declared const too.
> Done using Coccinelle:
> 
> @r1 disable optional_qualifier@
> identifier i;
> position p;
> @@
> static struct watchdog_info i at p={...};
> 
> @ok@
> identifier r1.i;
> position p;
> struct watchdog_device obj;
> @@
> obj.info=&i at p;
> 
> @bad@
> position p!={r1.p,ok.p};
> identifier r1.i;
> @@
> i at p
> 
> @depends on !bad disable optional_qualifier@
> identifier r1.i;
> @@
> +const
> struct watchdog_info i;
> 
> Signed-off-by: Bhumika Goyal <bhumirks@gmail.com>

For digicolor_wdt.c:

Acked-by: Baruch Siach <baruch@tkos.co.il>

Thanks,
baruch

-- 
     http://baruch.siach.name/blog/                  ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -

^ permalink raw reply

* [PATCH 1/3 v2] iio: adc: add device tree bindings for Qualcomm PM8xxx ADCs
From: Bjorn Andersson @ 2016-12-27  5:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1481842089-13999-1-git-send-email-linus.walleij@linaro.org>

On Thu 15 Dec 14:48 PST 2016, Linus Walleij wrote:

Sorry for taking so long time to give this a test.

> ---
>  .../bindings/iio/adc/qcom,pm8xxx-xoadc.txt         | 160 +++++++++++++++++++++
>  1 file changed, 160 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt
[..]
> +
> +- reg: should contain the ADC base address in the PMIC, typically
> +  0x197.

xoadc-ref is "required" by the code (setting 2.2V on the dummy regulator
returned in its absence will fail), so it should be mentioned here.

On 8064 pm8921_l14 seems to be our reference, which is 1.8V.

[..]
> +
> +- qcom,ratiometric:
> +  Value type: <empty>
> +  Definition: Channel calibration type. If this property is specified
> +          VADC will use the VDD reference (1.8V) and GND for channel
> +          calibration. If the property is not found, the channel will be
> +          calibrated with the 0.625V and 1.25V reference channels, also
> +          known as an absolute calibration.

So if I specify this property the reference should be 1.8V and if I
specify qcom,ratiometric-ref I will override this? Also, the
implementation requires that qcom,ratiometric-ref is specified if the
boolean property is used.

Am I missing something or should we just squash them?

> +
> +- qcom,ratiometric-ref:
> +  Value type: <u32>
> +  Definition: The reference voltage pair when using ratiometric
> +          calibration:
> +	  0 = XO_IN/XOADC_GND
> +	  1 = PMIC_IN/XOADC_GND
> +	  2 = PMIC_IN/BMS_CSP
> +	  3 (invalid)
> +	  4 = XOADC_GND/XOADC_GND
> +	  5 = XOADC_VREF/XOADC_GND
> +
> +Example:
> +
> +xoadc: xoadc at 197 {
> +	compatible = "qcom,pm8058-adc";
> +	reg = <0x197>;
> +	interrupt-parent = <&pm8058>;
> +	interrupts = <76 1>;
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +	#io-channel-cells = <1>;

xoadc-ref-supply = <>;

> +
> +	vcoin {

As the node has a "reg" you need to add @0 on these.

> +		reg = <0x00>;
> +	};

Regards,
Bjorn

^ permalink raw reply

* [PATCH v4 0/4] clk: rockchip: support clk controller for rk3328 SoC
From: Elaine Zhang @ 2016-12-27  6:32 UTC (permalink / raw)
  To: linux-arm-kernel

Changes in v4:
  dropping the "rockchip,cru" and "syscon" properties for bindings of rk3328
  adjust the pacth 3 and 4 order.
  move pll_rk3328 to patch 3.
Changes in v3:
  fix up the pll type pll_rk3328 description and use.
Changes in v2:
  add bindings for rk3328 clock controller

Elaine Zhang (4):
  clk: rockchip: add dt-binding header for rk3328
  dt-bindings: add bindings for rk3328 clock controller
  clk: rockchip: add new pll-type for rk3328
  clk: rockchip: add clock controller for rk3328

 .../bindings/clock/rockchip,rk3328-cru.txt         |   57 ++
 drivers/clk/rockchip/Makefile                      |    1 +
 drivers/clk/rockchip/clk-pll.c                     |   16 +-
 drivers/clk/rockchip/clk-rk3328.c                  | 1068 ++++++++++++++++++++
 drivers/clk/rockchip/clk.h                         |   23 +
 include/dt-bindings/clock/rk3328-cru.h             |  403 ++++++++
 6 files changed, 1565 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt
 create mode 100644 drivers/clk/rockchip/clk-rk3328.c
 create mode 100644 include/dt-bindings/clock/rk3328-cru.h

-- 
1.9.1

^ permalink raw reply

* [PATCH v4 1/4] clk: rockchip: add dt-binding header for rk3328
From: Elaine Zhang @ 2016-12-27  6:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482820373-10186-1-git-send-email-zhangqing@rock-chips.com>

Add the dt-bindings header for the rk3328, that gets shared between
the clock controller and the clock references in the dts.
Add softreset ID for rk3328.

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
 include/dt-bindings/clock/rk3328-cru.h | 403 +++++++++++++++++++++++++++++++++
 1 file changed, 403 insertions(+)
 create mode 100644 include/dt-bindings/clock/rk3328-cru.h

diff --git a/include/dt-bindings/clock/rk3328-cru.h b/include/dt-bindings/clock/rk3328-cru.h
new file mode 100644
index 000000000000..545ed7541316
--- /dev/null
+++ b/include/dt-bindings/clock/rk3328-cru.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Elaine <zhangqing@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3328_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3328_H
+
+/* core clocks */
+#define PLL_APLL		1
+#define PLL_DPLL		2
+#define PLL_CPLL		3
+#define PLL_GPLL		4
+#define PLL_NPLL		5
+#define ARMCLK			6
+
+/* sclk gates (special clocks) */
+#define SCLK_RTC32K		30
+#define SCLK_SDMMC_EXT		31
+#define SCLK_SPI		32
+#define SCLK_SDMMC		33
+#define SCLK_SDIO		34
+#define SCLK_EMMC		35
+#define SCLK_TSADC		36
+#define SCLK_SARADC		37
+#define SCLK_UART0		38
+#define SCLK_UART1		39
+#define SCLK_UART2		40
+#define SCLK_I2S0		41
+#define SCLK_I2S1		42
+#define SCLK_I2S2		43
+#define SCLK_I2S1_OUT		44
+#define SCLK_I2S2_OUT		45
+#define SCLK_SPDIF		46
+#define SCLK_TIMER0		47
+#define SCLK_TIMER1		48
+#define SCLK_TIMER2		49
+#define SCLK_TIMER3		50
+#define SCLK_TIMER4		51
+#define SCLK_TIMER5		52
+#define SCLK_WIFI		53
+#define SCLK_CIF_OUT		54
+#define SCLK_I2C0		55
+#define SCLK_I2C1		56
+#define SCLK_I2C2		57
+#define SCLK_I2C3		58
+#define SCLK_CRYPTO		59
+#define SCLK_PWM		60
+#define SCLK_PDM		61
+#define SCLK_EFUSE		62
+#define SCLK_OTP		63
+#define SCLK_DDRCLK		64
+#define SCLK_VDEC_CABAC		65
+#define SCLK_VDEC_CORE		66
+#define SCLK_VENC_DSP		67
+#define SCLK_VENC_CORE		68
+#define SCLK_RGA		69
+#define SCLK_HDMI_SFC		70
+#define SCLK_HDMI_CEC		71
+#define SCLK_USB3_REF		72
+#define SCLK_USB3_SUSPEND	73
+#define SCLK_SDMMC_DRV		74
+#define SCLK_SDIO_DRV		75
+#define SCLK_EMMC_DRV		76
+#define SCLK_SDMMC_EXT_DRV	77
+#define SCLK_SDMMC_SAMPLE	78
+#define SCLK_SDIO_SAMPLE	79
+#define SCLK_EMMC_SAMPLE	80
+#define SCLK_SDMMC_EXT_SAMPLE	81
+#define SCLK_VOP		82
+#define SCLK_MAC2PHY_RXTX	83
+#define SCLK_MAC2PHY_SRC	84
+#define SCLK_MAC2PHY_REF	85
+#define SCLK_MAC2PHY_OUT	86
+#define SCLK_MAC2IO_RX		87
+#define SCLK_MAC2IO_TX		88
+#define SCLK_MAC2IO_REFOUT	89
+#define SCLK_MAC2IO_REF		90
+#define SCLK_MAC2IO_OUT		91
+#define SCLK_TSP		92
+#define SCLK_HSADC_TSP		93
+#define SCLK_USB3PHY_REF	94
+#define SCLK_REF_USB3OTG	95
+#define SCLK_USB3OTG_REF	96
+#define SCLK_USB3OTG_SUSPEND	97
+#define SCLK_REF_USB3OTG_SRC	98
+#define SCLK_MAC2IO_SRC		99
+
+/* dclk gates */
+#define DCLK_LCDC		180
+#define DCLK_HDMIPHY		181
+#define HDMIPHY			182
+#define USB480M			183
+#define DCLK_LCDC_SRC		184
+
+/* aclk gates */
+#define ACLK_AXISRAM		190
+#define ACLK_VOP_PRE		191
+#define ACLK_USB3OTG		192
+#define ACLK_RGA_PRE		193
+#define ACLK_DMAC		194
+#define ACLK_GPU		195
+#define ACLK_BUS_PRE		196
+#define ACLK_PERI_PRE		197
+#define ACLK_RKVDEC_PRE		198
+#define ACLK_RKVDEC		199
+#define ACLK_RKVENC		200
+#define ACLK_VPU_PRE		201
+#define ACLK_VIO_PRE		202
+#define ACLK_VPU		203
+#define ACLK_VIO		204
+#define ACLK_VOP		205
+#define ACLK_GMAC		206
+#define ACLK_H265		207
+#define ACLK_H264		208
+#define ACLK_MAC2PHY		209
+#define ACLK_MAC2IO		210
+#define ACLK_DCF		211
+#define ACLK_TSP		212
+#define ACLK_PERI		213
+#define ACLK_RGA		214
+#define ACLK_IEP		215
+#define ACLK_CIF		216
+#define ACLK_HDCP		217
+
+/* pclk gates */
+#define PCLK_GPIO0		300
+#define PCLK_GPIO1		301
+#define PCLK_GPIO2		302
+#define PCLK_GPIO3		303
+#define PCLK_GRF		304
+#define PCLK_I2C0		305
+#define PCLK_I2C1		306
+#define PCLK_I2C2		307
+#define PCLK_I2C3		308
+#define PCLK_SPI		309
+#define PCLK_UART0		310
+#define PCLK_UART1		311
+#define PCLK_UART2		312
+#define PCLK_TSADC		313
+#define PCLK_PWM		314
+#define PCLK_TIMER		315
+#define PCLK_BUS_PRE		316
+#define PCLK_PERI_PRE		317
+#define PCLK_HDMI_CTRL		318
+#define PCLK_HDMI_PHY		319
+#define PCLK_GMAC		320
+#define PCLK_H265		321
+#define PCLK_MAC2PHY		322
+#define PCLK_MAC2IO		323
+#define PCLK_USB3PHY_OTG	324
+#define PCLK_USB3PHY_PIPE	325
+#define PCLK_USB3_GRF		326
+#define PCLK_USB2_GRF		327
+#define PCLK_HDMIPHY		328
+#define PCLK_DDR		329
+#define PCLK_PERI		330
+#define PCLK_HDMI		331
+#define PCLK_HDCP		332
+#define PCLK_DCF		333
+#define PCLK_SARADC		334
+
+/* hclk gates */
+#define HCLK_PERI		408
+#define HCLK_TSP		409
+#define HCLK_GMAC		410
+#define HCLK_I2S0_8CH		411
+#define HCLK_I2S1_8CH		413
+#define HCLK_I2S2_2CH		413
+#define HCLK_SPDIF_8CH		414
+#define HCLK_VOP		415
+#define HCLK_NANDC		416
+#define HCLK_SDMMC		417
+#define HCLK_SDIO		418
+#define HCLK_EMMC		419
+#define HCLK_SDMMC_EXT		420
+#define HCLK_RKVDEC_PRE		421
+#define HCLK_RKVDEC		422
+#define HCLK_RKVENC		423
+#define HCLK_VPU_PRE		424
+#define HCLK_VIO_PRE		425
+#define HCLK_VPU		426
+#define HCLK_VIO		427
+#define HCLK_BUS_PRE		428
+#define HCLK_PERI_PRE		429
+#define HCLK_H264		430
+#define HCLK_CIF		431
+#define HCLK_OTG_PMU		432
+#define HCLK_OTG		433
+#define HCLK_HOST0		434
+#define HCLK_HOST0_ARB		435
+#define HCLK_CRYPTO_MST		436
+#define HCLK_CRYPTO_SLV		437
+#define HCLK_PDM		438
+#define HCLK_IEP		439
+#define HCLK_RGA		440
+#define HCLK_HDCP		441
+
+#define CLK_NR_CLKS		(HCLK_HDCP + 1)
+
+#define SCLK_MAC2IO		0
+#define SCLK_MAC2PHY		1
+
+#define CLKGRF_NR_CLKS		(SCLK_MAC2PHY + 1)
+
+/* soft-reset indices */
+#define SRST_CORE0_PO		0
+#define SRST_CORE1_PO		1
+#define SRST_CORE2_PO		2
+#define SRST_CORE3_PO		3
+#define SRST_CORE0		4
+#define SRST_CORE1		5
+#define SRST_CORE2		6
+#define SRST_CORE3		7
+#define SRST_CORE0_DBG		8
+#define SRST_CORE1_DBG		9
+#define SRST_CORE2_DBG		10
+#define SRST_CORE3_DBG		11
+#define SRST_TOPDBG		12
+#define SRST_CORE_NIU		13
+#define SRST_STRC_A		14
+#define SRST_L2C		15
+
+#define SRST_A53_GIC		18
+#define SRST_DAP		19
+#define SRST_PMU_P		21
+#define SRST_EFUSE		22
+#define SRST_BUSSYS_H		23
+#define SRST_BUSSYS_P		24
+#define SRST_SPDIF		25
+#define SRST_INTMEM		26
+#define SRST_ROM		27
+#define SRST_GPIO0		28
+#define SRST_GPIO1		29
+#define SRST_GPIO2		30
+#define SRST_GPIO3		31
+
+#define SRST_I2S0		32
+#define SRST_I2S1		33
+#define SRST_I2S2		34
+#define SRST_I2S0_H		35
+#define SRST_I2S1_H		36
+#define SRST_I2S2_H		37
+#define SRST_UART0		38
+#define SRST_UART1		39
+#define SRST_UART2		40
+#define SRST_UART0_P		41
+#define SRST_UART1_P		42
+#define SRST_UART2_P		43
+#define SRST_I2C0		44
+#define SRST_I2C1		45
+#define SRST_I2C2		46
+#define SRST_I2C3		47
+
+#define SRST_I2C0_P		48
+#define SRST_I2C1_P		49
+#define SRST_I2C2_P		50
+#define SRST_I2C3_P		51
+#define SRST_EFUSE_SE_P		52
+#define SRST_EFUSE_NS_P		53
+#define SRST_PWM0		54
+#define SRST_PWM0_P		55
+#define SRST_DMA		56
+#define SRST_TSP_A		57
+#define SRST_TSP_H		58
+#define SRST_TSP		59
+#define SRST_TSP_HSADC		60
+#define SRST_DCF_A		61
+#define SRST_DCF_P		62
+
+#define SRST_SCR		64
+#define SRST_SPI		65
+#define SRST_TSADC		66
+#define SRST_TSADC_P		67
+#define SRST_CRYPTO		68
+#define SRST_SGRF		69
+#define SRST_GRF		70
+#define SRST_USB_GRF		71
+#define SRST_TIMER_6CH_P	72
+#define SRST_TIMER0		73
+#define SRST_TIMER1		74
+#define SRST_TIMER2		75
+#define SRST_TIMER3		76
+#define SRST_TIMER4		77
+#define SRST_TIMER5		78
+#define SRST_USB3GRF		79
+
+#define SRST_PHYNIU		80
+#define SRST_HDMIPHY		81
+#define SRST_VDAC		82
+#define SRST_ACODEC_p		83
+#define SRST_SARADC		85
+#define SRST_SARADC_P		86
+#define SRST_GRF_DDR		87
+#define SRST_DFIMON		88
+#define SRST_MSCH		89
+#define SRST_DDRMSCH		91
+#define SRST_DDRCTRL		92
+#define SRST_DDRCTRL_P		93
+#define SRST_DDRPHY		94
+#define SRST_DDRPHY_P		95
+
+#define SRST_GMAC_NIU_A		96
+#define SRST_GMAC_NIU_P		97
+#define SRST_GMAC2PHY_A		98
+#define SRST_GMAC2IO_A		99
+#define SRST_MACPHY		100
+#define SRST_OTP_PHY		101
+#define SRST_GPU_A		102
+#define SRST_GPU_NIU_A		103
+#define SRST_SDMMCEXT		104
+#define SRST_PERIPH_NIU_A	105
+#define SRST_PERIHP_NIU_H	106
+#define SRST_PERIHP_P		107
+#define SRST_PERIPHSYS_H	108
+#define SRST_MMC0		109
+#define SRST_SDIO		110
+#define SRST_EMMC		111
+
+#define SRST_USB2OTG_H		112
+#define SRST_USB2OTG		113
+#define SRST_USB2OTG_ADP	114
+#define SRST_USB2HOST_H		115
+#define SRST_USB2HOST_ARB	116
+#define SRST_USB2HOST_AUX	117
+#define SRST_USB2HOST_EHCIPHY	118
+#define SRST_USB2HOST_UTMI	119
+#define SRST_USB3OTG		120
+#define SRST_USBPOR		121
+#define SRST_USB2OTG_UTMI	122
+#define SRST_USB2HOST_PHY_UTMI	123
+#define SRST_USB3OTG_UTMI	124
+#define SRST_USB3PHY_U2		125
+#define SRST_USB3PHY_U3		126
+#define SRST_USB3PHY_PIPE	127
+
+#define SRST_VIO_A		128
+#define SRST_VIO_BUS_H		129
+#define SRST_VIO_H2P_H		130
+#define SRST_VIO_ARBI_H		131
+#define SRST_VOP_NIU_A		132
+#define SRST_VOP_A		133
+#define SRST_VOP_H		134
+#define SRST_VOP_D		135
+#define SRST_RGA		136
+#define SRST_RGA_NIU_A		137
+#define SRST_RGA_A		138
+#define SRST_RGA_H		139
+#define SRST_IEP_A		140
+#define SRST_IEP_H		141
+#define SRST_HDMI		142
+#define SRST_HDMI_P		143
+
+#define SRST_HDCP_A		144
+#define SRST_HDCP		145
+#define SRST_HDCP_H		146
+#define SRST_CIF_A		147
+#define SRST_CIF_H		148
+#define SRST_CIF_P		149
+#define SRST_OTP_P		150
+#define SRST_OTP_SBPI		151
+#define SRST_OTP_USER		152
+#define SRST_DDRCTRL_A		153
+#define SRST_DDRSTDY_P		154
+#define SRST_DDRSTDY		155
+#define SRST_PDM_H		156
+#define SRST_PDM		157
+#define SRST_USB3PHY_OTG_P	158
+#define SRST_USB3PHY_PIPE_P	159
+
+#define SRST_VCODEC_A		160
+#define SRST_VCODEC_NIU_A	161
+#define SRST_VCODEC_H		162
+#define SRST_VCODEC_NIU_H	163
+#define SRST_VDEC_A		164
+#define SRST_VDEC_NIU_A		165
+#define SRST_VDEC_H		166
+#define SRST_VDEC_NIU_H		167
+#define SRST_VDEC_CORE		168
+#define SRST_VDEC_CABAC		169
+#define SRST_DDRPHYDIV		175
+
+#define SRST_RKVENC_NIU_A	176
+#define SRST_RKVENC_NIU_H	177
+#define SRST_RKVENC_H265_A	178
+#define SRST_RKVENC_H265_P	179
+#define SRST_RKVENC_H265_CORE	180
+#define SRST_RKVENC_H265_DSP	181
+#define SRST_RKVENC_H264_A	182
+#define SRST_RKVENC_H264_H	183
+#define SRST_RKVENC_INTMEM	184
+
+#endif
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 2/4] dt-bindings: add bindings for rk3328 clock controller
From: Elaine Zhang @ 2016-12-27  6:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482820373-10186-1-git-send-email-zhangqing@rock-chips.com>

Add devicetree bindings for Rockchip cru which found on
Rockchip SoCs.

Changes in v4:
  dropping the "rockchip,cru" and "syscon" properties for bindings of rk3328

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
 .../bindings/clock/rockchip,rk3328-cru.txt         | 57 ++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt

diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt
new file mode 100644
index 000000000000..e71c675ba5da
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/rockchip,rk3328-cru.txt
@@ -0,0 +1,57 @@
+* Rockchip RK3328 Clock and Reset Unit
+
+The RK3328 clock controller generates and supplies clock to various
+controllers within the SoC and also implements a reset controller for SoC
+peripherals.
+
+Required Properties:
+
+- compatible: should be "rockchip,rk3328-cru"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- #clock-cells: should be 1.
+- #reset-cells: should be 1.
+
+Optional Properties:
+
+- rockchip,grf: phandle to the syscon managing the "general register files"
+  If missing pll rates are not changeable, due to the missing pll lock status.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. All available clocks are defined as
+preprocessor macros in the dt-bindings/clock/rk3328-cru.h headers and can be
+used in device tree sources. Similar macros exist for the reset sources in
+these files.
+
+External clocks:
+
+There are several clocks that are generated outside the SoC. It is expected
+that they are defined using standard clock bindings with following
+clock-output-names:
+ - "xin24m" - crystal input - required,
+ - "clkin_i2s" - external I2S clock - optional,
+ - "gmac_clkin" - external GMAC clock - optional
+ - "phy_50m_out" - output clock of the pll in the mac phy
+
+Example: Clock controller node:
+
+	cru: clock-controller at ff440000 {
+		compatible = "rockchip,rk3328-cru";
+		reg = <0x0 0xff440000 0x0 0x1000>;
+		rockchip,grf = <&grf>;
+
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+	};
+
+Example: UART controller node that consumes the clock generated by the clock
+  controller:
+
+	uart0: serial at ff120000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0xff120000 0x100>;
+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		clocks = <&cru SCLK_UART0>;
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 3/4] clk: rockchip: add new pll-type for rk3328
From: Elaine Zhang @ 2016-12-27  6:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482820373-10186-1-git-send-email-zhangqing@rock-chips.com>

The rk3328's pll and clock are similar with rk3036's,
it different with pll_mode_mask, the rk3328 soc
pll mode only one bit(rk3036 soc have two bits)
so these should be independent and separate from
the series of rk3328s.

Changes in v4:
  adjust the pacth 3 and 4 order.
  move pll_rk3328 to patch 3.
Changes in v3:
  fix up the pll type pll_rk3328 description and use

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
 drivers/clk/rockchip/clk-pll.c | 16 +++++++++++++---
 drivers/clk/rockchip/clk.h     |  1 +
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index 6ed605776abd..eec51893a7e6 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -29,6 +29,7 @@
 #define PLL_MODE_SLOW		0x0
 #define PLL_MODE_NORM		0x1
 #define PLL_MODE_DEEP		0x2
+#define PLL_RK3328_MODE_MASK	0x1
 
 struct rockchip_clk_pll {
 	struct clk_hw		hw;
@@ -848,7 +849,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	struct clk *pll_clk, *mux_clk;
 	char pll_name[20];
 
-	if (num_parents != 2) {
+	if ((pll_type != pll_rk3328 && num_parents != 2) ||
+	    (pll_type == pll_rk3328 && num_parents != 1)) {
 		pr_err("%s: needs two parent clocks\n", __func__);
 		return ERR_PTR(-EINVAL);
 	}
@@ -865,13 +867,17 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	pll_mux = &pll->pll_mux;
 	pll_mux->reg = ctx->reg_base + mode_offset;
 	pll_mux->shift = mode_shift;
-	pll_mux->mask = PLL_MODE_MASK;
+	if (pll_type == pll_rk3328)
+		pll_mux->mask = PLL_RK3328_MODE_MASK;
+	else
+		pll_mux->mask = PLL_MODE_MASK;
 	pll_mux->flags = 0;
 	pll_mux->lock = &ctx->lock;
 	pll_mux->hw.init = &init;
 
 	if (pll_type == pll_rk3036 ||
 	    pll_type == pll_rk3066 ||
+	    pll_type == pll_rk3328 ||
 	    pll_type == pll_rk3399)
 		pll_mux->flags |= CLK_MUX_HIWORD_MASK;
 
@@ -884,7 +890,10 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	init.flags = CLK_SET_RATE_PARENT;
 	init.ops = pll->pll_mux_ops;
 	init.parent_names = pll_parents;
-	init.num_parents = ARRAY_SIZE(pll_parents);
+	if (pll_type == pll_rk3328)
+		init.num_parents = 2;
+	else
+		init.num_parents = ARRAY_SIZE(pll_parents);
 
 	mux_clk = clk_register(NULL, &pll_mux->hw);
 	if (IS_ERR(mux_clk))
@@ -918,6 +927,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 
 	switch (pll_type) {
 	case pll_rk3036:
+	case pll_rk3328:
 		if (!pll->rate_table || IS_ERR(ctx->grf))
 			init.ops = &rockchip_rk3036_pll_clk_norate_ops;
 		else
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index d67eecc4ade9..06acb7e0911f 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -130,6 +130,7 @@
 enum rockchip_pll_type {
 	pll_rk3036,
 	pll_rk3066,
+	pll_rk3328,
 	pll_rk3399,
 };
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 4/4] clk: rockchip: add clock controller for rk3328
From: Elaine Zhang @ 2016-12-27  6:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482820373-10186-1-git-send-email-zhangqing@rock-chips.com>

Add the clock tree definition for the new rk3328 SoC.

Changes in v4:
  adjust the pacth 3 and 4 order.
Changes in v3:
  fix up the pll parent only xin24m.
Changes in v2:
  fix up these *_sample error description.

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
---
 drivers/clk/rockchip/Makefile     |    1 +
 drivers/clk/rockchip/clk-rk3328.c | 1068 +++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.h        |   22 +
 3 files changed, 1091 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-rk3328.c

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 16e098c36f90..68b04bfca282 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -16,5 +16,6 @@ obj-y	+= clk-rk3036.o
 obj-y	+= clk-rk3188.o
 obj-y	+= clk-rk3228.o
 obj-y	+= clk-rk3288.o
+obj-y	+= clk-rk3328.o
 obj-y	+= clk-rk3368.o
 obj-y	+= clk-rk3399.o
diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c
new file mode 100644
index 000000000000..9958ce7d0dcd
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rk3328.c
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Elaine <zhangqing@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+#include <dt-bindings/clock/rk3328-cru.h>
+#include "clk.h"
+
+#define RK3328_GRF_SOC_STATUS0		0x480
+#define RK3328_GRF_MAC_CON1		0x904
+#define RK3328_GRF_MAC_CON2		0x908
+
+enum rk3328_plls {
+	apll, dpll, cpll, gpll, npll,
+};
+
+static struct rockchip_pll_rate_table rk3328_pll_rates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0),
+	RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0),
+	RK3036_PLL_RATE(984000000, 1, 82, 2, 1, 1, 0),
+	RK3036_PLL_RATE(960000000, 1, 80, 2, 1, 1, 0),
+	RK3036_PLL_RATE(936000000, 1, 78, 2, 1, 1, 0),
+	RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0),
+	RK3036_PLL_RATE(900000000, 4, 300, 2, 1, 1, 0),
+	RK3036_PLL_RATE(888000000, 1, 74, 2, 1, 1, 0),
+	RK3036_PLL_RATE(864000000, 1, 72, 2, 1, 1, 0),
+	RK3036_PLL_RATE(840000000, 1, 70, 2, 1, 1, 0),
+	RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
+	RK3036_PLL_RATE(800000000, 6, 400, 2, 1, 1, 0),
+	RK3036_PLL_RATE(700000000, 6, 350, 2, 1, 1, 0),
+	RK3036_PLL_RATE(696000000, 1, 58, 2, 1, 1, 0),
+	RK3036_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0),
+	RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0),
+	RK3036_PLL_RATE(504000000, 1, 63, 3, 1, 1, 0),
+	RK3036_PLL_RATE(500000000, 6, 250, 2, 1, 1, 0),
+	RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0),
+	RK3036_PLL_RATE(312000000, 1, 52, 2, 2, 1, 0),
+	RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0),
+	RK3036_PLL_RATE(96000000, 1, 64, 4, 4, 1, 0),
+	{ /* sentinel */ },
+};
+
+static struct rockchip_pll_rate_table rk3328_pll_frac_rates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134217),
+	/* vco = 1016064000 */
+	RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671088),
+	/* vco = 983040000 */
+	RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671088),
+	/* vco = 983040000 */
+	RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671088),
+	/* vco = 860156000 */
+	RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797894),
+	/* vco = 903168000 */
+	RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066329),
+	/* vco = 819200000 */
+	{ /* sentinel */ },
+};
+
+#define RK3328_DIV_CPU_MASK		0x1f
+#define RK3328_DIV_CPU_SHIFT		8
+
+#define RK3328_DIV_PERI_MASK		0xf
+#define RK3328_DIV_PERI_SHIFT		0
+#define RK3328_DIV_ACLK_MASK		0x7
+#define RK3328_DIV_ACLK_SHIFT		4
+#define RK3328_DIV_HCLK_MASK		0x3
+#define RK3328_DIV_HCLK_SHIFT		8
+#define RK3328_DIV_PCLK_MASK		0x7
+#define RK3328_DIV_PCLK_SHIFT		12
+
+#define RK3328_CLKSEL1(_aclk_core, _pclk_dbg)				\
+{									\
+	.reg = RK3328_CLKSEL_CON(1),					\
+	.val = HIWORD_UPDATE(_aclk_core, RK3328_DIV_ACLKM_MASK,		\
+			     RK3328_DIV_ACLKM_SHIFT) |			\
+	       HIWORD_UPDATE(_pclk_dbg, RK3328_DIV_PCLK_DBG_MASK,	\
+			     RK3328_DIV_PCLK_DBG_SHIFT),		\
+}
+
+#define RK3328_CPUCLK_RATE(_prate, _aclk_core, _pclk_dbg)		\
+{									\
+	.prate = _prate,						\
+	.divs = {							\
+		RK3328_CLKSEL1(_aclk_core, _pclk_dbg),			\
+	},								\
+}
+
+static struct rockchip_cpuclk_rate_table rk3328_cpuclk_rates[] __initdata = {
+	RK3328_CPUCLK_RATE(1800000000, 1, 7),
+	RK3328_CPUCLK_RATE(1704000000, 1, 7),
+	RK3328_CPUCLK_RATE(1608000000, 1, 7),
+	RK3328_CPUCLK_RATE(1512000000, 1, 7),
+	RK3328_CPUCLK_RATE(1488000000, 1, 5),
+	RK3328_CPUCLK_RATE(1416000000, 1, 5),
+	RK3328_CPUCLK_RATE(1392000000, 1, 5),
+	RK3328_CPUCLK_RATE(1296000000, 1, 5),
+	RK3328_CPUCLK_RATE(1200000000, 1, 5),
+	RK3328_CPUCLK_RATE(1104000000, 1, 5),
+	RK3328_CPUCLK_RATE(1008000000, 1, 5),
+	RK3328_CPUCLK_RATE(912000000, 1, 5),
+	RK3328_CPUCLK_RATE(816000000, 1, 3),
+	RK3328_CPUCLK_RATE(696000000, 1, 3),
+	RK3328_CPUCLK_RATE(600000000, 1, 3),
+	RK3328_CPUCLK_RATE(408000000, 1, 1),
+	RK3328_CPUCLK_RATE(312000000, 1, 1),
+	RK3328_CPUCLK_RATE(216000000,  1, 1),
+	RK3328_CPUCLK_RATE(96000000, 1, 1),
+};
+
+static const struct rockchip_cpuclk_reg_data rk3328_cpuclk_data = {
+	.core_reg = RK3328_CLKSEL_CON(0),
+	.div_core_shift = 0,
+	.div_core_mask = 0x1f,
+	.mux_core_alt = 1,
+	.mux_core_main = 3,
+	.mux_core_shift = 6,
+	.mux_core_mask = 0x3,
+};
+
+PNAME(mux_pll_p)		= { "xin24m" };
+
+PNAME(mux_2plls_p)		= { "cpll", "gpll" };
+PNAME(mux_gpll_cpll_p)		= { "gpll", "cpll" };
+PNAME(mux_cpll_gpll_apll_p)	= { "cpll", "gpll", "apll" };
+PNAME(mux_2plls_xin24m_p)	= { "cpll", "gpll", "xin24m" };
+PNAME(mux_2plls_hdmiphy_p)	= { "cpll", "gpll",
+				    "dummy_hdmiphy" };
+PNAME(mux_4plls_p)		= { "cpll", "gpll",
+				    "dummy_hdmiphy",
+				    "usb480m" };
+PNAME(mux_2plls_u480m_p)	= { "cpll", "gpll",
+				    "usb480m" };
+PNAME(mux_2plls_24m_u480m_p)	= { "cpll", "gpll",
+				     "xin24m", "usb480m" };
+
+PNAME(mux_ddrphy_p)		= { "dpll", "apll", "cpll" };
+PNAME(mux_armclk_p)		= { "apll_core",
+				    "gpll_core",
+				    "dpll_core",
+				    "npll_core"};
+PNAME(mux_hdmiphy_p)		= { "hdmi_phy", "xin24m" };
+PNAME(mux_usb480m_p)		= { "usb480m_phy",
+				    "xin24m" };
+
+PNAME(mux_i2s0_p)		= { "clk_i2s0_div",
+				    "clk_i2s0_frac",
+				    "xin12m",
+				    "xin12m" };
+PNAME(mux_i2s1_p)		= { "clk_i2s1_div",
+				    "clk_i2s1_frac",
+				    "clkin_i2s1",
+				    "xin12m" };
+PNAME(mux_i2s2_p)		= { "clk_i2s2_div",
+				    "clk_i2s2_frac",
+				    "clkin_i2s2",
+				    "xin12m" };
+PNAME(mux_i2s1out_p)		= { "clk_i2s1", "xin12m"};
+PNAME(mux_i2s2out_p)		= { "clk_i2s2", "xin12m" };
+PNAME(mux_spdif_p)		= { "clk_spdif_div",
+				    "clk_spdif_frac",
+				    "xin12m",
+				    "xin12m" };
+PNAME(mux_uart0_p)		= { "clk_uart0_div",
+				    "clk_uart0_frac",
+				    "xin24m" };
+PNAME(mux_uart1_p)		= { "clk_uart1_div",
+				    "clk_uart1_frac",
+				    "xin24m" };
+PNAME(mux_uart2_p)		= { "clk_uart2_div",
+				    "clk_uart2_frac",
+				    "xin24m" };
+
+PNAME(mux_sclk_cif_p)		= { "clk_cif_src",
+				    "xin24m" };
+PNAME(mux_dclk_lcdc_p)		= { "hdmiphy",
+				    "dclk_lcdc_src" };
+PNAME(mux_aclk_peri_pre_p)	= { "cpll_peri",
+				    "gpll_peri",
+				    "hdmiphy_peri" };
+PNAME(mux_ref_usb3otg_src_p)	= { "xin24m",
+				    "clk_usb3otg_ref" };
+PNAME(mux_xin24m_32k_p)		= { "xin24m",
+				    "clk_rtc32k" };
+PNAME(mux_mac2io_src_p)		= { "clk_mac2io_src",
+				    "gmac_clkin" };
+PNAME(mux_mac2phy_src_p)	= { "clk_mac2phy_src",
+				    "phy_50m_out" };
+
+static struct rockchip_pll_clock rk3328_pll_clks[] __initdata = {
+	[apll] = PLL(pll_rk3328, PLL_APLL, "apll", mux_pll_p,
+		     0, RK3328_PLL_CON(0),
+		     RK3328_MODE_CON, 0, 4, 0, rk3328_pll_frac_rates),
+	[dpll] = PLL(pll_rk3328, PLL_DPLL, "dpll", mux_pll_p,
+		     0, RK3328_PLL_CON(8),
+		     RK3328_MODE_CON, 4, 3, 0, NULL),
+	[cpll] = PLL(pll_rk3328, PLL_CPLL, "cpll", mux_pll_p,
+		     0, RK3328_PLL_CON(16),
+		     RK3328_MODE_CON, 8, 2, 0, rk3328_pll_rates),
+	[gpll] = PLL(pll_rk3328, PLL_GPLL, "gpll", mux_pll_p,
+		     0, RK3328_PLL_CON(24),
+		     RK3328_MODE_CON, 12, 1, 0, rk3328_pll_frac_rates),
+	[npll] = PLL(pll_rk3328, PLL_NPLL, "npll", mux_pll_p,
+		     0, RK3328_PLL_CON(40),
+		     RK3328_MODE_CON, 1, 0, 0, rk3328_pll_rates),
+};
+
+#define MFLAGS CLK_MUX_HIWORD_MASK
+#define DFLAGS CLK_DIVIDER_HIWORD_MASK
+#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
+
+static struct rockchip_clk_branch rk3328_i2s0_fracmux __initdata =
+	MUX(0, "i2s0_pre", mux_i2s0_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(6), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_i2s1_fracmux __initdata =
+	MUX(0, "i2s1_pre", mux_i2s1_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(8), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_i2s2_fracmux __initdata =
+	MUX(0, "i2s2_pre", mux_i2s2_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(10), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_spdif_fracmux __initdata =
+	MUX(SCLK_SPDIF, "sclk_spdif", mux_spdif_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(12), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_uart0_fracmux __initdata =
+	MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(14), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_uart1_fracmux __initdata =
+	MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(16), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_uart2_fracmux __initdata =
+	MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(18), 8, 2, MFLAGS);
+
+static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
+	/*
+	 * Clock-Architecture Diagram 1
+	 */
+
+	DIV(0, "clk_24m", "xin24m", CLK_IGNORE_UNUSED,
+	    RK3328_CLKSEL_CON(2), 8, 5, DFLAGS),
+	COMPOSITE(SCLK_RTC32K, "clk_rtc32k", mux_2plls_xin24m_p, 0,
+		  RK3328_CLKSEL_CON(38), 14, 2, MFLAGS, 0, 14, DFLAGS,
+		  RK3328_CLKGATE_CON(0), 11, GFLAGS),
+	/* PD_MISC */
+	MUX(HDMIPHY, "hdmiphy", mux_hdmiphy_p, CLK_SET_RATE_PARENT,
+	    RK3328_MISC_CON, 13, 1, MFLAGS),
+	MUX(USB480M, "usb480m", mux_usb480m_p, CLK_SET_RATE_PARENT,
+	    RK3328_MISC_CON, 15, 1, MFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 2
+	 */
+
+	/* PD_CORE */
+	GATE(0, "apll_core", "apll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(0), 0, GFLAGS),
+	GATE(0, "gpll_core", "gpll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(0), 2, GFLAGS),
+	GATE(0, "dpll_core", "dpll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(0), 1, GFLAGS),
+	GATE(0, "npll_core", "npll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(0), 12, GFLAGS),
+	COMPOSITE_NOMUX(0, "pclk_dbg", "armclk", CLK_IGNORE_UNUSED,
+			RK3328_CLKSEL_CON(1), 0, 4,
+			DFLAGS | CLK_DIVIDER_READ_ONLY,
+			RK3328_CLKGATE_CON(7), 0, GFLAGS),
+	COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED,
+			RK3328_CLKSEL_CON(1), 4, 3,
+			DFLAGS | CLK_DIVIDER_READ_ONLY,
+			RK3328_CLKGATE_CON(7), 1, GFLAGS),
+	GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(13), 0, GFLAGS),
+	GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(13), 1, GFLAGS),
+
+	GATE(0, "clk_jtag", "jtag_clkin", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(7), 2, GFLAGS),
+
+	/* PD_GPU */
+	COMPOSITE(0, "aclk_gpu_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(44), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 6, GFLAGS),
+	GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(14), 0, GFLAGS),
+	GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(14), 1, GFLAGS),
+
+	/* PD_DDR */
+	COMPOSITE(0, "clk_ddr", mux_ddrphy_p, CLK_IGNORE_UNUSED,
+		  RK3328_CLKSEL_CON(3), 8, 2, MFLAGS, 0, 3,
+		  DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+		  RK3328_CLKGATE_CON(0), 4, GFLAGS),
+	GATE(0, "clk_ddrmsch", "clk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 6, GFLAGS),
+	GATE(0, "clk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 5, GFLAGS),
+	GATE(0, "aclk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 4, GFLAGS),
+	GATE(0, "clk_ddrmon", "xin24m", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(0), 6, GFLAGS),
+
+	COMPOSITE(PCLK_DDR, "pclk_ddr", mux_2plls_hdmiphy_p, 0,
+		  RK3328_CLKSEL_CON(4), 13, 2, MFLAGS, 8, 3, DFLAGS,
+		  RK3328_CLKGATE_CON(7), 4, GFLAGS),
+	GATE(0, "pclk_ddrupctl", "pclk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 1, GFLAGS),
+	GATE(0, "pclk_ddr_msch", "pclk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 2, GFLAGS),
+	GATE(0, "pclk_ddr_mon", "pclk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 3, GFLAGS),
+	GATE(0, "pclk_ddrstdby", "pclk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 7, GFLAGS),
+	GATE(0, "pclk_ddr_grf", "pclk_ddr", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(18), 9, GFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 3
+	 */
+	/* PD_BUS */
+	COMPOSITE(ACLK_BUS_PRE, "aclk_bus_pre", mux_2plls_hdmiphy_p, 0,
+		  RK3328_CLKSEL_CON(0), 13, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(8), 0, GFLAGS),
+	COMPOSITE_NOMUX(HCLK_BUS_PRE, "hclk_bus_pre", "aclk_bus_pre", 0,
+			RK3328_CLKSEL_CON(1), 8, 2, DFLAGS,
+			RK3328_CLKGATE_CON(8), 1, GFLAGS),
+	COMPOSITE_NOMUX(PCLK_BUS_PRE, "pclk_bus_pre", "aclk_bus_pre", 0,
+			RK3328_CLKSEL_CON(1), 12, 3, DFLAGS,
+			RK3328_CLKGATE_CON(8), 2, GFLAGS),
+	GATE(0, "pclk_bus", "pclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(8), 3, GFLAGS),
+	GATE(0, "pclk_phy_pre", "pclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(8), 4, GFLAGS),
+
+	COMPOSITE(SCLK_TSP, "clk_tsp", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(21), 15, 1, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 5, GFLAGS),
+	GATE(0, "clk_hsadc_tsp", "ext_gpio3a2", 0,
+	     RK3328_CLKGATE_CON(17), 13, GFLAGS),
+
+	/* PD_I2S */
+	COMPOSITE(0, "clk_i2s0_div", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(6), 15, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(1), 1, GFLAGS),
+	COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(7), 0,
+			  RK3328_CLKGATE_CON(1), 2, GFLAGS,
+			  &rk3328_i2s0_fracmux),
+	GATE(SCLK_I2S0, "clk_i2s0", "i2s0_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(1), 3, GFLAGS),
+
+	COMPOSITE(0, "clk_i2s1_div", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(8), 15, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(1), 4, GFLAGS),
+	COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(9), 0,
+			  RK3328_CLKGATE_CON(1), 5, GFLAGS,
+			  &rk3328_i2s1_fracmux),
+	GATE(SCLK_I2S1, "clk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(0), 6, GFLAGS),
+	COMPOSITE_NODIV(SCLK_I2S1_OUT, "i2s1_out", mux_i2s1out_p, 0,
+			RK3328_CLKSEL_CON(8), 12, 1, MFLAGS,
+			RK3328_CLKGATE_CON(1), 7, GFLAGS),
+
+	COMPOSITE(0, "clk_i2s2_div", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(1), 8, GFLAGS),
+	COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(11), 0,
+			  RK3328_CLKGATE_CON(1), 9, GFLAGS,
+			  &rk3328_i2s2_fracmux),
+	GATE(SCLK_I2S2, "clk_i2s2", "i2s2_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(1), 10, GFLAGS),
+	COMPOSITE_NODIV(SCLK_I2S2_OUT, "i2s2_out", mux_i2s2out_p, 0,
+			RK3328_CLKSEL_CON(10), 12, 1, MFLAGS,
+			RK3328_CLKGATE_CON(1), 11, GFLAGS),
+
+	COMPOSITE(0, "clk_spdif_div", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(12), 15, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(1), 12, GFLAGS),
+	COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(13), 0,
+			  RK3328_CLKGATE_CON(1), 13, GFLAGS,
+			  &rk3328_spdif_fracmux),
+
+	/* PD_UART */
+	COMPOSITE(0, "clk_uart0_div", mux_2plls_u480m_p, 0,
+		  RK3328_CLKSEL_CON(14), 12, 2, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(1), 14, GFLAGS),
+	COMPOSITE(0, "clk_uart1_div", mux_2plls_u480m_p, 0,
+		  RK3328_CLKSEL_CON(16), 12, 2, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 0, GFLAGS),
+	COMPOSITE(0, "clk_uart2_div", mux_2plls_u480m_p,
+		  0, RK3328_CLKSEL_CON(18), 12, 2, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 2, GFLAGS),
+	COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(15), 0,
+			  RK3328_CLKGATE_CON(1), 15, GFLAGS,
+			  &rk3328_uart0_fracmux),
+	COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(17), 0,
+			  RK3328_CLKGATE_CON(2), 1, GFLAGS,
+			  &rk3328_uart1_fracmux),
+	COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div",
+			  CLK_SET_RATE_PARENT,
+			  RK3328_CLKSEL_CON(19), 0,
+			  RK3328_CLKGATE_CON(2), 3, GFLAGS,
+			  &rk3328_uart2_fracmux),
+
+	/*
+	 * Clock-Architecture Diagram 4
+	 */
+	 COMPOSITE(SCLK_I2C0, "clk_i2c0", mux_2plls_p, 0,
+		   RK3328_CLKSEL_CON(34), 7, 1, MFLAGS, 0, 7, DFLAGS,
+		   RK3328_CLKGATE_CON(2), 9, GFLAGS),
+	COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(34), 15, 1, MFLAGS, 8, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 10, GFLAGS),
+	COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(35), 7, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 11, GFLAGS),
+	COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(35), 15, 1, MFLAGS, 8, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 12, GFLAGS),
+	COMPOSITE(SCLK_CRYPTO, "clk_crypto", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(20), 7, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 4, GFLAGS),
+	COMPOSITE_NOMUX(SCLK_TSADC, "clk_tsadc", "clk_24m", 0,
+			RK3328_CLKSEL_CON(22), 0, 10, DFLAGS,
+			RK3328_CLKGATE_CON(2), 6, GFLAGS),
+	COMPOSITE_NOMUX(SCLK_SARADC, "clk_saradc", "clk_24m", 0,
+			RK3328_CLKSEL_CON(23), 0, 10, DFLAGS,
+			RK3328_CLKGATE_CON(2), 14, GFLAGS),
+	COMPOSITE(SCLK_SPI, "clk_spi", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(24), 7, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 7, GFLAGS),
+	COMPOSITE(SCLK_PWM, "clk_pwm", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(24), 15, 1, MFLAGS, 8, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 8, GFLAGS),
+	COMPOSITE(SCLK_OTP, "clk_otp", mux_2plls_xin24m_p, 0,
+		  RK3328_CLKSEL_CON(4), 6, 2, MFLAGS, 0, 6, DFLAGS,
+		  RK3328_CLKGATE_CON(3), 8, GFLAGS),
+	COMPOSITE(SCLK_EFUSE, "clk_efuse", mux_2plls_xin24m_p, 0,
+		  RK3328_CLKSEL_CON(5), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 13, GFLAGS),
+	COMPOSITE(SCLK_PDM, "clk_pdm", mux_cpll_gpll_apll_p,
+		  CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+		  RK3328_CLKSEL_CON(20), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(2), 15, GFLAGS),
+
+	GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 5, GFLAGS),
+	GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 6, GFLAGS),
+	GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 7, GFLAGS),
+	GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 8, GFLAGS),
+	GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 9, GFLAGS),
+	GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 0,
+	     RK3328_CLKGATE_CON(8), 10, GFLAGS),
+
+	COMPOSITE(SCLK_WIFI, "clk_wifi", mux_2plls_u480m_p, 0,
+		  RK3328_CLKSEL_CON(52), 6, 2, MFLAGS, 0, 6, DFLAGS,
+		  RK3328_CLKGATE_CON(0), 10, GFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 5
+	 */
+	/* PD_VIDEO */
+	COMPOSITE(ACLK_RKVDEC_PRE, "aclk_rkvdec_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(48), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 0, GFLAGS),
+	FACTOR_GATE(HCLK_RKVDEC_PRE, "hclk_rkvdec_pre", "aclk_rkvdec_pre",
+		    0, 1, 4,
+		    RK3328_CLKGATE_CON(11), 0, GFLAGS),
+	GATE(ACLK_RKVDEC, "aclk_rkvdec", "aclk_rkvdec_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(24), 0, GFLAGS),
+	GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(24), 1, GFLAGS),
+	GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(24), 2, GFLAGS),
+	GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(24), 3, GFLAGS),
+
+	COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(48), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 1, GFLAGS),
+
+	COMPOSITE(SCLK_VDEC_CORE, "sclk_vdec_core", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(49), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 2, GFLAGS),
+
+	COMPOSITE(ACLK_VPU_PRE, "aclk_vpu_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(50), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 5, GFLAGS),
+	FACTOR_GATE(HCLK_VPU_PRE, "hclk_vpu_pre", "aclk_vpu_pre", 0, 1, 4,
+		    RK3328_CLKGATE_CON(11), 8, GFLAGS),
+	GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(23), 0, GFLAGS),
+	GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(23), 1, GFLAGS),
+	GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(23), 2, GFLAGS),
+	GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(23), 3, GFLAGS),
+
+	COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(51), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 3, GFLAGS),
+	FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4,
+		    RK3328_CLKGATE_CON(11), 4, GFLAGS),
+	GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(25), 0, GFLAGS),
+	GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(25), 1, GFLAGS),
+	GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0,
+	     RK3328_CLKGATE_CON(25), 0, GFLAGS),
+	GATE(PCLK_H265, "pclk_h265", "hclk_rkvenc", 0,
+	     RK3328_CLKGATE_CON(25), 1, GFLAGS),
+	GATE(ACLK_H264, "aclk_h264", "aclk_rkvenc", 0,
+	     RK3328_CLKGATE_CON(25), 0, GFLAGS),
+	GATE(HCLK_H264, "hclk_h264", "hclk_rkvenc", 0,
+	     RK3328_CLKGATE_CON(25), 1, GFLAGS),
+	GATE(ACLK_AXISRAM, "aclk_axisram", "aclk_rkvenc", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(25), 0, GFLAGS),
+
+	COMPOSITE(SCLK_VENC_CORE, "sclk_venc_core", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(51), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 4, GFLAGS),
+
+	COMPOSITE(SCLK_VENC_DSP, "sclk_venc_dsp", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(52), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(6), 7, GFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 6
+	 */
+	/* PD_VIO */
+	COMPOSITE(ACLK_VIO_PRE, "aclk_vio_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(37), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(5), 2, GFLAGS),
+	DIV(HCLK_VIO_PRE, "hclk_vio_pre", "aclk_vio_pre", 0,
+	    RK3328_CLKSEL_CON(37), 8, 5, DFLAGS),
+
+	COMPOSITE(ACLK_RGA_PRE, "aclk_rga_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(36), 14, 2, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(5), 0, GFLAGS),
+	COMPOSITE(SCLK_RGA, "clk_rga", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(36), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(5), 1, GFLAGS),
+	COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0,
+		  RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(5), 5, GFLAGS),
+	GATE(0, "clk_hdmi_sfc", "xin24m", 0,
+	     RK3328_CLKGATE_CON(5), 4, GFLAGS),
+
+	COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0,
+			RK3328_CLKSEL_CON(42), 7, 1, MFLAGS,
+			RK3328_CLKGATE_CON(5), 3, GFLAGS),
+	COMPOSITE_NOGATE(SCLK_CIF_OUT, "clk_cif_out", mux_sclk_cif_p,
+			 CLK_SET_RATE_PARENT,
+			 RK3328_CLKSEL_CON(42), 5, 1, MFLAGS, 0, 5, DFLAGS),
+
+	COMPOSITE(DCLK_LCDC_SRC, "dclk_lcdc_src", mux_gpll_cpll_p, 0,
+		  RK3328_CLKSEL_CON(40), 0, 1, MFLAGS, 8, 8, DFLAGS,
+		  RK3328_CLKGATE_CON(5), 6, GFLAGS),
+	DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0,
+	    RK3328_CLKSEL_CON(40), 3, 3, DFLAGS),
+	MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0,
+	    RK3328_CLKSEL_CON(40), 1, 1, MFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 7
+	 */
+	/* PD_PERI */
+	GATE(0, "gpll_peri", "gpll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(4), 0, GFLAGS),
+	GATE(0, "cpll_peri", "cpll", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(4), 1, GFLAGS),
+	GATE(0, "hdmiphy_peri", "hdmiphy", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(4), 2, GFLAGS),
+	COMPOSITE_NOGATE(ACLK_PERI_PRE, "aclk_peri_pre", mux_aclk_peri_pre_p, 0,
+			 RK3328_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS),
+	COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_pre",
+			CLK_IGNORE_UNUSED,
+			RK3328_CLKSEL_CON(29), 0, 2, DFLAGS,
+			RK3328_CLKGATE_CON(10), 2, GFLAGS),
+	COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_pre",
+			CLK_IGNORE_UNUSED,
+			RK3328_CLKSEL_CON(29), 4, 3, DFLAGS,
+			RK3328_CLKGATE_CON(10), 1, GFLAGS),
+	GATE(ACLK_PERI, "aclk_peri", "aclk_peri_pre",
+	     CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT,
+	     RK3328_CLKGATE_CON(10), 0, GFLAGS),
+
+	COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_2plls_24m_u480m_p, 0,
+		  RK3328_CLKSEL_CON(30), 8, 2, MFLAGS, 0, 8, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 3, GFLAGS),
+
+	COMPOSITE(SCLK_SDIO, "clk_sdio", mux_2plls_24m_u480m_p, 0,
+		  RK3328_CLKSEL_CON(31), 8, 2, MFLAGS, 0, 8, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 4, GFLAGS),
+
+	COMPOSITE(SCLK_EMMC, "clk_emmc", mux_2plls_24m_u480m_p, 0,
+		  RK3328_CLKSEL_CON(32), 8, 2, MFLAGS, 0, 8, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 5, GFLAGS),
+
+	COMPOSITE(SCLK_SDMMC_EXT, "clk_sdmmc_ext",
+		  mux_2plls_24m_u480m_p, 0,
+		  RK3328_CLKSEL_CON(43), 8, 2, MFLAGS, 0, 8, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 10, GFLAGS),
+
+	COMPOSITE(SCLK_REF_USB3OTG_SRC, "clk_ref_usb3otg_src", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(45), 7, 1, MFLAGS, 0, 7, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 9, GFLAGS),
+
+	MUX(SCLK_REF_USB3OTG, "clk_ref_usb3otg",
+	    mux_ref_usb3otg_src_p, CLK_SET_RATE_PARENT,
+	    RK3328_CLKSEL_CON(45), 8, 1, MFLAGS),
+
+	GATE(SCLK_USB3OTG_REF, "clk_usb3otg_ref", "xin24m", 0,
+	     RK3328_CLKGATE_CON(4), 7, GFLAGS),
+
+	COMPOSITE(SCLK_USB3OTG_SUSPEND, "clk_usb3otg_suspend",
+		  mux_xin24m_32k_p, 0,
+		  RK3328_CLKSEL_CON(33), 15, 1, MFLAGS, 0, 10, DFLAGS,
+		  RK3328_CLKGATE_CON(4), 8, GFLAGS),
+
+	/*
+	 * Clock-Architecture Diagram 8
+	 */
+	/* PD_GMAC */
+
+	COMPOSITE(ACLK_GMAC, "aclk_gmac", mux_2plls_hdmiphy_p, 0,
+		  RK3328_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(3), 2, GFLAGS),
+	COMPOSITE_NOMUX(PCLK_GMAC, "pclk_gmac", "aclk_gmac", 0,
+			RK3328_CLKSEL_CON(25), 8, 3, DFLAGS,
+			RK3328_CLKGATE_CON(9), 0, GFLAGS),
+
+	COMPOSITE(SCLK_MAC2IO_SRC, "clk_mac2io_src", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(27), 7, 1, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(3), 1, GFLAGS),
+	GATE(SCLK_MAC2IO_REF, "clk_mac2io_ref", "clk_mac2io", 0,
+	     RK3328_CLKGATE_CON(9), 7, GFLAGS),
+	GATE(SCLK_MAC2IO_RX, "clk_mac2io_rx", "clk_mac2io", 0,
+	     RK3328_CLKGATE_CON(9), 4, GFLAGS),
+	GATE(SCLK_MAC2IO_TX, "clk_mac2io_tx", "clk_mac2io", 0,
+	     RK3328_CLKGATE_CON(9), 5, GFLAGS),
+	GATE(SCLK_MAC2IO_REFOUT, "clk_mac2io_refout", "clk_mac2io", 0,
+	     RK3328_CLKGATE_CON(9), 6, GFLAGS),
+	COMPOSITE(SCLK_MAC2IO_OUT, "clk_mac2io_out", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(27), 15, 1, MFLAGS, 8, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(3), 5, GFLAGS),
+
+	COMPOSITE(SCLK_MAC2PHY_SRC, "clk_mac2phy_src", mux_2plls_p, 0,
+		  RK3328_CLKSEL_CON(26), 7, 1, MFLAGS, 0, 5, DFLAGS,
+		  RK3328_CLKGATE_CON(3), 0, GFLAGS),
+	GATE(SCLK_MAC2PHY_REF, "clk_mac2phy_ref", "clk_mac2phy", 0,
+	     RK3328_CLKGATE_CON(9), 3, GFLAGS),
+	GATE(SCLK_MAC2PHY_RXTX, "clk_mac2phy_rxtx", "clk_mac2phy", 0,
+	     RK3328_CLKGATE_CON(9), 1, GFLAGS),
+	COMPOSITE_NOMUX(SCLK_MAC2PHY_OUT, "clk_mac2phy_out", "clk_mac2phy", 0,
+			RK3328_CLKSEL_CON(26), 8, 2, DFLAGS,
+			RK3328_CLKGATE_CON(9), 2, GFLAGS),
+
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
+	/*
+	 * Clock-Architecture Diagram 9
+	 */
+
+	/* PD_VOP */
+	GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0,
+	     RK3328_CLKGATE_CON(21), 10, GFLAGS),
+	GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(22), 3, GFLAGS),
+	GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0,
+	     RK3328_CLKGATE_CON(21), 2, GFLAGS),
+	GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(21), 4, GFLAGS),
+
+	GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 6, GFLAGS),
+	GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 8, GFLAGS),
+	GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 15, GFLAGS),
+	GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(22), 2, GFLAGS),
+
+	GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 3, GFLAGS),
+	GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 5, GFLAGS),
+	GATE(HCLK_IEP, "hclk_iep", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 7, GFLAGS),
+	GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 9, GFLAGS),
+	GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(21), 11, GFLAGS),
+	GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(21), 12, GFLAGS),
+	GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(21), 13, GFLAGS),
+	GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(21), 14, GFLAGS),
+	GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(22), 0, GFLAGS),
+	GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(22), 1, GFLAGS),
+	GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(22), 4, GFLAGS),
+	GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0,
+	     RK3328_CLKGATE_CON(22), 5, GFLAGS),
+
+	/* PD_PERI */
+	GATE(0, "aclk_peri_noc", "aclk_peri", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(19), 11, GFLAGS),
+	GATE(ACLK_USB3OTG, "aclk_usb3otg", "aclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 4, GFLAGS),
+
+	GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 0, GFLAGS),
+	GATE(HCLK_SDIO, "hclk_sdio", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 1, GFLAGS),
+	GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 2, GFLAGS),
+	GATE(HCLK_SDMMC_EXT, "hclk_sdmmc_ext", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 15, GFLAGS),
+	GATE(HCLK_HOST0, "hclk_host0", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 6, GFLAGS),
+	GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(19), 7, GFLAGS),
+	GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 8, GFLAGS),
+	GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0,
+	     RK3328_CLKGATE_CON(19), 9, GFLAGS),
+	GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(19), 12, GFLAGS),
+	GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(19), 13, GFLAGS),
+
+	/* PD_GMAC */
+	GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0,
+	     RK3328_CLKGATE_CON(26), 0, GFLAGS),
+	GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0,
+	     RK3328_CLKGATE_CON(26), 2, GFLAGS),
+	GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(26), 4, GFLAGS),
+	GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0,
+	     RK3328_CLKGATE_CON(26), 1, GFLAGS),
+	GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0,
+	     RK3328_CLKGATE_CON(26), 3, GFLAGS),
+	GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(26), 5, GFLAGS),
+
+	/* PD_BUS */
+	GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 12, GFLAGS),
+	GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 11, GFLAGS),
+	GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(17), 12, GFLAGS),
+	GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 0, GFLAGS),
+	GATE(ACLK_DMAC, "aclk_dmac_bus", "aclk_bus_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 1, GFLAGS),
+
+	GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 2, GFLAGS),
+	GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 3, GFLAGS),
+	GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 4, GFLAGS),
+	GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 5, GFLAGS),
+	GATE(HCLK_SPDIF_8CH, "hclk_spdif_8ch", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 6, GFLAGS),
+	GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(17), 11, GFLAGS),
+	GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 7, GFLAGS),
+	GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(15), 8, GFLAGS),
+	GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 13, GFLAGS),
+	GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0,
+	     RK3328_CLKGATE_CON(28), 0, GFLAGS),
+
+	GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 14, GFLAGS),
+	GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 9, GFLAGS),
+	GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(28), 4, GFLAGS),
+	GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(15), 10, GFLAGS),
+	GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 0, GFLAGS),
+	GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 1, GFLAGS),
+	GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 2, GFLAGS),
+	GATE(PCLK_TIMER, "pclk_timer0", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 3, GFLAGS),
+	GATE(0, "pclk_stimer", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 4, GFLAGS),
+	GATE(PCLK_SPI, "pclk_spi", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 5, GFLAGS),
+	GATE(PCLK_PWM, "pclk_rk_pwm", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 6, GFLAGS),
+	GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 7, GFLAGS),
+	GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 8, GFLAGS),
+	GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 9, GFLAGS),
+	GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 10, GFLAGS),
+	GATE(PCLK_UART0, "pclk_uart0", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 11, GFLAGS),
+	GATE(PCLK_UART1, "pclk_uart1", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 12, GFLAGS),
+	GATE(PCLK_UART2, "pclk_uart2", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 13, GFLAGS),
+	GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 14, GFLAGS),
+	GATE(PCLK_DCF, "pclk_dcf", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(16), 15, GFLAGS),
+	GATE(PCLK_GRF, "pclk_grf", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 0, GFLAGS),
+	GATE(0, "pclk_cru", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 4, GFLAGS),
+	GATE(0, "pclk_sgrf", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 6, GFLAGS),
+	GATE(0, "pclk_sim", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 10, GFLAGS),
+	GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 0,
+	     RK3328_CLKGATE_CON(17), 15, GFLAGS),
+	GATE(0, "pclk_pmu", "pclk_bus", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(28), 3, GFLAGS),
+
+	GATE(PCLK_USB3PHY_OTG, "pclk_usb3phy_otg", "pclk_phy_pre", 0,
+	     RK3328_CLKGATE_CON(28), 1, GFLAGS),
+	GATE(PCLK_USB3PHY_PIPE, "pclk_usb3phy_pipe", "pclk_phy_pre", 0,
+	     RK3328_CLKGATE_CON(28), 2, GFLAGS),
+	GATE(PCLK_USB3_GRF, "pclk_usb3_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 2, GFLAGS),
+	GATE(PCLK_USB2_GRF, "pclk_usb2_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 14, GFLAGS),
+	GATE(0, "pclk_ddrphy", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 13, GFLAGS),
+	GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 5, GFLAGS),
+	GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 7, GFLAGS),
+	GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(17), 8, GFLAGS),
+	GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED,
+	     RK3328_CLKGATE_CON(15), 15, GFLAGS),
+
+	/* PD_MMC */
+	MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc",
+	    RK3328_SDMMC_CON0, 1),
+	MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc",
+	    RK3328_SDMMC_CON1, 1),
+
+	MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio",
+	    RK3328_SDIO_CON0, 1),
+	MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio",
+	    RK3328_SDIO_CON1, 1),
+
+	MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc",
+	    RK3328_EMMC_CON0, 1),
+	MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc",
+	    RK3328_EMMC_CON1, 1),
+
+	MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "sclk_sdmmc_ext",
+	    RK3328_SDMMC_EXT_CON0, 1),
+	MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "sclk_sdmmc_ext",
+	    RK3328_SDMMC_EXT_CON1, 1),
+};
+
+static struct rockchip_clk_branch rk3328_clk_grf_branches[] __initdata = {
+	/*
+	 * GRF CRU Clock-Architecture
+	 */
+	MUX(SCLK_MAC2IO, "clk_mac2io", mux_mac2io_src_p, 0,
+	    RK3328_GRF_MAC_CON1, 10, 1, MFLAGS),
+	MUX(SCLK_MAC2PHY, "clk_mac2phy", mux_mac2phy_src_p, 0,
+	    RK3328_GRF_MAC_CON2, 10, 1, MFLAGS),
+};
+
+static const char *const rk3328_critical_clocks[] __initconst = {
+	"aclk_bus",
+	"pclk_bus",
+	"hclk_bus",
+	"aclk_peri",
+	"hclk_peri",
+	"pclk_peri",
+	"pclk_dbg",
+	"aclk_core_niu",
+	"aclk_gic400",
+	"aclk_intmem",
+	"hclk_rom",
+	"pclk_grf",
+	"pclk_cru",
+	"pclk_sgrf",
+	"pclk_timer0",
+	"clk_timer0",
+	"pclk_ddr_msch",
+	"pclk_ddr_mon",
+	"pclk_ddr_grf",
+	"clk_ddrupctl",
+	"clk_ddrmsch",
+	"hclk_ahb1tom",
+	"clk_jtag",
+	"pclk_ddrphy",
+	"pclk_pmu",
+	"hclk_otg_pmu",
+	"aclk_rga_niu",
+	"pclk_vio_h2p",
+	"hclk_vio_h2p",
+};
+
+static void __iomem *rk3328_cru_base;
+
+void rk3328_dump_cru(void)
+{
+	if (rk3328_cru_base) {
+		pr_warn("CRU:\n");
+		print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET,
+			       32, 4, rk3328_cru_base,
+			       0x400, false);
+	}
+}
+EXPORT_SYMBOL_GPL(rk3328_dump_cru);
+
+static int rk3328_clk_panic(struct notifier_block *this,
+			    unsigned long ev, void *ptr)
+{
+	rk3328_dump_cru();
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block rk3328_clk_panic_block = {
+	.notifier_call = rk3328_clk_panic,
+};
+
+static void __init rk3328_clk_init(struct device_node *np)
+{
+	struct rockchip_clk_provider *ctx;
+	void __iomem *reg_base;
+
+	reg_base = of_iomap(np, 0);
+	if (!reg_base) {
+		pr_err("%s: could not map cru region\n", __func__);
+		return;
+	}
+
+	rk3328_cru_base = reg_base;
+
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		iounmap(reg_base);
+		return;
+	}
+
+	rockchip_clk_register_plls(ctx, rk3328_pll_clks,
+				   ARRAY_SIZE(rk3328_pll_clks),
+				   RK3328_GRF_SOC_STATUS0);
+	rockchip_clk_register_branches(ctx, rk3328_clk_branches,
+				       ARRAY_SIZE(rk3328_clk_branches));
+	rockchip_clk_protect_critical(rk3328_critical_clocks,
+				      ARRAY_SIZE(rk3328_critical_clocks));
+
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
+				     mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
+				     &rk3328_cpuclk_data, rk3328_cpuclk_rates,
+				     ARRAY_SIZE(rk3328_cpuclk_rates));
+
+	rockchip_register_softrst(np, 11, reg_base + RK3328_SOFTRST_CON(0),
+				  ROCKCHIP_SOFTRST_HIWORD_MASK);
+
+	rockchip_register_restart_notifier(ctx, RK3328_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
+
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &rk3328_clk_panic_block);
+}
+
+CLK_OF_DECLARE(rk3328_cru, "rockchip,rk3328-cru", rk3328_clk_init);
+
+static void __init rk3328_grf_clk_init(struct device_node *np)
+{
+	struct rockchip_clk_provider *ctx;
+	void __iomem *reg_base;
+
+	reg_base = of_iomap(np, 0);
+	if (!reg_base) {
+		pr_err("%s: could not map cru pmu region\n", __func__);
+		return;
+	}
+
+	ctx = rockchip_clk_init(np, reg_base, CLKGRF_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip pmu clk init failed\n", __func__);
+		return;
+	}
+
+	rockchip_clk_register_branches(ctx, rk3328_clk_grf_branches,
+				       ARRAY_SIZE(rk3328_clk_grf_branches));
+
+	rockchip_clk_of_add_provider(np, ctx);
+}
+
+CLK_OF_DECLARE(rk3328_cru_grf, "rockchip,rk3328-grf", rk3328_grf_clk_init);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 06acb7e0911f..7225997f8d52 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -91,6 +91,28 @@
 #define RK3288_EMMC_CON0		0x218
 #define RK3288_EMMC_CON1		0x21c
 
+#define RK3328_PLL_CON(x)		RK2928_PLL_CON(x)
+#define RK3328_CLKSEL_CON(x)		((x) * 0x4 + 0x100)
+#define RK3328_CLKGATE_CON(x)		((x) * 0x4 + 0x200)
+#define RK3328_GRFCLKSEL_CON(x)		((x) * 0x4 + 0x100)
+#define RK3328_GLB_SRST_FST		0x9c
+#define RK3328_GLB_SRST_SND		0x98
+#define RK3328_SOFTRST_CON(x)		((x) * 0x4 + 0x300)
+#define RK3328_MODE_CON			0x80
+#define RK3328_MISC_CON			0x84
+#define RK3328_DIV_ACLKM_MASK		0x7
+#define RK3328_DIV_ACLKM_SHIFT		4
+#define RK3328_DIV_PCLK_DBG_MASK	0xf
+#define RK3328_DIV_PCLK_DBG_SHIFT	0
+#define RK3328_SDMMC_CON0		0x380
+#define RK3328_SDMMC_CON1		0x384
+#define RK3328_SDIO_CON0		0x388
+#define RK3328_SDIO_CON1		0x38c
+#define RK3328_EMMC_CON0		0x390
+#define RK3328_EMMC_CON1		0x394
+#define RK3328_SDMMC_EXT_CON0		0x398
+#define RK3328_SDMMC_EXT_CON1		0x39C
+
 #define RK3368_PLL_CON(x)		RK2928_PLL_CON(x)
 #define RK3368_CLKSEL_CON(x)		((x) * 0x4 + 0x100)
 #define RK3368_CLKGATE_CON(x)		((x) * 0x4 + 0x200)
-- 
1.9.1

^ permalink raw reply related

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Alim Akhtar @ 2016-12-27  6:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <6de4bc5b-05fb-793f-d3ab-b1c6ee19cfeb@samsung.com>

Hi Jaehoon,

On 12/27/2016 08:00 AM, Jaehoon Chung wrote:
> On 12/27/2016 11:12 AM, Alim Akhtar wrote:
>>
>>
>> On 12/27/2016 06:39 AM, Jaehoon Chung wrote:
>>> Dear Alim,
>>>
>>> On 12/26/2016 06:46 PM, Alim Akhtar wrote:
>>>> Hello Jaehoon
>>>>
>>>> On 12/26/2016 02:32 PM, Jaehoon Chung wrote:
>>>>> Hi Pankaj,
>>>>>
>>>>> On 12/23/2016 07:56 PM, Pankaj Dubey wrote:
>>>>>> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>>>>
>>>>>> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
>>>>>> This patch does refactoring of Exynos PCIe driver to extend support
>>>>>> for other Exynos SoC.
>>>>>>
>>>>>> Following are the main changes done via this patch:
>>>>>> 1) It adds separate structs for memory, clock resources.
>>>>>> 2) It add exynos_pcie_ops struct which will allow us to support the
>>>>>> differences in resources in different Exynos SoC.
>>>>>
>>>>> It's nice to me for reusing this file.
>>>>> but after considering too many times, i decided not to use this file.
>>>>>
>>>>> I'm not sure what block base is..actually this pci-exynos.c is really black-box.
>>>>> (No one maintains this file, even Samsung didn't care.)
>>>>> Who is using this?
>>>>> If Someone can share the information about exynos5440, i can refactor everything.
>>>>> Otherwise, there are two solution..
>>>>>
>>>>> One is "adding the new pci-exynos.c" likes pci-exynos5433.c
>>>>> Other is "refactor this file" under assuming the each register's usage.
>>>>>
>>>> Its alway good to reuse code as far as possible.
>>>> I am yet to see the details of 5440, but since people are now going to support more Exynos variants, in my opinion, instead of adding pci-exynos5433.c, you can rename current pci-exynos.c to something like pci-exynos5440.c (only in case its too much changes needed to support 5433/exynos7) and lets have a common pci-exynos.c where we can add exynos7/5433 and others SoCs, AFAIK at least exynos7 and 5433 has similar pci controller.
>>>
>>> Yes, It's always good to reuse. but as you know, current pci-exynos.c file is really specific for only exynos5440.
>>> I will try to check about combining.
>>>
>>>>> I want to use the PHY generic Framework for EXYNOS PCIe.
>>>>>
>>>> I don't think you have an option here, you should use generic PHY APIs, otherwise phy drive should go to drivers/misc.
>>>> Other thoughts are welcome.
>>>
>>> Why go to drivers/misc? There is driver/phy/ for PHY generic Framework.
>>> If i will touch this file, then i will put our phy-exynos-pcie file under driver/phy/ .
>>> I already sent the patch for this. Could you check them?
>>>
>> My point was, if you are not going to use generic PHY APIs, then phy driver should go into drivers/misc. Anyway as you said you have already posted patches with generic phy framework, I will take a look.
>
> Ah. Right..And i'm doing the refactoring to reuse the current pci-exynos.c.
There is a nice refactoring patch posted by Pankaj recently @
https://lkml.org/lkml/2016/12/23/73
I would suggest you to rebase your work on this top.

> Maybe..Today or Tomorrow..I will send the patches..At that time, could you also check them?
> Any comments might be helpful to me! :)
>
Will wait for you patches :-)

> Best Regards,
> Jaehoon Chung
>
>>> http://patchwork.ozlabs.org/patch/708738/
>>> http://patchwork.ozlabs.org/patch/708742/
>>>
>> Thanks.
>>
>>> Best Regards,
>>> Jaehoon Chung
>>>
>>>>
>>>>> If you or other guys really want to use the pci-exynos.c for other exynos,
>>>>> I will rework with PHY generic framework. Then i will resend the my patches as V2.
>>>>>
>>>>> One more thing..Does anyone know what the usage of block base is?
>>>>> Can i use that register as "syscon"?
>>>>>
>>>>> Best Regards,
>>>>> Jaehoon Chung
>>>>>
>>>>>>
>>>>>> No functional change intended.
>>>>>>
>>>>>> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>>>>>> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
>>>>>> ---
>>>>>> This patch set is prepared on top of Krzysztof's for-next and
>>>>>> PCIe driver cleanup patch [1] by Jaehoon Chung.
>>>>>>
>>>>>> [1]: https://lkml.org/lkml/2016/12/19/44
>>>>>>
>>>>>>
>>>>>>     drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++----------------
>>>>>>     1 file changed, 217 insertions(+), 129 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
>>>>>> index 33562cf..2dc54f7 100644
>>>>>> --- a/drivers/pci/host/pci-exynos.c
>>>>>> +++ b/drivers/pci/host/pci-exynos.c
>>>>>> @@ -17,6 +17,7 @@
>>>>>>     #include <linux/interrupt.h>
>>>>>>     #include <linux/kernel.h>
>>>>>>     #include <linux/init.h>
>>>>>> +#include <linux/of_device.h>
>>>>>>     #include <linux/of_gpio.h>
>>>>>>     #include <linux/pci.h>
>>>>>>     #include <linux/platform_device.h>
>>>>>> @@ -28,16 +29,6 @@
>>>>>>
>>>>>>     #define to_exynos_pcie(x)    container_of(x, struct exynos_pcie, pp)
>>>>>>
>>>>>> -struct exynos_pcie {
>>>>>> -    struct pcie_port    pp;
>>>>>> -    void __iomem        *elbi_base;    /* DT 0th resource */
>>>>>> -    void __iomem        *phy_base;    /* DT 1st resource */
>>>>>> -    void __iomem        *block_base;    /* DT 2nd resource */
>>>>>> -    int            reset_gpio;
>>>>>> -    struct clk        *clk;
>>>>>> -    struct clk        *bus_clk;
>>>>>> -};
>>>>>> -
>>>>>>     /* PCIe ELBI registers */
>>>>>>     #define PCIE_IRQ_PULSE            0x000
>>>>>>     #define IRQ_INTA_ASSERT            BIT(0)
>>>>>> @@ -102,6 +93,122 @@ struct exynos_pcie {
>>>>>>     #define PCIE_PHY_TRSV3_PD_TSV        BIT(7)
>>>>>>     #define PCIE_PHY_TRSV3_LVCC        0x31c
>>>>>>
>>>>>> +struct exynos_pcie_mem_res {
>>>>>> +    void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
>>>>>> +    void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
>>>>>> +    void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
>>>>>> +};
>>>>>> +
>>>>>> +struct exynos_pcie_clk_res {
>>>>>> +    struct clk *clk;
>>>>>> +    struct clk *bus_clk;
>>>>>> +};
>>>>>> +
>>>>>> +struct exynos_pcie {
>>>>>> +    struct pcie_port        pp;
>>>>>> +    struct exynos_pcie_mem_res    *mem_res;
>>>>>> +    struct exynos_pcie_clk_res    *clk_res;
>>>>>> +    const struct exynos_pcie_ops    *ops;
>>>>>> +    int                reset_gpio;
>>>>>> +};
>>>>>> +
>>>>>> +struct exynos_pcie_ops {
>>>>>> +    int (*get_mem_resources)(struct platform_device *pdev,
>>>>>> +            struct exynos_pcie *ep);
>>>>>> +    int (*get_clk_resources)(struct exynos_pcie *ep);
>>>>>> +    int (*init_clk_resources)(struct exynos_pcie *ep);
>>>>>> +    void (*deinit_clk_resources)(struct exynos_pcie *ep);
>>>>>> +};
>>>>>> +
>>>>>> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
>>>>>> +                        struct exynos_pcie *ep)
>>>>>> +{
>>>>>> +    struct resource *res;
>>>>>> +    struct device *dev = ep->pp.dev;
>>>>>> +
>>>>>> +    ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
>>>>>> +    if (!ep->mem_res)
>>>>>> +        return -ENOMEM;
>>>>>> +
>>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>> +    ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
>>>>>> +    if (IS_ERR(ep->mem_res->elbi_base))
>>>>>> +        return PTR_ERR(ep->mem_res->elbi_base);
>>>>>> +
>>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>>>> +    ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
>>>>>> +    if (IS_ERR(ep->mem_res->phy_base))
>>>>>> +        return PTR_ERR(ep->mem_res->phy_base);
>>>>>> +
>>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>>>> +    ep->mem_res->block_base = devm_ioremap_resource(dev, res);
>>>>>> +    if (IS_ERR(ep->mem_res->block_base))
>>>>>> +        return PTR_ERR(ep->mem_res->block_base);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
>>>>>> +{
>>>>>> +    struct device *dev = ep->pp.dev;
>>>>>> +
>>>>>> +    ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
>>>>>> +    if (!ep->clk_res)
>>>>>> +        return -ENOMEM;
>>>>>> +
>>>>>> +    ep->clk_res->clk = devm_clk_get(dev, "pcie");
>>>>>> +    if (IS_ERR(ep->clk_res->clk)) {
>>>>>> +        dev_err(dev, "Failed to get pcie rc clock\n");
>>>>>> +        return PTR_ERR(ep->clk_res->clk);
>>>>>> +    }
>>>>>> +
>>>>>> +    ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>>>> +    if (IS_ERR(ep->clk_res->bus_clk)) {
>>>>>> +        dev_err(dev, "Failed to get pcie bus clock\n");
>>>>>> +        return PTR_ERR(ep->clk_res->bus_clk);
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
>>>>>> +{
>>>>>> +    struct device *dev = ep->pp.dev;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    ret = clk_prepare_enable(ep->clk_res->clk);
>>>>>> +    if (ret) {
>>>>>> +        dev_err(dev, "cannot enable pcie rc clock");
>>>>>> +        return ret;
>>>>>> +    }
>>>>>> +
>>>>>> +    ret = clk_prepare_enable(ep->clk_res->bus_clk);
>>>>>> +    if (ret) {
>>>>>> +        dev_err(dev, "cannot enable pcie bus clock");
>>>>>> +        goto err_bus_clk;
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +
>>>>>> +err_bus_clk:
>>>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>>>> +
>>>>>> +    return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
>>>>>> +{
>>>>>> +    clk_disable_unprepare(ep->clk_res->bus_clk);
>>>>>> +    clk_disable_unprepare(ep->clk_res->clk);
>>>>>> +}
>>>>>> +
>>>>>> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
>>>>>> +    .get_mem_resources    = exynos5440_pcie_get_mem_resources,
>>>>>> +    .get_clk_resources    = exynos5440_pcie_get_clk_resources,
>>>>>> +    .init_clk_resources    = exynos5440_pcie_init_clk_resources,
>>>>>> +    .deinit_clk_resources    = exynos5440_pcie_deinit_clk_resources,
>>>>>> +};
>>>>>> +
>>>>>>     static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
>>>>>>     {
>>>>>>         writel(val, base + reg);
>>>>>> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
>>>>>>         if (on)
>>>>>>             val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>>         else
>>>>>>             val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
>>>>>>         if (on)
>>>>>>             val |= PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>>         else
>>>>>>             val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>>>         val &= ~PCIE_CORE_RESET_ENABLE;
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
>>>>>>         val |= PCIE_CORE_RESET_ENABLE;
>>>>>>
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
>>>>>>     {
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
>>>>>> -    exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
>>>>>>     {
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>>>> -    exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
>>>>>> +    exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>>         val &= ~PCIE_PHY_COMMON_PD_CMN;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>>         val &= ~PCIE_PHY_TRSV0_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>>         val &= ~PCIE_PHY_TRSV1_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>>         val &= ~PCIE_PHY_TRSV2_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>>         val &= ~PCIE_PHY_TRSV3_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
>>>>>>         val |= PCIE_PHY_COMMON_PD_CMN;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
>>>>>>         val |= PCIE_PHY_TRSV0_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
>>>>>>         val |= PCIE_PHY_TRSV1_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
>>>>>>         val |= PCIE_PHY_TRSV2_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
>>>>>>         val |= PCIE_PHY_TRSV3_PD_TSV;
>>>>>> -    exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_init_phy(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         /* DCC feedback control off */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>>>>>>
>>>>>>         /* set TX/RX impedance */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>>>>>>
>>>>>>         /* set 50Mhz PHY clock */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>>>>>>
>>>>>>         /* set TX Differential output for lane 0 */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>>>>>>
>>>>>>         /* set TX Pre-emphasis Level Control for lane 0 to minimum */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>>>>>>
>>>>>>         /* set RX clock and data recovery bandwidth */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>>>>>>
>>>>>>         /* change TX Pre-emphasis Level Control for lanes */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>>>>>>
>>>>>>         /* set LVCC */
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>>>> -    exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
>>>>>> +    exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
>>>>>> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
>>>>>>         exynos_pcie_init_phy(exynos_pcie);
>>>>>>
>>>>>>         /* pulse for common reset */
>>>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
>>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
>>>>>> +                PCIE_PHY_COMMON_RESET);
>>>>>>         udelay(500);
>>>>>> -    exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
>>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
>>>>>> +                PCIE_PHY_COMMON_RESET);
>>>>>>
>>>>>>         exynos_pcie_deassert_core_reset(exynos_pcie);
>>>>>>         dw_pcie_setup_rc(pp);
>>>>>>         exynos_pcie_assert_reset(exynos_pcie);
>>>>>>
>>>>>>         /* assert LTSSM enable */
>>>>>> -    exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
>>>>>> -              PCIE_APP_LTSSM_ENABLE);
>>>>>> +    exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
>>>>>> +            PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>>>>>>
>>>>>>         /* check if the link is up or not */
>>>>>>         if (!dw_pcie_wait_for_link(pp))
>>>>>>             return 0;
>>>>>>
>>>>>> -    while (exynos_pcie_readl(exynos_pcie->phy_base,
>>>>>> +    while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
>>>>>>                     PCIE_PHY_PLL_LOCKED) == 0) {
>>>>>> -        val = exynos_pcie_readl(exynos_pcie->block_base,
>>>>>> +        val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
>>>>>>                     PCIE_PHY_PLL_LOCKED);
>>>>>>             dev_info(dev, "PLL Locked: 0x%x\n", val);
>>>>>>         }
>>>>>> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
>>>>>>     {
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>>>> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
>>>>>>         /* enable INTX interrupt */
>>>>>>         val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
>>>>>>             IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
>>>>>>     }
>>>>>>
>>>>>>     static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
>>>>>> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
>>>>>>         dw_pcie_msi_init(pp);
>>>>>>
>>>>>>         /* enable MSI interrupt */
>>>>>> -    val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>>>> +    val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
>>>>>>         val |= IRQ_MSI_ENABLE;
>>>>>> -    exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>>>> +    exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
>>>>>>     }
>>>>>>
>>>>>>     static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
>>>>>> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
>>>>>>         struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>>>>>>         u32 val;
>>>>>>
>>>>>> -    val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
>>>>>> +    val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
>>>>>> +                    PCIE_ELBI_RDLH_LINKUP);
>>>>>>         if (val == PCIE_ELBI_LTSSM_ENABLE)
>>>>>>             return 1;
>>>>>>
>>>>>> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>>>         struct exynos_pcie *exynos_pcie;
>>>>>>         struct pcie_port *pp;
>>>>>>         struct device_node *np = dev->of_node;
>>>>>> -    struct resource *res;
>>>>>>         int ret;
>>>>>>
>>>>>>         exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
>>>>>> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
>>>>>>         pp = &exynos_pcie->pp;
>>>>>>         pp->dev = dev;
>>>>>>
>>>>>> -    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>>>> -
>>>>>> -    exynos_pcie->clk = devm_clk_get(dev, "pcie");
>>>>>> -    if (IS_ERR(exynos_pcie->clk)) {
>>>>>> -        dev_err(dev, "Failed to get pcie rc clock\n");
>>>>>> -        return PTR_ERR(exynos_pcie->clk);
>>>>>> -    }
>>>>>> -    ret = clk_prepare_enable(exynos_pcie->clk);
>>>>>> -    if (ret)
>>>>>> -        return ret;
>>>>>> +    exynos_pcie->ops = (const struct exynos_pcie_ops *)
>>>>>> +                of_device_get_match_data(dev);
>>>>>>
>>>>>> -    exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
>>>>>> -    if (IS_ERR(exynos_pcie->bus_clk)) {
>>>>>> -        dev_err(dev, "Failed to get pcie bus clock\n");
>>>>>> -        ret = PTR_ERR(exynos_pcie->bus_clk);
>>>>>> -        goto fail_clk;
>>>>>> -    }
>>>>>> -    ret = clk_prepare_enable(exynos_pcie->bus_clk);
>>>>>> -    if (ret)
>>>>>> -        goto fail_clk;
>>>>>> +    exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>>>>>>
>>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>> -    exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
>>>>>> -    if (IS_ERR(exynos_pcie->elbi_base)) {
>>>>>> -        ret = PTR_ERR(exynos_pcie->elbi_base);
>>>>>> -        goto fail_bus_clk;
>>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
>>>>>> +        ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>>         }
>>>>>>
>>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>>>>> -    exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
>>>>>> -    if (IS_ERR(exynos_pcie->phy_base)) {
>>>>>> -        ret = PTR_ERR(exynos_pcie->phy_base);
>>>>>> -        goto fail_bus_clk;
>>>>>> -    }
>>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
>>>>>> +        ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>>
>>>>>> -    res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>>>>> -    exynos_pcie->block_base = devm_ioremap_resource(dev, res);
>>>>>> -    if (IS_ERR(exynos_pcie->block_base)) {
>>>>>> -        ret = PTR_ERR(exynos_pcie->block_base);
>>>>>> -        goto fail_bus_clk;
>>>>>> +        ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>>         }
>>>>>>
>>>>>>         ret = exynos_add_pcie_port(exynos_pcie, pdev);
>>>>>>         if (ret < 0)
>>>>>> -        goto fail_bus_clk;
>>>>>> +        goto fail_probe;
>>>>>>
>>>>>>         platform_set_drvdata(pdev, exynos_pcie);
>>>>>>         return 0;
>>>>>>
>>>>>> -fail_bus_clk:
>>>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>>>> -fail_clk:
>>>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>>>> +fail_probe:
>>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>>>         return ret;
>>>>>>     }
>>>>>>
>>>>>> @@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
>>>>>>     {
>>>>>>         struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>>>>>>
>>>>>> -    clk_disable_unprepare(exynos_pcie->bus_clk);
>>>>>> -    clk_disable_unprepare(exynos_pcie->clk);
>>>>>> +    if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
>>>>>> +        exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>>>>>>
>>>>>>         return 0;
>>>>>>     }
>>>>>>
>>>>>>     static const struct of_device_id exynos_pcie_of_match[] = {
>>>>>> -    { .compatible = "samsung,exynos5440-pcie", },
>>>>>> +    {    .compatible = "samsung,exynos5440-pcie",
>>>>>> +        .data = &exynos5440_pcie_ops },
>>>>>>         {},
>>>>>>     };
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>>
>>
>>
>
>
>

^ permalink raw reply

* [PATCH 3/3 v2] iio: adc: add a driver for Qualcomm PM8xxx HK/XOADC
From: Bjorn Andersson @ 2016-12-27  6:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1481842118-14095-1-git-send-email-linus.walleij@linaro.org>

On Thu 15 Dec 14:48 PST 2016, Linus Walleij wrote:

> The Qualcomm PM8xxx PMICs contain a simpler ADC than its
> successors (already in the kernel as qcom-spmi-adc.c):
> the HK/XO ADC (Housekeeping/Chrystal oscillator ADC).
> 
> As far as I can understand this is equal to the PMICs
> using SSBI transport and encompass PM8018, PM8038,
> PM8058, PM8917 and PM8921, so this is shortly named
> PM8xxx.
> 
> This ADC monitors a bunch of on-board voltages and the die
> temperature of the PMIC itself, but it can also be routed
> to convert a few external MPPs (multi-purpose pins). On
> the APQ8060 DragonBoard this feature is used to let this
> ADC convert an analog ALS (Ambient Light Sensor) voltage
> signal from a Capella CM3605 ALS into a LUX value.
> 
> Developed and tested with APQ8060 DragonBoard based on
> Ivan's driver. The SPMI VADC driver is quite different,
> but share enough minor functionality that I have split
> out to the common file in a previous patch.

I gave this a spin on one of my APQ8064 devices - with PM8921 - and I
can do measurements.

Several of the channels do give me reasonable readings. But the channels
in PM8921 are different than PM8058, so it looks like we need additional
prescales defined. Further more, the 8921 has several different
thermo-related channels, each with their own (post-)scaling function.

And lastly it seems that I have 2 thermo-channels on "mpp amux", but I'm
still confused to how that is supposed to work in the downstream code -
I will need to investigate this further.


One of the channels defined in my APQ8064 device measures
ADC_MPP_1_AMUX5, a lookup table is used to convert the measured value to
a temperature. As the table depends on an external thermistor, which
seems to differ between products a number of lookup tables exists - with
156 data points each.

[..]
> +
> +/*
> + * The different channels have hard-coded prescale ratios defined
> + * by the hardware.
> + */
> +static const struct vadc_prescale_ratio adc_prescale_ratios[] = {
> +	{ .num = 1, .den = 2 }, /* CHANNEL_VCOIN */
> +	{ .num = 1, .den = 3 }, /* CHANNEL_VBAT */
> +	{ .num = 1, .den = 10 }, /* CHANNEL_VCHG */
> +	{ .num = 1, .den = 1 }, /* CHANNEL_CHG_MONITOR */
> +	{ .num =  1, .den = 3 }, /* CHANNEL_VPH_PWR */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_MPP5 */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_MPP6 */
> +	{ .num =  1, .den = 2 }, /* CHANNEL_MPP7 */
> +	{ .num =  1, .den = 2 }, /* CHANNEL_MPP8 */
> +	{ .num =  1, .den = 3 }, /* CHANNEL_MPP9 */
> +	{ .num =  1, .den = 3 }, /* CHANNEL_USB_VBUS */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_DIE_TEMP */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_INTERNAL */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_125V */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_INTERNAL_2 */
> +	{ .num =  1, .den = 1 }, /* CHANNEL_MUXOFF */
> +};

The pm8921 have different meaning for the channels and the prescale
values are hence different. In the downstream kernel the values comes
from the board file, but I don't know if that means they will ever
change.

[..]
> +
> +static int pm8xxx_read_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
> +	const struct pm8xxx_chan_info *ch;
> +	u16 adc_code;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_PROCESSED:
> +		/* Only die temperature ends up here */
> +		ch = pm8xxx_get_channel(adc, chan->address);
> +		if (!ch) {
> +			dev_err(adc->dev, "no such channel %lu\n",
> +				chan->address);
> +			return -EINVAL;
> +		}
> +		ret = pm8xxx_read_channel(adc, ch, &adc_code);
> +		if (ret)
> +			return ret;
> +		*val = pm8xxx_calibrate(adc, ch, adc_code);
> +		/* 2mV/K, return milli Celsius */
> +		*val /= 2;
> +		*val -= KELVINMIL_CELSIUSMIL;

On my 8064 device I have 6 different temperatures (2 coming from amux'ed
mpp channels). They all have different lookup tables, which seems
calibrated based on some external resistor.

> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_RAW:
> +		switch (chan->type) {
> +		case IIO_VOLTAGE:
> +			ch = pm8xxx_get_channel(adc, chan->address);
> +			if (!ch) {
> +				dev_err(adc->dev, "no such channel %lu\n",
> +					chan->address);
> +				return -EINVAL;
> +			}
> +			ret = pm8xxx_read_channel(adc, ch, &adc_code);
> +			if (ret)
> +				return ret;
> +			*val = pm8xxx_calibrate(adc, ch, adc_code);
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_SCALE:
> +		/*
> +		 * Applies to all voltage channels: we scale the microvolts
> +		 * to millivolts as required by the userspace ABI.
> +		 */
> +		*val = 0;
> +		*val2 = 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
> +			   const struct of_phandle_args *iiospec)
> +{
> +	struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
> +	unsigned int i;
> +
> +	for (i = 0; i < adc->nchans; i++)
> +		if (adc->iio_chans[i].channel == iiospec->args[0])
> +			return i;
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info pm8xxx_xoadc_info = {
> +	.driver_module = THIS_MODULE,
> +	.of_xlate = pm8xxx_of_xlate,
> +	.read_raw = pm8xxx_read_raw,
> +};
> +
> +static int pm8xxx_xoadc_parse_channel(struct device *dev,
> +				      struct device_node *np,
> +				      struct pm8xxx_chan_info *ch)
> +{
> +	const char *name = np->name;
> +	u32 chan, rsv, dec;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "reg", &chan);
> +	if (ret) {
> +		dev_err(dev, "invalid channel number %s\n", name);
> +		return ret;
> +	}
> +	if (chan > XOADC_CHAN_MAX) {
> +		dev_err(dev, "%s too big channel number %d\n", name, chan);
> +		return -EINVAL;
> +	}
> +
> +	if (of_property_read_bool(np, "qcom,ratiometric")) {
> +		ch->calibration = VADC_CALIB_RATIOMETRIC;
> +		ret = of_property_read_u32(np, "qcom,ratiometric-ref", &rsv);

If this is what you intended, you can squash the two properties into
one.

> +		if (ret) {
> +			dev_err(dev, "invalid RSV %s\n", name);
> +			return ret;
> +		}
> +		if (rsv > XOADC_RSV_MAX) {
> +			dev_err(dev, "%s too large RSV value %d\n", name, rsv);
> +			return -EINVAL;
> +		}
> +		if (rsv == AMUX_RSV3) {
> +			dev_err(dev, "%s invalid RSV value %d\n", name, rsv);
> +			return -EINVAL;
> +		}
> +	} else {
> +		ch->calibration = VADC_CALIB_ABSOLUTE;
> +		rsv = 0;
> +	}
> +
> +	ret = of_property_read_u32(np, "qcom,decimation", &dec);
> +	if (!ret) {
> +		/* It's OK to skip this ... */
> +		ret = qcom_vadc_decimation_from_dt(dec);
> +		if (ret < 0) {
> +			dev_err(dev, "%s invalid decimation %d\n",
> +				name, dec);
> +			return ret;
> +		}
> +		ch->decimation = ret;
> +	} else {
> +		ch->decimation = VADC_DEF_DECIMATION;
> +	}
> +
> +	ch->amux_channel = chan;
> +	ch->name = name;
> +	ch->amux_ip_rsv = rsv;
> +	ch->amux_mpp_channel = PREMUX_MPP_SCALE_0; /* FIXME: get from DT */

After reading the code related to "amux/mpp" I've quite confused, it
does indeed look like the only thing done is to apply some scaling to
the value - but this doesn't make sense looking at the board files and
naming of the defines used downstream.

I will have to try to find some hints in the documentation and play
around with this a little bit more.

> +
> +	dev_dbg(dev, "channel %d \"%s\" ref voltage: %d, decimation %d\n",
> +		ch->amux_channel,
> +		ch->name,
> +		ch->amux_ip_rsv,
> +		ch->decimation);
> +	return 0;
> +}
> +
> +static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc,
> +				       struct device_node *np)
> +{
> +	struct device_node *child;
> +	struct pm8xxx_chan_info *ch;
> +	int ret;
> +	int i;
> +
> +	adc->nchans = of_get_available_child_count(np);
> +	if (!adc->nchans) {
> +		dev_err(adc->dev, "no channel children\n");
> +		return -ENODEV;
> +	}
> +	dev_dbg(adc->dev, "found %d ADC channels\n", adc->nchans);
> +
> +	adc->iio_chans = devm_kcalloc(adc->dev, adc->nchans,
> +				      sizeof(*adc->iio_chans), GFP_KERNEL);
> +	if (!adc->iio_chans)
> +		return -ENOMEM;
> +
> +	adc->chans = devm_kcalloc(adc->dev, adc->nchans,
> +				  sizeof(*adc->chans), GFP_KERNEL);
> +	if (!adc->chans)
> +		return -ENOMEM;
> +
> +	i = 0;
> +	for_each_available_child_of_node(np, child) {
> +		struct iio_chan_spec *iio_chan;
> +
> +		ch = &adc->chans[i];
> +		ret = pm8xxx_xoadc_parse_channel(adc->dev, child, ch);
> +		if (ret) {
> +			of_node_put(child);
> +			return ret;
> +		}
> +
> +		iio_chan = &adc->iio_chans[i];
> +		iio_chan->channel = ch->amux_channel;
> +		iio_chan->datasheet_name = ch->name;
> +		/* A single temperature channel, the rest are voltages */
> +		if (ch->amux_channel == CHANNEL_DIE_TEMP) {
> +			iio_chan->type = IIO_TEMP;
> +			iio_chan->info_mask_separate =
> +				BIT(IIO_CHAN_INFO_PROCESSED);
> +		} else {
> +			iio_chan->type = IIO_VOLTAGE;
> +			iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				BIT(IIO_CHAN_INFO_SCALE);
> +		}
> +		iio_chan->indexed = 1;
> +		iio_chan->address = ch->amux_channel;
> +
> +		i++;
> +	}
> +
> +	/* Check for required channels */
> +	ch = pm8xxx_get_channel(adc, CHANNEL_125V);
> +	if (!ch) {
> +		dev_err(adc->dev, "missing 1.25V reference channel\n");
> +		return -ENODEV;
> +	}
> +	ch = pm8xxx_get_channel(adc, CHANNEL_INTERNAL);
> +	if (!ch) {
> +		dev_err(adc->dev, "missing 0.625V reference channel\n");
> +		return -ENODEV;
> +	}
> +	ch = pm8xxx_get_channel(adc, CHANNEL_MUXOFF);
> +	if (!ch) {
> +		dev_err(adc->dev, "missing MUXOFF reference channel\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_xoadc_probe(struct platform_device *pdev)
> +{
> +	struct pm8xxx_xoadc *adc;
> +	struct iio_dev *indio_dev;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct regmap *map;
> +	struct device *dev = &pdev->dev;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	adc = iio_priv(indio_dev);
> +	adc->dev = dev;
> +	init_completion(&adc->complete);
> +	mutex_init(&adc->lock);
> +
> +	ret = pm8xxx_xoadc_parse_channels(adc, np);
> +	if (ret)
> +		return ret;
> +
> +	map = dev_get_regmap(dev->parent, NULL);
> +	if (!map) {
> +		dev_err(dev, "Parent regmap unavailable.\n");
> +		return -ENXIO;
> +	}
> +	adc->map = map;
> +
> +	/* Bring up regulator */
> +	adc->vref = devm_regulator_get(dev, "xoadc-ref");
> +	if (IS_ERR(adc->vref)) {
> +		dev_err(dev, "failed to get XOADC VREF regulator\n");
> +		return PTR_ERR(adc->vref);
> +	}
> +	/* We strictly require this voltage */
> +	ret = regulator_set_voltage(adc->vref, 2200000, 2200000);

On 8064 the reference is L14 and it's at 1.8V, so this fails for me.

However, the way that this is written should reflect on the board design
and your L18 (and my L14) will have a single valid value. As such  there
should be no need to set a voltage here (other then to catch mistakes).

> +	if (ret) {
> +		dev_err(dev, "unable to set LDO18 voltage to 2.2V\n");
> +		return ret;
> +	}

Regards,
Bjorn

^ permalink raw reply

* [PATCH v3 0/3] make kurobox-pro be able to shutdown after device-tree migration
From: Roger Shimizu @ 2016-12-27  7:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161216100501.18173-1-rogershimizu@gmail.com>

Dear Kernel Maintainers,

Kurobox-Pro (and variants) need more commands sending to UART1 to shutdown.
So here I make this patch series to let current qnap-poweroff implementation
be able to handle such case.

Here I give 3 patches, for 3 different kernel subsystems respectivelay:
  - Patch 1/3: System reset/shutdown driver
  - Patch 2/3: Open firmware and flattened device tree bindings
  - Patch 3/3: ARM/Marvell Dove/MV78xx0/Orion SOC support

Ryan Tandy, the issue reporter, and I have already tested those changes on
Linkstation/KuroBoxPro devices, and confirmed it's working well.

Merry Xmax and look forward to your feedback!

Changes:
  v0 => v1:
  - Update 0003 to split kuroboxpro related code into kuroboxpro-common.c
  v1 => v2:
  - Split off linkstation/kuroboxpro related code to linkstation-reset.c
    Because linkstation before kuroboxpro also need this driver to power
    off properly. It's more proper to call it linkstation driver.
  v2 => v3:
  - Split off devicetree bindings doc into a separate patch.
  - Add device-tree patch to make Linkstation LS-GL and KuroBox Pro to
    use the newly created linkstation-reset driver.
  - Remove the unused one-byte command sending case in linkstation-reset
    driver.

Cheers,
Roger Shimizu, GMT +9 Tokyo
PGP/GPG: 4096R/6C6ACD6417B3ACB1

Roger Shimizu (3):
  power: reset: add linkstation-reset driver
  DT: bingdings: power: reset: add linkstation-reset doc
  ARM: DT: add power-off support to linkstation lsgl and kuroboxpro

 .../bindings/power/reset/linkstation-reset.txt     |  26 ++
 arch/arm/boot/dts/orion5x-kuroboxpro.dts           |   8 +
 arch/arm/boot/dts/orion5x-linkstation-lsgl.dts     |  10 +
 arch/arm/boot/dts/orion5x-linkstation-lswtgl.dts   |   4 +
 arch/arm/boot/dts/orion5x-linkstation.dtsi         |   4 -
 drivers/power/reset/Kconfig                        |  10 +
 drivers/power/reset/Makefile                       |   1 +
 drivers/power/reset/linkstation-reset.c            | 269 +++++++++++++++++++++
 8 files changed, 328 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/reset/linkstation-reset.txt
 create mode 100644 drivers/power/reset/linkstation-reset.c

-- 
2.11.0

^ permalink raw reply

* [PATCH v3 1/3] power: reset: add linkstation-reset driver
From: Roger Shimizu @ 2016-12-27  7:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161227070611.14852-1-rogershimizu@gmail.com>

Buffalo Linkstation / KuroBox and their variants need magic command
sending to UART1 to power-off.

Power driver linkstation-reset implements the magic command and I/O
routine, which come from files listed below:
  - arch/arm/mach-orion5x/kurobox_pro-setup.c
  - arch/arm/mach-orion5x/terastation_pro2-setup.c

To: Sebastian Reichel <sre@kernel.org>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Martin Michlmayr <tbm@cyrius.com>
Cc: Sylver Bruneau <sylver.bruneau@googlemail.com>
Cc: Herbert Valerio Riedel <hvr@gnu.org>
Cc: Ryan Tandy <ryan@nardis.ca>
Cc: linux-pm at vger.kernel.org
Cc: devicetree at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org

Reported-by: Ryan Tandy <ryan@nardis.ca>
Tested-by: Ryan Tandy <ryan@nardis.ca>
Signed-off-by: Roger Shimizu <rogershimizu@gmail.com>
---
 drivers/power/reset/Kconfig             |  10 ++
 drivers/power/reset/Makefile            |   1 +
 drivers/power/reset/linkstation-reset.c | 269 ++++++++++++++++++++++++++++++++
 3 files changed, 280 insertions(+)
 create mode 100644 drivers/power/reset/linkstation-reset.c

diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index c74c3f67b8da..77c44cad7ece 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -98,6 +98,16 @@ config POWER_RESET_IMX
 	  say N here or disable in dts to make sure pm_power_off never be
 	  overwrote wrongly by this driver.
 
+config POWER_RESET_LINKSTATION
+	bool "Buffalo Linkstation and its variants reset driver"
+	depends on OF_GPIO && PLAT_ORION
+	help
+	  This driver supports power off Buffalo Linkstation / KuroBox Pro
+	  NAS and their variants by sending commands to the micro-controller
+	  which controls the main power.
+
+	  Say Y if you have a Buffalo Linkstation / KuroBox Pro NAS.
+
 config POWER_RESET_MSM
 	bool "Qualcomm MSM power-off driver"
 	depends on ARCH_QCOM
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 1be307c7fc25..692ba6417cfb 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
 obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
 obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
 obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o
+obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-reset.o
 obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
 obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
 obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
diff --git a/drivers/power/reset/linkstation-reset.c b/drivers/power/reset/linkstation-reset.c
new file mode 100644
index 000000000000..4390495dfe0e
--- /dev/null
+++ b/drivers/power/reset/linkstation-reset.c
@@ -0,0 +1,269 @@
+/*
+ * Buffalo Linkstation power reset driver.
+ * It may also be used on following devices:
+ *  - KuroBox Pro
+ *  - Buffalo Linkstation Pro (LS-GL)
+ *  - Buffalo Terastation Pro II/Live
+ *  - Buffalo Linkstation Duo (LS-WTGL)
+ *  - Buffalo Linkstation Mini (LS-WSGL)
+ *
+ * Copyright (C) 2016  Roger Shimizu <rogershimizu@gmail.com>
+ *
+ * Based on the code from:
+ *
+ * Copyright (C) 2012  Andrew Lunn <andrew@lunn.ch>
+ * Copyright (C) 2009  Martin Michlmayr <tbm@cyrius.com>
+ * Copyright (C) 2008  Byron Bradley <byron.bbradley@gmail.com>
+ * Copyright (C) 2008  Sylver Bruneau <sylver.bruneau@googlemail.com>
+ * Copyright (C) 2007  Herbert Valerio Riedel <hvr@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial_reg.h>
+#include <linux/kallsyms.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#define UART1_REG(x)	(base + ((UART_##x) << 2))
+#define MICON_CMD_SIZE	4
+
+/* 4-byte magic hello command to UART1-attached microcontroller */
+static const unsigned char linkstation_micon_magic[] = {
+	0x1b,
+	0x00,
+	0x07,
+	0x00
+};
+
+/* for each row, first byte is the size of command */
+static const unsigned char linkstation_power_off_cmd[][MICON_CMD_SIZE] = {
+	{ 3,	0x01, 0x35, 0x00},
+	{ 2,	0x00, 0x0c},
+	{ 2,	0x00, 0x06},
+	{}
+};
+
+struct reset_cfg {
+	u32 baud;
+	const unsigned char *magic;
+	const unsigned char (*cmd)[MICON_CMD_SIZE];
+};
+
+static const struct reset_cfg linkstation_power_off_cfg = {
+	.baud = 38400,
+	.magic = linkstation_micon_magic,
+	.cmd = linkstation_power_off_cmd,
+};
+
+static const struct of_device_id linkstation_reset_of_match_table[] = {
+	{ .compatible = "linkstation,power-off",
+	  .data = &linkstation_power_off_cfg,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, linkstation_reset_of_match_table);
+
+static int uart1_micon_read(void *base, unsigned char *buf, int count)
+{
+	int i;
+	int timeout;
+
+	for (i = 0; i < count; i++) {
+		timeout = 10;
+
+		while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
+			if (--timeout == 0)
+				break;
+			udelay(1000);
+		}
+
+		if (timeout == 0)
+			break;
+		buf[i] = readl(UART1_REG(RX));
+	}
+
+	/* return read bytes */
+	return i;
+}
+
+static int uart1_micon_write(void *base, const unsigned char *buf, int count)
+{
+	int i = 0;
+
+	while (count--) {
+		while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
+			barrier();
+		writel(buf[i++], UART1_REG(TX));
+	}
+
+	return 0;
+}
+
+int uart1_micon_send(void *base, const unsigned char *data, int count)
+{
+	int i;
+	unsigned char checksum = 0;
+	unsigned char recv_buf[40];
+	unsigned char send_buf[40];
+	unsigned char correct_ack[3];
+	int retry = 2;
+
+	/* Generate checksum */
+	for (i = 0; i < count; i++)
+		checksum -=  data[i];
+
+	do {
+		/* Send data */
+		uart1_micon_write(base, data, count);
+
+		/* send checksum */
+		uart1_micon_write(base, &checksum, 1);
+
+		if (uart1_micon_read(base, recv_buf, sizeof(recv_buf)) <= 3) {
+			printk(KERN_ERR ">%s: receive failed.\n", __func__);
+
+			/* send preamble to clear the receive buffer */
+			memset(&send_buf, 0xff, sizeof(send_buf));
+			uart1_micon_write(base, send_buf, sizeof(send_buf));
+
+			/* make dummy reads */
+			mdelay(100);
+			uart1_micon_read(base, recv_buf, sizeof(recv_buf));
+		} else {
+			/* Generate expected ack */
+			correct_ack[0] = 0x01;
+			correct_ack[1] = data[1];
+			correct_ack[2] = 0x00;
+
+			/* checksum Check */
+			if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
+			     recv_buf[3]) & 0xFF) {
+				printk(KERN_ERR ">%s: Checksum Error : "
+					"Received data[%02x, %02x, %02x, %02x]"
+					"\n", __func__, recv_buf[0],
+					recv_buf[1], recv_buf[2], recv_buf[3]);
+			} else {
+				/* Check Received Data */
+				if (correct_ack[0] == recv_buf[0] &&
+				    correct_ack[1] == recv_buf[1] &&
+				    correct_ack[2] == recv_buf[2]) {
+					/* Interval for next command */
+					mdelay(10);
+
+					/* Receive ACK */
+					return 0;
+				}
+			}
+			/* Received NAK or illegal Data */
+			printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
+					"Received\n", __func__);
+		}
+	} while (retry--);
+
+	/* Interval for next command */
+	mdelay(10);
+
+	return -1;
+}
+
+static void __iomem *base;
+static unsigned long tclk;
+static const struct reset_cfg *cfg;
+
+static void linkstation_reset(void)
+{
+	const unsigned divisor = ((tclk + (8 * cfg->baud)) / (16 * cfg->baud));
+	int i;
+
+	pr_err("%s: triggering power-off...\n", __func__);
+
+	/* hijack UART1 and reset into sane state */
+	writel(0x83, UART1_REG(LCR));
+	writel(divisor & 0xff, UART1_REG(DLL));
+	writel((divisor >> 8) & 0xff, UART1_REG(DLM));
+	writel(cfg->magic[0], UART1_REG(LCR));
+	writel(cfg->magic[1], UART1_REG(IER));
+	writel(cfg->magic[2], UART1_REG(FCR));
+	writel(cfg->magic[3], UART1_REG(MCR));
+
+	/* send the power-off command to PIC */
+	for(i = 0; cfg->cmd[i][0] > 0; i ++) {
+		/* [0] is size of the command; command starts from [1] */
+		uart1_micon_send(base, &(cfg->cmd[i][1]), cfg->cmd[i][0]);
+	}
+}
+
+static int linkstation_reset_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	struct clk *clk;
+	char symname[KSYM_NAME_LEN];
+
+	const struct of_device_id *match =
+		of_match_node(linkstation_reset_of_match_table, np);
+	cfg = match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing resource");
+		return -EINVAL;
+	}
+
+	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!base) {
+		dev_err(&pdev->dev, "Unable to map resource");
+		return -EINVAL;
+	}
+
+	/* We need to know tclk in order to calculate the UART divisor */
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Clk missing");
+		return PTR_ERR(clk);
+	}
+
+	tclk = clk_get_rate(clk);
+
+	/* Check that nothing else has already setup a handler */
+	if (pm_power_off) {
+		lookup_symbol_name((ulong)pm_power_off, symname);
+		dev_err(&pdev->dev,
+			"pm_power_off already claimed %p %s",
+			pm_power_off, symname);
+		return -EBUSY;
+	}
+	pm_power_off = linkstation_reset;
+
+	return 0;
+}
+
+static int linkstation_reset_remove(struct platform_device *pdev)
+{
+	pm_power_off = NULL;
+	return 0;
+}
+
+static struct platform_driver linkstation_reset_driver = {
+	.probe	= linkstation_reset_probe,
+	.remove	= linkstation_reset_remove,
+	.driver	= {
+		.name	= "linkstation_reset",
+		.of_match_table = of_match_ptr(linkstation_reset_of_match_table),
+	},
+};
+
+module_platform_driver(linkstation_reset_driver);
+
+MODULE_AUTHOR("Roger Shimizu <rogershimizu@gmail.com>");
+MODULE_DESCRIPTION("Linkstation Reset driver");
+MODULE_LICENSE("GPL v2");
-- 
2.11.0

^ permalink raw reply related


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