Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 08/11] ARM: shmobile: genmai legacy: Add RSPI support
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Geert Uytterhoeven <geert+renesas@linux-m68k.org>

Add RSPI platform device, resources, platform data, and SPI child.

On this board, only rspi4 is in use. Its bus contains a single device
(a wm8978 audio codec).

Signed-off-by: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
Acked-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/board-genmai.c | 44 +++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm/mach-shmobile/board-genmai.c b/arch/arm/mach-shmobile/board-genmai.c
index 3e92e3c..c406461 100644
--- a/arch/arm/mach-shmobile/board-genmai.c
+++ b/arch/arm/mach-shmobile/board-genmai.c
@@ -20,15 +20,59 @@
 
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <linux/spi/rspi.h>
+#include <linux/spi/spi.h>
 #include <mach/common.h>
+#include <mach/irqs.h>
 #include <mach/r7s72100.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
+/* RSPI */
+#define RSPI_RESOURCE(idx, baseaddr, irq)				\
+static const struct resource rspi##idx##_resources[] __initconst = {	\
+	DEFINE_RES_MEM(baseaddr, 0x24),					\
+	DEFINE_RES_IRQ_NAMED(irq, "error"),				\
+	DEFINE_RES_IRQ_NAMED(irq + 1, "rx"),				\
+	DEFINE_RES_IRQ_NAMED(irq + 2, "tx"),				\
+}
+
+RSPI_RESOURCE(0, 0xe800c800, gic_iid(270));
+RSPI_RESOURCE(1, 0xe800d000, gic_iid(273));
+RSPI_RESOURCE(2, 0xe800d800, gic_iid(276));
+RSPI_RESOURCE(3, 0xe800e000, gic_iid(279));
+RSPI_RESOURCE(4, 0xe800e800, gic_iid(282));
+
+static const struct rspi_plat_data rspi_pdata __initconst = {
+	.num_chipselect	= 1,
+};
+
+#define r7s72100_register_rspi(idx)					   \
+	platform_device_register_resndata(&platform_bus, "rspi-rz", idx,   \
+					rspi##idx##_resources,		   \
+					ARRAY_SIZE(rspi##idx##_resources), \
+					&rspi_pdata, sizeof(rspi_pdata))
+
+static const struct spi_board_info spi_info[] __initconst = {
+	{
+		.modalias               = "wm8978",
+		.max_speed_hz           = 5000000,
+		.bus_num                = 4,
+		.chip_select            = 0,
+	},
+};
+
 static void __init genmai_add_standard_devices(void)
 {
 	r7s72100_clock_init();
 	r7s72100_add_dt_devices();
+
+	r7s72100_register_rspi(0);
+	r7s72100_register_rspi(1);
+	r7s72100_register_rspi(2);
+	r7s72100_register_rspi(3);
+	r7s72100_register_rspi(4);
+	spi_register_board_info(spi_info, ARRAY_SIZE(spi_info));
 }
 
 static const char * const genmai_boards_compat_dt[] __initconst = {
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 07/11] Revert "ARM: shmobile: marzen: Conditionally select SMSC_PHY"
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

This reverts commit 317af6612ee29dfcb5ae04df9c58e9f79fc8d4ff.

This seems to prevent the board from booting now that the tree has been
rebased on v3.14-rc1.

Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/Kconfig | 2 --
 1 file changed, 2 deletions(-)

diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index bcbd74c..c431114 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -250,7 +250,6 @@ config MACH_MARZEN
 	depends on ARCH_R8A7779
 	select ARCH_REQUIRE_GPIOLIB
 	select REGULATOR_FIXED_VOLTAGE if REGULATOR
-	select SMSC_PHY if SMSC911X
 	select USE_OF
 
 config MACH_MARZEN_REFERENCE
@@ -258,7 +257,6 @@ config MACH_MARZEN_REFERENCE
 	depends on ARCH_R8A7779
 	select ARCH_REQUIRE_GPIOLIB
 	select REGULATOR_FIXED_VOLTAGE if REGULATOR
-	select SMSC_PHY if SMSC911X
 	select USE_OF
 	---help---
 	   Use reference implementation of Marzen board support
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 06/11] ARM: shmobile: lager: add sound support
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

This patch adds sound support for Lager board.
But, it is using PIO transfer at this point.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
[horms+renesas at verge.net.au: resolved conflicts]
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/Kconfig       |   1 +
 arch/arm/mach-shmobile/board-lager.c | 100 +++++++++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)

diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 7e3f42b..bcbd74c 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -272,6 +272,7 @@ config MACH_LAGER
 	depends on ARCH_R8A7790
 	select USE_OF
 	select MICREL_PHY if SH_ETH
+	select SND_SOC_AK4642 if SND_SIMPLE_CARD
 
 config MACH_KOELSCH
 	bool "Koelsch board"
diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index 6a7041f..e8242c5 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -20,6 +20,7 @@
 
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
+#include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -52,6 +53,20 @@
 #include <linux/spi/flash.h>
 #include <linux/spi/rspi.h>
 #include <linux/spi/spi.h>
+#include <sound/rcar_snd.h>
+#include <sound/simple_card.h>
+
+/*
+ * SSI-AK4643
+ *
+ * SW1: 1: AK4643
+ *      2: CN22
+ *      3: ADV7511
+ *
+ * this command is required when playback.
+ *
+ * # amixer set "LINEOUT Mixer DACL" on
+ */
 
 /* DU */
 static struct rcar_du_encoder_data lager_du_encoders[] = {
@@ -509,6 +524,77 @@ static const struct resource usbhs_phy_resources[] __initconst = {
 	DEFINE_RES_MEM(0xe6590100, 0x100),
 };
 
+/* I2C */
+static struct i2c_board_info i2c2_devices[] = {
+	{
+		I2C_BOARD_INFO("ak4643", 0x12),
+	}
+};
+
+/* Sound */
+static struct resource rsnd_resources[] __initdata = {
+	[RSND_GEN2_SCU]  = DEFINE_RES_MEM(0xec500000, 0x1000),
+	[RSND_GEN2_ADG]  = DEFINE_RES_MEM(0xec5a0000, 0x100),
+	[RSND_GEN2_SSIU] = DEFINE_RES_MEM(0xec540000, 0x1000),
+	[RSND_GEN2_SSI]  = DEFINE_RES_MEM(0xec541000, 0x1280),
+};
+
+static struct rsnd_ssi_platform_info rsnd_ssi[] = {
+	RSND_SSI_SET(0, 0, gic_spi(370), RSND_SSI_PLAY),
+	RSND_SSI_SET(0, 0, gic_spi(371), RSND_SSI_CLK_PIN_SHARE),
+};
+
+static struct rsnd_scu_platform_info rsnd_scu[2] = {
+	/* no member at this point */
+};
+
+static struct rcar_snd_info rsnd_info = {
+	.flags		= RSND_GEN2,
+	.ssi_info	= rsnd_ssi,
+	.ssi_info_nr	= ARRAY_SIZE(rsnd_ssi),
+	.scu_info	= rsnd_scu,
+	.scu_info_nr	= ARRAY_SIZE(rsnd_scu),
+};
+
+static struct asoc_simple_card_info rsnd_card_info = {
+	.name		= "AK4643",
+	.card		= "SSI01-AK4643",
+	.codec		= "ak4642-codec.2-0012",
+	.platform	= "rcar_sound",
+	.daifmt		= SND_SOC_DAIFMT_LEFT_J,
+	.cpu_dai = {
+		.name	= "rcar_sound",
+		.fmt	= SND_SOC_DAIFMT_CBS_CFS,
+	},
+	.codec_dai = {
+		.name	= "ak4642-hifi",
+		.fmt	= SND_SOC_DAIFMT_CBM_CFM,
+		.sysclk	= 11289600,
+	},
+};
+
+static void __init lager_add_rsnd_device(void)
+{
+	struct platform_device_info cardinfo = {
+		.parent         = &platform_bus,
+		.name           = "asoc-simple-card",
+		.id             = -1,
+		.data           = &rsnd_card_info,
+		.size_data      = sizeof(struct asoc_simple_card_info),
+		.dma_mask       = DMA_BIT_MASK(32),
+	};
+
+	i2c_register_board_info(2, i2c2_devices,
+				ARRAY_SIZE(i2c2_devices));
+
+	platform_device_register_resndata(
+		&platform_bus, "rcar_sound", -1,
+		rsnd_resources, ARRAY_SIZE(rsnd_resources),
+		&rsnd_info, sizeof(rsnd_info));
+
+	platform_device_register_full(&cardinfo);
+}
+
 static const struct pinctrl_map lager_pinctrl_map[] = {
 	/* DU (CN10: ARGB0, CN13: LVDS) */
 	PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7790", "pfc-r8a7790",
@@ -517,12 +603,24 @@ static const struct pinctrl_map lager_pinctrl_map[] = {
 				  "du_sync_1", "du"),
 	PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7790", "pfc-r8a7790",
 				  "du_clk_out_0", "du"),
+	/* I2C2 */
+	PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar.2", "pfc-r8a7790",
+				  "i2c2", "i2c2"),
 	/* SCIF0 (CN19: DEBUG SERIAL0) */
 	PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.6", "pfc-r8a7790",
 				  "scif0_data", "scif0"),
 	/* SCIF1 (CN20: DEBUG SERIAL1) */
 	PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.7", "pfc-r8a7790",
 				  "scif1_data", "scif1"),
+	/* SSI (CN17: sound) */
+	PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+				  "ssi0129_ctrl", "ssi"),
+	PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+				  "ssi0_data", "ssi"),
+	PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+				  "ssi1_data", "ssi"),
+	PIN_MAP_MUX_GROUP_DEFAULT("rcar_sound", "pfc-r8a7790",
+				  "audio_clk_a", "audio_clk"),
 	/* MMCIF1 */
 	PIN_MAP_MUX_GROUP_DEFAULT("sh_mmcif.1", "pfc-r8a7790",
 				  "mmc1_data8", "mmc1"),
@@ -616,6 +714,8 @@ static void __init lager_add_standard_devices(void)
 					  &usbhs_phy_pdata,
 					  sizeof(usbhs_phy_pdata));
 	lager_register_usbhs();
+
+	lager_add_rsnd_device();
 }
 
 /*
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 05/11] ARM: shmobile: Lager USB0 cable detection workaround
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Magnus Damm <damm@opensource.se>

Add Lager board code to check the PWEN GPIO signal and refuse to
allow probe of the USBHS driver in case of DIP misconfiguration.

For correct operation Lager DIP switches SW5 and SW6 shall be
configured in 2-3 position to enable USB Function support.

If the DIP switch is configured incorrectly then the user can
simply adjust the hardware and either reboot or use the bind interface
to try to probe again:

# echo renesas_usbhs > /sys/bus/platform/drivers/renesas_usbhs/bind

Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/board-lager.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index 30ebd08..6a7041f 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -408,13 +408,30 @@ static int usbhs_hardware_init(struct platform_device *pdev)
 {
 	struct usbhs_private *priv = usbhs_get_priv(pdev);
 	struct usb_phy *phy;
+	int ret;
+
+	/* USB0 Function - use PWEN as GPIO input to detect DIP Switch SW5
+	 * setting to avoid VBUS short circuit due to wrong cable.
+	 * PWEN should be pulled up high if USB Function is selected by SW5
+	 */
+	gpio_request_one(RCAR_GP_PIN(5, 18), GPIOF_IN, NULL); /* USB0_PWEN */
+	if (!gpio_get_value(RCAR_GP_PIN(5, 18))) {
+		pr_warn("Error: USB Function not selected - check SW5 + SW6\n");
+		ret = -ENOTSUPP;
+		goto error;
+	}
 
 	phy = usb_get_phy_dev(&pdev->dev, 0);
-	if (IS_ERR(phy))
-		return PTR_ERR(phy);
+	if (IS_ERR(phy)) {
+		ret = PTR_ERR(phy);
+		goto error;
+	}
 
 	priv->phy = phy;
 	return 0;
+ error:
+	gpio_free(RCAR_GP_PIN(5, 18));
+	return ret;
 }
 
 static int usbhs_hardware_exit(struct platform_device *pdev)
@@ -426,6 +443,8 @@ static int usbhs_hardware_exit(struct platform_device *pdev)
 
 	usb_put_phy(priv->phy);
 	priv->phy = NULL;
+
+	gpio_free(RCAR_GP_PIN(5, 18));
 	return 0;
 }
 
@@ -536,7 +555,7 @@ static const struct pinctrl_map lager_pinctrl_map[] = {
 				  "vin1_clk", "vin1"),
 	/* USB0 */
 	PIN_MAP_MUX_GROUP_DEFAULT("renesas_usbhs", "pfc-r8a7790",
-				  "usb0", "usb0"),
+				  "usb0_ovc_vbus", "usb0"),
 };
 
 static void __init lager_add_standard_devices(void)
-- 
1.8.5.2

^ permalink raw reply related

* [GIT PULL 00/11] Second Round of Renesas ARM Based SoC Board Updates for v3.15
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Olof, Hi Kevin, Hi Arnd,

please consider this second round of Renesas ARM based SoC board updates
for v3.15.

This pull-request is based on the first round of such updates,
tagged as renesas-boards-for-v3.15, which I have previously sent a
pull-request for.


The following changes since commit 235cda29e4d5047622ff9b82b1f0b4cb6cf95f6c:

  ARM: shmobile: Remove Lager USBHS UDC ifdefs (2014-02-04 14:28:33 +0900)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git tags/renesas-boards2-for-v3.15

for you to fetch changes up to 03fec7dee502d43114f384a1588ce84a3c9bf38d:

  ARM: shmobile: genmai: Enable r7s72100-ether (2014-02-07 09:20:03 +0900)

----------------------------------------------------------------
Second Round of Renesas ARM Based SoC Board Updates for v3.15

* r7s72100 (RZ/A1H) based Genmai board
  - Enable SH ethernet
  - Add RSPI support

* r8a7791 (R-Car M2) based Koelsch board
  - Add QSPI support
  - Add I2C support
  - Pass SH ethernet PHY IRQ

* r8a7790 (R-Car H2) based Lager board
  - Switch QSPI to named IRQs
  - Add sound support
  - Add USB0 cable detection workaround
  - Pass SH ethernet PHY IRQ

* r8a7779 (R-Car H1) based Marzen board
  - Revert conditional selection of SMSC_PHY

* r8a7778 (R-Car M1) based Bock-W board
  - Use wp-gpios instead of WP pin

----------------------------------------------------------------
Geert Uytterhoeven (3):
      ARM: shmobile: genmai legacy: Add RSPI support
      ARM: shmobile: koelsch legacy: Add QSPI support
      ARM: shmobile: lager legacy: Switch QSPI to named IRQs

Kuninori Morimoto (2):
      ARM: shmobile: bockw: use wp-gpios instead of WP pin
      ARM: shmobile: lager: add sound support

Magnus Damm (1):
      ARM: shmobile: Lager USB0 cable detection workaround

Sergei Shtylyov (2):
      ARM: shmobile: Lager: pass Ether PHY IRQ
      ARM: shmobile: Koelsch: pass Ether PHY IRQ

Simon Horman (2):
      Revert "ARM: shmobile: marzen: Conditionally select SMSC_PHY"
      ARM: shmobile: genmai: Enable r7s72100-ether

Valentine Barshak (1):
      ARM: shmobile: koelsch: Add I2C support

 arch/arm/boot/dts/r8a7778-bockw-reference.dts |   4 +-
 arch/arm/mach-shmobile/Kconfig                |   3 +-
 arch/arm/mach-shmobile/board-genmai.c         |  64 +++++++++++++
 arch/arm/mach-shmobile/board-koelsch.c        | 114 ++++++++++++++++++++++
 arch/arm/mach-shmobile/board-lager.c          | 131 +++++++++++++++++++++++++-
 5 files changed, 309 insertions(+), 7 deletions(-)

^ permalink raw reply

* [PATCH 04/11] ARM: shmobile: bockw: use wp-gpios instead of WP pin
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

Latest Renesas Chip has some SDHI channels and the WP pin
availability depends on its channel or HW implementation.
Thus, this patch decides new policy whch indicates
WP is disabled as default.
But, we can use wp-gpios property to enable it as
other method.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/boot/dts/r8a7778-bockw-reference.dts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/r8a7778-bockw-reference.dts b/arch/arm/boot/dts/r8a7778-bockw-reference.dts
index bb62c7a..06cda19 100644
--- a/arch/arm/boot/dts/r8a7778-bockw-reference.dts
+++ b/arch/arm/boot/dts/r8a7778-bockw-reference.dts
@@ -17,6 +17,7 @@
 /dts-v1/;
 #include "r8a7778.dtsi"
 #include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
 
 / {
 	model = "bockw";
@@ -84,7 +85,7 @@
 
 	sdhi0_pins: sd0 {
 		renesas,groups = "sdhi0_data4", "sdhi0_ctrl",
-				  "sdhi0_cd", "sdhi0_wp";
+				  "sdhi0_cd";
 		renesas,function = "sdhi0";
 	};
 
@@ -101,6 +102,7 @@
 	vmmc-supply = <&fixedregulator3v3>;
 	bus-width = <4>;
 	status = "okay";
+	wp-gpios = <&gpio3 18 GPIO_ACTIVE_HIGH>;
 };
 
 &hspi0 {
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 03/11] ARM: shmobile: koelsch: Add I2C support
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Valentine Barshak <valentine.barshak@cogentembedded.com>

This adds I2C[1245] busses support to Koelsch board.
I2C[03] do not have any slave devices connected and
are not used because of the following:
* I2C0 pins are multiplexed with LBSC pins;
* I2C3 pins are multiplexed with EtherMAC and VIN0 pins.

Signed-off-by: Valentine Barshak <valentine.barshak@cogentembedded.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/board-koelsch.c | 46 ++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/arch/arm/mach-shmobile/board-koelsch.c b/arch/arm/mach-shmobile/board-koelsch.c
index 1ec3b91..2741dba 100644
--- a/arch/arm/mach-shmobile/board-koelsch.c
+++ b/arch/arm/mach-shmobile/board-koelsch.c
@@ -165,6 +165,38 @@ static const struct platform_device_info sata0_info __initconst = {
 	.dma_mask	= DMA_BIT_MASK(32),
 };
 
+/* I2C */
+static const struct resource i2c_resources[] __initconst = {
+	/* I2C0 */
+	DEFINE_RES_MEM(0xE6508000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(287)),
+	/* I2C1 */
+	DEFINE_RES_MEM(0xE6518000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(288)),
+	/* I2C2 */
+	DEFINE_RES_MEM(0xE6530000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(286)),
+	/* I2C3 */
+	DEFINE_RES_MEM(0xE6540000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(290)),
+	/* I2C4 */
+	DEFINE_RES_MEM(0xE6520000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(19)),
+	/* I2C5 */
+	DEFINE_RES_MEM(0xE6528000, 0x40),
+	DEFINE_RES_IRQ(gic_spi(20)),
+};
+
+static void __init koelsch_add_i2c(unsigned idx)
+{
+	unsigned res_idx = idx * 2;
+
+	BUG_ON(res_idx >= ARRAY_SIZE(i2c_resources));
+
+	platform_device_register_simple("i2c-rcar_gen2", idx,
+					i2c_resources + res_idx, 2);
+}
+
 static const struct pinctrl_map koelsch_pinctrl_map[] = {
 	/* DU */
 	PIN_MAP_MUX_GROUP_DEFAULT("rcar-du-r8a7791", "pfc-r8a7791",
@@ -188,6 +220,15 @@ static const struct pinctrl_map koelsch_pinctrl_map[] = {
 	/* SCIF1 (CN20: DEBUG SERIAL1) */
 	PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.7", "pfc-r8a7791",
 				  "scif1_data_d", "scif1"),
+	/* I2C1 */
+	PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.1", "pfc-r8a7791",
+				  "i2c1_e", "i2c1"),
+	/* I2C2 */
+	PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.2", "pfc-r8a7791",
+				  "i2c2", "i2c2"),
+	/* I2C4 */
+	PIN_MAP_MUX_GROUP_DEFAULT("i2c-rcar_gen2.4", "pfc-r8a7791",
+				  "i2c4_c", "i2c4"),
 };
 
 static void __init koelsch_add_standard_devices(void)
@@ -211,6 +252,11 @@ static void __init koelsch_add_standard_devices(void)
 	koelsch_add_du_device();
 
 	platform_device_register_full(&sata0_info);
+
+	koelsch_add_i2c(1);
+	koelsch_add_i2c(2);
+	koelsch_add_i2c(4);
+	koelsch_add_i2c(5);
 }
 
 /*
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 02/11] ARM: shmobile: Koelsch: pass Ether PHY IRQ
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

Pass Ether's PHY IRQ (which is IRQC's IRQ0) to the 'sh_eth' driver. Set the IRQ
trigger type to be low-level as per the Micrel PHY driver's setup.

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/board-koelsch.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/mach-shmobile/board-koelsch.c b/arch/arm/mach-shmobile/board-koelsch.c
index 2ab5c75..1ec3b91 100644
--- a/arch/arm/mach-shmobile/board-koelsch.c
+++ b/arch/arm/mach-shmobile/board-koelsch.c
@@ -23,6 +23,7 @@
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/leds.h>
 #include <linux/phy.h>
@@ -92,6 +93,7 @@ static void __init koelsch_add_du_device(void)
 /* Ether */
 static const struct sh_eth_plat_data ether_pdata __initconst = {
 	.phy			= 0x1,
+	.phy_irq		= irq_pin(0),
 	.edmac_endian		= EDMAC_LITTLE_ENDIAN,
 	.phy_interface		= PHY_INTERFACE_MODE_RMII,
 	.ether_link_active_low	= 1,
@@ -232,6 +234,8 @@ static void __init koelsch_init(void)
 {
 	koelsch_add_standard_devices();
 
+	irq_set_irq_type(irq_pin(0), IRQ_TYPE_LEVEL_LOW);
+
 	if (IS_ENABLED(CONFIG_PHYLIB))
 		phy_register_fixup_for_id("r8a7791-ether-ff:01",
 					  koelsch_ksz8041_fixup);
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH 01/11] ARM: shmobile: Lager: pass Ether PHY IRQ
From: Simon Horman @ 2014-02-13  8:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392276547.git.horms+renesas@verge.net.au>

From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

Pass Ether's PHY IRQ (which is IRQC's IRQ0) to the 'sh_eth' driver. Set the IRQ
trigger type to be low-level as per the Micrel PHY driver's setup.

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/mach-shmobile/board-lager.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index fdcc868..30ebd08 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -22,6 +22,7 @@
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/leds.h>
 #include <linux/mmc/host.h>
@@ -233,6 +234,7 @@ static const struct resource mmcif1_resources[] __initconst = {
 /* Ether */
 static const struct sh_eth_plat_data ether_pdata __initconst = {
 	.phy			= 0x1,
+	.phy_irq		= irq_pin(0),
 	.edmac_endian		= EDMAC_LITTLE_ENDIAN,
 	.phy_interface		= PHY_INTERFACE_MODE_RMII,
 	.ether_link_active_low	= 1,
@@ -618,6 +620,8 @@ static void __init lager_init(void)
 {
 	lager_add_standard_devices();
 
+	irq_set_irq_type(irq_pin(0), IRQ_TYPE_LEVEL_LOW);
+
 	if (IS_ENABLED(CONFIG_PHYLIB))
 		phy_register_fixup_for_id("r8a7790-ether-ff:01",
 					  lager_ksz8041_fixup);
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH v3 1/7] cpufreq: cpufreq-cpu0: allow use of optional boost mode frequencies
From: Thomas Abraham @ 2014-02-13  8:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52FB8C23.30003@samsung.com>

On Wed, Feb 12, 2014 at 8:28 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Thomas,
>
>
> On 07.02.2014 16:55, Thomas Abraham wrote:
>>
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> Lookup for the optional boost-frequency property in cpu0 node and if
>> available, enable support for boost mode frequencies. The frequencies
>> usable in boost mode are determined while preparing the cpufreq table
>> from the list of operating points available.
>>
>> In addition to this, enable the CPU_FREQ_BOOST_SW config option for
>> this driver by default. On platforms that do not support boost mode,
>> the boost mode frequencies will not be specified in cpu0 node and
>> hence the boost mode support will not be enabled. Since this driver
>> anyways depends on THERMAL config option, it is safe to enable
>> CPU_FREQ_BOOST_SW config option as default.
>>
>> Cc: Shawn Guo <shawn.guo@linaro.org>
>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>>   Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt |    2 ++
>>   drivers/cpufreq/Kconfig                                    |    1 +
>>   drivers/cpufreq/cpufreq-cpu0.c                             |    3 +++
>>   3 files changed, 6 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
>> b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
>> index f055515..60f321a 100644
>> --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
>> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
>> @@ -19,6 +19,8 @@ Optional properties:
>>   - cooling-min-level:
>>   - cooling-max-level:
>>        Please refer to
>> Documentation/devicetree/bindings/thermal/thermal.txt.
>> +- boost-frequency:
>> +     Please refer to
>> Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>
>>   Examples:
>>
>> diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
>> index 4b029c0..52cc704 100644
>> --- a/drivers/cpufreq/Kconfig
>> +++ b/drivers/cpufreq/Kconfig
>> @@ -187,6 +187,7 @@ config GENERIC_CPUFREQ_CPU0
>>         tristate "Generic CPU0 cpufreq driver"
>>         depends on HAVE_CLK && REGULATOR && OF && THERMAL && CPU_THERMAL
>>         select PM_OPP
>> +       select CPU_FREQ_BOOST_SW
>>         help
>>           This adds a generic cpufreq driver for CPU0 frequency
>> management.
>>           It supports both uniprocessor (UP) and symmetric multiprocessor
>> (SMP)
>> diff --git a/drivers/cpufreq/cpufreq-cpu0.c
>> b/drivers/cpufreq/cpufreq-cpu0.c
>> index 0c12ffc..06539eb 100644
>> --- a/drivers/cpufreq/cpufreq-cpu0.c
>> +++ b/drivers/cpufreq/cpufreq-cpu0.c
>> @@ -195,6 +195,9 @@ static int cpu0_cpufreq_probe(struct platform_device
>> *pdev)
>>                         transition_latency += ret * 1000;
>>         }
>>
>> +       if (of_find_property(cpu_dev->of_node, "boost-frequency", NULL))
>> +               cpu0_cpufreq_driver.boost_supported = true;
>> +
>>         ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
>>         if (ret) {
>>                 pr_err("failed register driver: %d\n", ret);
>>
>
> I'd say that boost should be enabled depending on user's preference, as done
> before in Exynos cpufreq driver. So both presence of boost-frequency
> property and state of CPU_FREQ_BOOST_SW should be considered.
>
> As for CPU_FREQ_BOOST_SW, I don't think it should be always selected, but
> ather, either converted to a user-selectable bool entry or made selectable
> by other entry, like current ARM_EXYNOS_CPU_FREQ_BOOST_SW.

We still will have the same problem on Exynos multi-platform kernel
where one Exynos platform needs it and others don't. Same with just
using the CPU_FREQ_BOOST_SW config option. So that was the reason to
just fallback on presence of boost property.

Thanks,
Thomas.


>
> Best regards,
> Tomasz

^ permalink raw reply

* [PATCH v3 2/7] clk: samsung: add infrastructure to register cpu clocks
From: Thomas Abraham @ 2014-02-13  7:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52FBBC99.8060406@samsung.com>

Hi Tomasz,

Thanks for your detailed review.

On Wed, Feb 12, 2014 at 11:55 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Thomas,
>
>
> On 07.02.2014 16:55, Thomas Abraham wrote:
>>
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> The CPU clock provider supplies the clock to the CPU clock domain. The
>> composition and organization of the CPU clock provider could vary among
>> Exynos SoCs. A CPU clock provider can be composed of clock mux, dividers
>> and gates. This patch defines a new clock type for CPU clock provider and
>> adds infrastructure to register the CPU clock providers for Samsung
>> platforms.
>>
>> In addition to this, the arm cpu clock provider for Exynos4210 and
>> compatible SoCs is instantiated using the new cpu clock type. The clock
>> configuration data for this clock is obtained from device tree. This
>> implementation is reusable for Exynos4x12 and Exynos5250 SoCs as well.
>>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>>   drivers/clk/samsung/Makefile  |    2 +-
>>   drivers/clk/samsung/clk-cpu.c |  409
>> +++++++++++++++++++++++++++++++++++++++++
>>   drivers/clk/samsung/clk.h     |    5 +
>>   3 files changed, 415 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/clk/samsung/clk-cpu.c
>
>
> [snip]
>
>
>> diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c
>> new file mode 100644
>> index 0000000..673f620
>> --- /dev/null
>> +++ b/drivers/clk/samsung/clk-cpu.c
>> @@ -0,0 +1,409 @@
>
>
> [snip]
>
>
>> +#define SRC_CPU                        0x0
>> +#define STAT_CPU               0x200
>> +#define DIV_CPU0               0x300
>> +#define DIV_CPU1               0x304
>> +#define DIV_STAT_CPU0          0x400
>> +#define DIV_STAT_CPU1          0x404
>> +
>> +#define MAX_DIV                        8
>> +
>> +#define EXYNOS4210_ARM_DIV1(base) ((readl(base + DIV_CPU0) & 0xf) + 1)
>> +#define EXYNOS4210_ARM_DIV2(base) (((readl(base + DIV_CPU0) >> 28) & 0xf)
>> + 1)
>
>
> Those are 3-bit dividers, so the mask should be rather 0x7, shouldn't it?

Yes, that is correct. I will fix this.

>
> Btw. Somehow I feel like this kind of macros is simply obfuscating code
> without any real benefits. Could you rewrite them to simply take the value
> of DIV_CPU0 register, without hiding readl behind the scenes?

Okay. I will change this.

>
>
>> +
>> +#define EXYNOS4210_DIV_CPU0(d5, d4, d3, d2, d1, d0)                    \
>> +               ((d5 << 24) | (d4 << 20) | (d3 << 16) | (d2 << 12) |    \
>> +                (d1 << 8) | (d0 <<  4))
>> +#define EXYNOS4210_DIV_CPU1(d2, d1, d0)
>> \
>> +               ((d2 << 8) | (d1 << 4) | (d0 << 0))
>> +
>> +#define EXYNOS4210_DIV1_HPM_MASK       ((0x7 << 0) | (0x7 << 4))
>> +#define EXYNOS4210_MUX_HPM_MASK                (1 << 20)
>
>
> [snip]
>
>
>> +/* common round rate callback useable for all types of cpu clocks */
>> +static long samsung_cpuclk_round_rate(struct clk_hw *hw,
>> +                       unsigned long drate, unsigned long *prate)
>> +{
>> +       struct clk *parent = __clk_get_parent(hw->clk);
>> +       unsigned long max_prate = __clk_round_rate(parent, UINT_MAX);
>> +       unsigned long t_prate, best_div = 1;
>> +       unsigned long delta, min_delta = UINT_MAX;
>> +
>> +       do {
>> +               t_prate = __clk_round_rate(parent, drate * best_div);
>> +               delta = drate - (t_prate / best_div);
>> +               if (delta < min_delta) {
>> +                       *prate = t_prate;
>> +                       min_delta = delta;
>> +               }
>> +               if (!delta)
>> +                       break;
>> +               best_div++;
>> +       } while ((drate * best_div) < max_prate && best_div <= MAX_DIV);
>> +
>> +       return t_prate / best_div;
>> +}
>
>
> I think there is something wrong with the code above. You increment best_div
> in every iteration of the loop and use it to calculate the final best
> frequency. Shouldn't best_div be the divisor which was found to give the
> least delta and so the loop should rather iterate on a different variable
> and store best_div only if (delta < min_delta) condition is true?

This function finds out the best parent frequency (APLL in this case)
which when divided with some divider value provides the the closest
possible drate. Probably, the name best_div is misleading since there
is no need to know the value of best_div outside this function.

>
> Anyway, I wonder if you couldn't somehow reuse the code from
> drivers/clk/clk-divider.c...

Yes, there are some similarities between these but they are not
entirely the same.

>
>
>> +
>> +static unsigned long _calc_div(unsigned long prate, unsigned long drate)
>> +{
>> +       unsigned long div = prate / drate;
>> +
>> +       WARN_ON(div >= MAX_DIV);
>> +       return (!(prate % drate)) ? div-- : div;
>> +}
>> +
>> +/* helper function to register a cpu clock */
>> +static int __init samsung_cpuclk_register(unsigned int lookup_id,
>
>
> Isn't the name a bit too generic? I'd say it should be
> exynos_cpuclk_register(), since the implementation is rather Exynos specific
> anyway.

The implementation of the cpu clock type is supposed to be usable on
all Samsung SoCs starting from s3c24xx onwards. I should have probably
stated that explicitly.

>
>
>> +               const char *name, const char **parents,
>> +               unsigned int num_parents, void __iomem *base,
>> +               const struct samsung_cpuclk_soc_data *soc_data,
>> +               struct device_node *np, const struct clk_ops *ops)
>> +{
>> +       struct samsung_cpuclk *cpuclk;
>> +       struct clk_init_data init;
>> +       struct clk *clk;
>> +       int ret;
>> +
>> +       cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
>> +       if (!cpuclk) {
>> +               pr_err("%s: could not allocate memory for %s clock\n",
>> +                                       __func__, name);
>> +               return -ENOMEM;
>> +       }
>> +
>> +       init.name = name;
>> +       init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT;
>
>
> Why CLK_GET_RATE_NOCACHE? There is only one entity that can change rate of
> this clock and it is this clock driver, so it can update the cache on any
> change.

Right, will remove this flag.

>
>
>> +       init.parent_names = parents;
>> +       init.num_parents = 1;
>
>
> I believe this clock should take two clocks as its parents, because it can
> dynamically switch between mout_apll and mout_mpll using mout_core mux.

Since the mout_core mux is encapsulated into the cpu clock type, the
dynamic switching is not allowed from CCF API. The switching is fully
controlled by the cpu clock type when required and hence the CCF need
not be told about 2 parents.

>
>
>> +       init.ops = ops;
>> +
>> +       cpuclk->hw.init = &init;
>> +       cpuclk->ctrl_base = base;
>> +
>> +       if (soc_data && soc_data->parser) {
>
>
> Is it even possible to instantiate this clock without soc_data and parser
> function? Shouldn't it simply bail out instead?

Yes, the intent was to allow Samsung cpu clock type to usable on
non-DT platforms as well. If a platform has soc_data, then the parser
can be called.

>
>
>> +               ret = soc_data->parser(np, &cpuclk->data);
>> +               if (ret) {
>> +                       pr_err("%s: error %d in parsing %s clock data",
>> +                                       __func__, ret, name);
>> +                       ret = -EINVAL;
>> +                       goto free_cpuclk;
>> +               }
>> +               cpuclk->offset = soc_data->offset;
>> +               init.ops = soc_data->ops;
>> +       }
>> +
>> +       if (soc_data && soc_data->clk_cb) {
>
>
> Same here. Does it make any sense to instantiate this clock without a
> notifier callback?
>
>
>> +               cpuclk->clk_nb.notifier_call = soc_data->clk_cb;
>> +               if (clk_notifier_register(__clk_lookup(parents[0]),
>> +                               &cpuclk->clk_nb)) {
>> +                       pr_err("%s: failed to register clock notifier for
>> %s\n",
>> +                                       __func__, name);
>> +                       goto free_cpuclk_data;
>> +               }
>> +       }
>> +
>> +       if (num_parents == 2) {
>
>
> When num_parents could be other than 2?

If any Samsung SoC needs no alternate parent clock when changing armclk rate.

>
>
>> +               cpuclk->alt_parent = __clk_lookup(parents[1]);
>> +               if (!cpuclk->alt_parent) {
>> +                       pr_err("%s: could not lookup alternate parent
>> %s\n",
>> +                                       __func__, parents[1]);
>> +                       ret = -EINVAL;
>> +                       goto free_cpuclk_data;
>> +               }
>> +       }
>> +
>> +       clk = clk_register(NULL, &cpuclk->hw);
>> +       if (IS_ERR(clk)) {
>> +               pr_err("%s: could not register cpuclk %s\n", __func__,
>> name);
>> +               ret = PTR_ERR(clk);
>> +               goto free_cpuclk_data;
>> +       }
>> +
>> +       samsung_clk_add_lookup(clk, lookup_id);
>> +       return 0;
>> +
>> +free_cpuclk_data:
>> +       kfree(cpuclk->data);
>> +free_cpuclk:
>> +       kfree(cpuclk);
>> +       return ret;
>> +}
>> +
>> +static inline void _exynos4210_set_armclk_div(void __iomem *base,
>> +                       unsigned long div)
>
>
> I'd say it would be better to leave the decision about inlining to the
> compiler.

Okay.

>
>
>> +{
>> +       writel((readl(base + DIV_CPU0) & ~0xf) | div, base + DIV_CPU0);
>
>
> CORE_RATIO bitfield is 3-bit wide.

Right, will fix.

>
>
>> +       while (readl(base + DIV_STAT_CPU0) != 0)
>> +               ;
>
>
> Wouldn't it be better to add some timeout and print an error to make sure
> that even if something wrong happens the user will know that it happened?

Okay, will add timeout.

>
>
>> +}
>> +
>> +static unsigned long exynos4210_armclk_recalc_rate(struct clk_hw *hw,
>> +                               unsigned long parent_rate)
>> +{
>> +       struct samsung_cpuclk *armclk = to_samsung_cpuclk_hw(hw);
>> +       void __iomem *base = armclk->ctrl_base + armclk->offset;
>> +
>> +       return parent_rate / EXYNOS4210_ARM_DIV1(base) /
>> +                               EXYNOS4210_ARM_DIV2(base);
>
>
> The code would be more readable if you read the register to a temporary
> variable using readl() directly and then accessing its contents.

Okay.

>
>
>> +}
>> +
>> +/*
>> + * This clock notifier is called when the frequency of the parent clock
>> + * of armclk is to be changed. This notifier handles the setting up all
>> + * the divider clocks, remux to temporary parent and handling the safe
>> + * frequency levels when using temporary parent.
>> + */
>> +static int exynos4210_armclk_notifier_cb(struct notifier_block *nb,
>> +                               unsigned long event, void *data)
>> +{
>> +       struct clk_notifier_data *ndata = data;
>> +       struct samsung_cpuclk *armclk = to_samsung_cpuclk_nb(nb);
>> +       struct exynos4210_armclk_data *armclk_data;
>> +       unsigned long alt_prate, alt_div, div0, div1, mux_reg;
>> +       void __iomem *base;
>> +       bool need_safe_freq;
>> +
>> +       armclk_data = armclk->data;
>> +       base = armclk->ctrl_base + armclk->offset;
>> +       alt_prate = clk_get_rate(armclk->alt_parent);
>> +
>> +       if (event == POST_RATE_CHANGE)
>> +               goto e4210_armclk_post_rate_change;
>
>
> This would look better if you separated the main configuration code from the
> notifier callback, like this:
>
>         int ret = 0;
>
>         switch (event) {
>         case POST_RATE_CHANGE:
>                 ret = exynos4210_armclk_post_rate_change(...);
>                 break;
>         case PRE_RATE_CHANGE:
>                 ret = exynos4210_armclk_pre_rate_change(...);
>                 break;
>         }
>
>         return notifier_from_errno(ret);

Okay.

>
> By the way, I wonder if some of this couldn't simply happen in .set_rate()
> op of this clock, since
>
>
>> +
>> +       /* pre-rate change. find out the divider values to use for clock
>> data */
>> +       while (armclk_data->prate != ndata->new_rate) {
>> +               if (armclk_data->prate == 0)
>> +                       return NOTIFY_BAD;
>> +               armclk_data++;
>> +       }
>> +
>> +       div0 = armclk_data->div0;
>> +       div1 = armclk_data->div1;
>
>
> You should always use read-modify-write for such registers to preserve
> reserved bits. This will also clean a bit safe frequency activation, see
> below.
>
>
>> +       if (readl(base + SRC_CPU) & EXYNOS4210_MUX_HPM_MASK) {
>> +               div1 = readl(base + DIV_CPU1) & EXYNOS4210_DIV1_HPM_MASK;
>> +               div1 |= ((armclk_data->div1) & ~EXYNOS4210_DIV1_HPM_MASK);
>> +       }
>> +
>> +       /*
>> +        * if the new and old parent clock speed is less than the clock
>> speed
>> +        * of the alternate parent, then it should be ensured that at no
>> point
>> +        * the armclk speed is more than the old_prate until the dividers
>> are
>> +        * set.
>> +        */
>> +       need_safe_freq = ndata->old_rate < alt_prate &&
>> +                               ndata->new_rate < alt_prate;
>
>
> Are you sure this condition is correct? The parent clock (PLL) alone doesn't
> fully determine the rate of ARM clock, because you are also changing
> div_core. So you can end up with PLL going down, while ARM clock going up,
> because the divisors are significantly lowered.

Interesting! I did not think about this. Will fix it.

>
> I think you should compare ARM clock rates here OR just remove div_core
> configuration, keeping it as 0 (divide by 1) all the time, except when safe
> frequency is active.

I would like to keep have div_core configuration, since it gives more
options for armclk, not just limited to PLL speeds.

>
>
>> +       if (need_safe_freq) {
>> +               alt_div = _calc_div(alt_prate, ndata->old_rate);
>> +               _exynos4210_set_armclk_div(base, alt_div);
>> +               div0 |= alt_div;
>> +       }
>
>
>
> You could move div0 and div1 calculation (including reading original values
> of registers) here to remove "div0 |= alt_div;" line from the if above and
> fully isolate ARM divisor setup code from changing other divisors.
>
>
>> +
>> +       mux_reg = readl(base + SRC_CPU);
>> +       writel(mux_reg | (1 << 16), base + SRC_CPU);
>
>
> Don't you need to hold the samsung clock spinlock when writing to this
> register? It seems to control more muxes than just this one.

Right, will fix it.

>
>
>> +       while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
>> +               ;
>> +
>> +       writel(div0, base + DIV_CPU0);
>> +       while (readl(base + DIV_STAT_CPU0) != 0)
>> +               ;
>> +       writel(div1, base + DIV_CPU1);
>> +       while (readl(base + DIV_STAT_CPU1) != 0)
>> +               ;
>
>
> You seem to always perform the configuration in pre rate change notifier,
> but original cpufreq code used to do it depending on whether the new
> frequency is less than old or not, e.g.

Yes, but with the clock notifier callback, we need not do it like below.

>
> static void exynos5250_set_frequency(unsigned int old_index,
>                                   unsigned int new_index)
> {
>         if (old_index > new_index) {
>                 set_clkdiv(new_index);
>                 set_apll(new_index);
>         } else if (old_index < new_index) {
>                 set_apll(new_index);
>                 set_clkdiv(new_index);
>         }
> }
>
> set_clkdiv does the following:
>   1) set DIV_CPU0
>   2) wait for DIV_STAT_CPU0
>   3) set DIV_CPU1
>   4) wait for DIV_STAT_CPU1
>
> and set_apll:
>   1) set parent to MPLL
>   2) wait for MUX_STAT_CPU
>   3) set APLL rate
>   4) set parent to APLL
>   5) wait for MUX_STAT_CPU
>
> Note that higher index means lower frequency.
>
> Btw. It would be nice to handle timeouts here as well, to prevent system
> lockups.

Okay.

>
>
>> +       return NOTIFY_OK;
>> +
>> +e4210_armclk_post_rate_change:
>> +       /* post-rate change event, re-mux back to primary parent */
>> +       mux_reg = readl(base + SRC_CPU);
>> +       writel(mux_reg & ~(1 << 16), base + SRC_CPU);
>> +       while (((readl(base + STAT_CPU) >> 16) & 0x7) != 1)
>> +                       ;
>
>
> nit: Too many tabs.
>
> Timeout would be nice too.
>
>
>> +
>> +       return NOTIFY_OK;
>> +}
>> +
>> +static int exynos4210_armclk_set_rate(struct clk_hw *hw, unsigned long
>> drate,
>> +                                       unsigned long prate)
>> +{
>> +       struct samsung_cpuclk *armclk = to_samsung_cpuclk_hw(hw);
>> +       void __iomem *base = armclk->ctrl_base + armclk->offset;
>> +       unsigned long div;
>> +
>> +       div = drate < prate ? _calc_div(prate, drate) : 0;
>> +       _exynos4210_set_armclk_div(base, div);
>> +       return 0;
>
>
> Hmm, here you are supposed to set exactly the rate given to you in drate
> argument. Core clock code calls your .round_rate() first and the rate
> returned by it is what .set_rate() gets as drate. You can safely assume that
>
>
>> +}
>> +
>> +static const struct clk_ops exynos4210_armclk_clk_ops = {
>> +       .recalc_rate = exynos4210_armclk_recalc_rate,
>> +       .round_rate = samsung_cpuclk_round_rate,
>> +       .set_rate = exynos4210_armclk_set_rate,
>> +};
>> +
>> +/*
>> + * parse divider configuration data from dt for all the cpu clock domain
>> + * clocks in exynos4210 and compatible SoC's.
>> + */
>> +static int __init exynos4210_armclk_parser(struct device_node *np, void
>> **data)
>> +{
>> +       struct exynos4210_armclk_data *tdata;
>> +       unsigned long cfg[10], row, col;
>> +       const struct property *prop;
>> +       const __be32 *val;
>> +       u32 cells;
>> +       int ret;
>> +
>> +       if (of_property_read_u32(np, "samsung,armclk-cells", &cells))
>> +               return -EINVAL;
>
>
> The property should be prefixed with "#" as other properties defining number
> of cells.

Isn't # a unnecessary requirement here. The old dt bindings used to
have it and I am not sure in what way it helps. And this is a samsung
specific property. Anyways, I do not have any particular preference on
this.

>
> Also you should check if cells value is correct, e.g. a number of cells
> supported

Okay.

>
>
>> +       prop = of_find_property(np, "samsung,armclk-divider-table", NULL);
>> +       if (!prop)
>> +               return -EINVAL;
>> +       if (!prop->value)
>> +               return -EINVAL;
>> +       if ((prop->length / sizeof(u32)) % cells)
>> +               return -EINVAL;
>> +       row = ((prop->length / sizeof(u32)) / cells) + 1;
>
>
> Why + 1? Also the variable could be named in a bit more meaningful way, e.g.
> num_rows.

+1 since this is a zero terminated table. Will change the variable name.

>
>
>> +
>> +       *data = kzalloc(sizeof(*tdata) * row, GFP_KERNEL);
>> +       if (!*data)
>> +               ret = -ENOMEM;
>> +       tdata = *data;
>> +
>> +       val = prop->value;
>> +       for (; row > 1; row--, tdata++) {
>> +               for (col = 0; col < cells; col++)
>> +                       cfg[col] = be32_to_cpup(val++);
>
>
> You could use of_prop_next_u32() here.

Okay.

>
>
>> +
>> +               tdata->prate = cfg[0] * 1000;
>> +               tdata->div0 = EXYNOS4210_DIV_CPU0(cfg[6], cfg[5], cfg[4],
>> +                                               cfg[3], cfg[2], cfg[1]);
>> +               tdata->div1 = cells == 10 ?
>> +                               EXYNOS4210_DIV_CPU1(cfg[9], cfg[8],
>> cfg[7]) :
>> +                               EXYNOS4210_DIV_CPU1(0, cfg[8], cfg[7]);
>> +       }
>> +       tdata->prate = 0;
>> +       return 0;
>> +}
>> +
>> +static const struct samsung_cpuclk_soc_data exynos4210_cpuclk_soc_data =
>> {
>> +       .parser = exynos4210_armclk_parser,
>> +       .ops = &exynos4210_armclk_clk_ops,
>> +       .offset = 0x14200,
>> +       .clk_cb = exynos4210_armclk_notifier_cb,
>> +};
>> +
>> +static const struct samsung_cpuclk_soc_data exynos5250_cpuclk_soc_data =
>> {
>> +       .parser = exynos4210_armclk_parser,
>> +       .ops = &exynos4210_armclk_clk_ops,
>> +       .offset = 0x200,
>> +       .clk_cb = exynos4210_armclk_notifier_cb,
>> +};
>> +
>> +static const struct of_device_id samsung_clock_ids_armclk[] = {
>> +       { .compatible = "samsung,exynos4210-clock",
>> +                       .data = &exynos4210_cpuclk_soc_data, },
>> +       { .compatible = "samsung,exynos4412-clock",
>> +                       .data = &exynos4210_cpuclk_soc_data, },
>> +       { .compatible = "samsung,exynos5250-clock",
>> +                       .data = &exynos5250_cpuclk_soc_data, },
>> +       { },
>> +};
>> +
>> +/**
>> + * samsung_register_arm_clock: register arm clock with ccf.
>> + * @lookup_id: armclk clock output id for the clock controller.
>> + * @parent: name of the parent clock for armclk.
>> + * @base: base address of the clock controller from which armclk is
>> generated.
>> + * @np: device tree node pointer of the clock controller (optional).
>> + * @ops: clock ops for this clock (optional)
>> + */
>> +int __init samsung_register_arm_clock(unsigned int lookup_id,
>> +               const char **parent_names, unsigned int num_parents,
>> +               void __iomem *base, struct device_node *np, struct clk_ops
>> *ops)
>> +{
>> +       const struct of_device_id *match;
>> +       const struct samsung_cpuclk_soc_data *data = NULL;
>> +
>> +       if (np) {
>> +               match = of_match_node(samsung_clock_ids_armclk, np);
>> +               data = match ? match->data : NULL;
>> +       }
>
>
> Since this is rather Exynos-specific and Exynos is DT-only, np being NULL
> would be simply an error.

The cpu clock type is usable for not-dt samsung platform also.

>
> Best regards,
> Tomasz

Thanks for your review.

Regards,
Thomas.

^ permalink raw reply

* [linux-sunxi] [PATCH v5 4/8] ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
From: Chen-Yu Tsai @ 2014-02-13  7:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140211193358.4568.45603.stgit@dizzy-6.o2s.ch>

Hi,

On Wed, Feb 12, 2014 at 3:33 AM, David Lanzend?rfer
<david.lanzendoerfer@o2s.ch> wrote:
> This is based on the driver Allwinner ships in their Android kernel sources.
>
> Initial porting to upstream kernels done by David Lanzend?rfer, additional
> fixes and cleanups by Hans de Goede.
>
> It uses dma in bus-master mode using a built-in designware idmac controller,
> which is identical to the one found in the mmc-dw hosts.
> The rest of the host is not identical to mmc-dw.
>
> Signed-off-by: David Lanzend?rfer <david.lanzendoerfer@o2s.ch>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/mmc/host/Kconfig     |    7
>  drivers/mmc/host/Makefile    |    2
>  drivers/mmc/host/sunxi-mmc.c |  872 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/sunxi-mmc.h |  239 ++++++++++++
>  4 files changed, 1120 insertions(+)
>  create mode 100644 drivers/mmc/host/sunxi-mmc.c
>  create mode 100644 drivers/mmc/host/sunxi-mmc.h
>
[..]
> diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
> new file mode 100644
> index 0000000..5fc4634
> --- /dev/null
> +++ b/drivers/mmc/host/sunxi-mmc.c
> @@ -0,0 +1,872 @@
> +/*
> + * Driver for sunxi SD/MMC host controllers
> + * (C) Copyright 2014-2015 Reuuimlla Technology Co., Ltd.
> + * (C) Copyright 2014-2015 Aaron Maoye <leafy.myeh@reuuimllatech.com>
> + * (C) Copyright 2014-2015 O2S GmbH <www.o2s.ch>
> + * (C) Copyright 2014-2015 David Lanzend?rfer <david.lanzendoerfer@o2s.ch>
> + * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +
> +#include <linux/clk.h>
> +#include <linux/clk-private.h>
> +#include <linux/clk/sunxi.h>
> +
> +#include <linux/gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/scatterlist.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <linux/of_address.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_platform.h>
> +
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/core.h>
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/slot-gpio.h>
> +
> +#include "sunxi-mmc.h"
> +
> +static int sunxi_mmc_init_host(struct mmc_host *mmc)
> +{
> +       u32 rval;
> +       struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
> +       int ret;
> +
> +       ret =  clk_prepare_enable(smc_host->clk_ahb);
> +       if (ret) {
> +               dev_err(mmc_dev(smc_host->mmc), "AHB clk err %d\n", ret);
> +               return ret;
> +       }
> +       ret =  clk_prepare_enable(smc_host->clk_mod);
> +       if (ret) {
> +               dev_err(mmc_dev(smc_host->mmc), "MOD clk err %d\n", ret);
> +               clk_disable_unprepare(smc_host->clk_ahb);
> +               return ret;
> +       }
> +
> +       /* reset controller */
> +       rval = mci_readl(smc_host, REG_GCTRL) | SDXC_HARDWARE_RESET;
> +       mci_writel(smc_host, REG_GCTRL, rval);
> +
> +       mci_writel(smc_host, REG_FTRGL, 0x20070008);
> +       mci_writel(smc_host, REG_TMOUT, 0xffffffff);
> +       mci_writel(smc_host, REG_IMASK, smc_host->sdio_imask);
> +       mci_writel(smc_host, REG_RINTR, 0xffffffff);
> +       mci_writel(smc_host, REG_DBGC, 0xdeb);
> +       mci_writel(smc_host, REG_FUNS, 0xceaa0000);
> +       mci_writel(smc_host, REG_DLBA, smc_host->sg_dma);
> +       rval = mci_readl(smc_host, REG_GCTRL)|SDXC_INTERRUPT_ENABLE_BIT;
> +       rval &= ~SDXC_ACCESS_DONE_DIRECT;
> +       mci_writel(smc_host, REG_GCTRL, rval);
> +
> +       return 0;
> +}
> +
> +static void sunxi_mmc_exit_host(struct sunxi_mmc_host *smc_host)
> +{
> +       mci_writel(smc_host, REG_GCTRL, SDXC_HARDWARE_RESET);
> +       clk_disable_unprepare(smc_host->clk_ahb);
> +       clk_disable_unprepare(smc_host->clk_mod);
> +}
> +
> +/* /\* UHS-I Operation Modes */
> +/*  * DS               25MHz   12.5MB/s        3.3V */
> +/*  * HS               50MHz   25MB/s          3.3V */
> +/*  * SDR12    25MHz   12.5MB/s        1.8V */
> +/*  * SDR25    50MHz   25MB/s          1.8V */
> +/*  * SDR50    100MHz  50MB/s          1.8V */
> +/*  * SDR104   208MHz  104MB/s         1.8V */
> +/*  * DDR50    50MHz   50MB/s          1.8V */
> +/*  * MMC Operation Modes */
> +/*  * DS               26MHz   26MB/s          3/1.8/1.2V */
> +/*  * HS               52MHz   52MB/s          3/1.8/1.2V */
> +/*  * HSDDR    52MHz   104MB/s         3/1.8/1.2V */
> +/*  * HS200    200MHz  200MB/s         1.8/1.2V */
> +/*  * */
> +/*  * Spec. Timing */
> +/*  * SD3.0 */
> +/*  * Fcclk    Tcclk   Fsclk   Tsclk   Tis     Tih     odly  RTis     RTih */
> +/*  * 400K     2.5us   24M     41ns    5ns     5ns     1     2209ns   41ns */
> +/*  * 25M      40ns    600M    1.67ns  5ns     5ns     3     14.99ns  5.01ns */
> +/*  * 50M      20ns    600M    1.67ns  6ns     2ns     3     14.99ns  5.01ns */
> +/*  * 50MDDR   20ns    600M    1.67ns  6ns     0.8ns   2     6.67ns   3.33ns */
> +/*  * 104M     9.6ns   600M    1.67ns  3ns     0.8ns   1     7.93ns   1.67ns */
> +/*  * 208M     4.8ns   600M    1.67ns  1.4ns   0.8ns   1     3.33ns   1.67ns */
> +
> +/*  * 25M      40ns    300M    3.33ns  5ns     5ns     2     13.34ns   6.66ns */
> +/*  * 50M      20ns    300M    3.33ns  6ns     2ns     2     13.34ns   6.66ns */
> +/*  * 50MDDR   20ns    300M    3.33ns  6ns     0.8ns   1     6.67ns    3.33ns */
> +/*  * 104M     9.6ns   300M    3.33ns  3ns     0.8ns   0     7.93ns    1.67ns */
> +/*  * 208M     4.8ns   300M    3.33ns  1.4ns   0.8ns   0     3.13ns    1.67ns */
> +
> +/*  * eMMC4.5 */
> +/*  * 400K     2.5us   24M     41ns    3ns     3ns     1     2209ns    41ns */
> +/*  * 25M      40ns    600M    1.67ns  3ns     3ns     3     14.99ns   5.01ns */
> +/*  * 50M      20ns    600M    1.67ns  3ns     3ns     3     14.99ns   5.01ns */
> +/*  * 50MDDR   20ns    600M    1.67ns  2.5ns   2.5ns   2     6.67ns    3.33ns */
> +/*  * 200M     5ns     600M    1.67ns  1.4ns   0.8ns   1     3.33ns    1.67ns */
> +/*  *\/ */
> +
> +static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
> +                                   struct mmc_data *data)
> +{
> +       struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
> +       struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
> +       int i, max_len = (1 << host->idma_des_size_bits);
> +
> +       for (i = 0; i < data->sg_len; i++) {
> +               pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN |
> +                                SDXC_IDMAC_DES0_DIC;
> +
> +               if (data->sg[i].length == max_len)
> +                       pdes[i].buf_size = 0; /* 0 == max_len */
> +               else
> +                       pdes[i].buf_size = data->sg[i].length;
> +
> +               pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
> +               pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
> +       }
> +
> +       pdes[0].config |= SDXC_IDMAC_DES0_FD;
> +       pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD;
> +
> +       wmb(); /* Ensure idma_des hit main mem before we start the idmac */
> +}
> +
> +static enum dma_data_direction sunxi_mmc_get_dma_dir(struct mmc_data *data)
> +{
> +       if (data->flags & MMC_DATA_WRITE)
> +               return DMA_TO_DEVICE;
> +       else
> +               return DMA_FROM_DEVICE;
> +}
> +
> +static int sunxi_mmc_prepare_dma(struct sunxi_mmc_host *smc_host,
> +                                struct mmc_data *data)
> +{
> +       u32 dma_len;
> +       u32 i;
> +       u32 temp;
> +       struct scatterlist *sg;
> +
> +       dma_len = dma_map_sg(mmc_dev(smc_host->mmc), data->sg, data->sg_len,
> +                            sunxi_mmc_get_dma_dir(data));
> +       if (dma_len == 0) {
> +               dev_err(mmc_dev(smc_host->mmc), "dma_map_sg failed\n");
> +               return -ENOMEM;
> +       }
> +
> +       for_each_sg(data->sg, sg, data->sg_len, i) {
> +               if (sg->offset & 3 || sg->length & 3) {
> +                       dev_err(mmc_dev(smc_host->mmc),
> +                               "unaligned scatterlist: os %x length %d\n",
> +                               sg->offset, sg->length);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       sunxi_mmc_init_idma_des(smc_host, data);
> +
> +       temp = mci_readl(smc_host, REG_GCTRL);
> +       temp |= SDXC_DMA_ENABLE_BIT;
> +       mci_writel(smc_host, REG_GCTRL, temp);
> +       temp |= SDXC_DMA_RESET;
> +       mci_writel(smc_host, REG_GCTRL, temp);
> +       mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_SOFT_RESET);
> +
> +       if (!(data->flags & MMC_DATA_WRITE))
> +               mci_writel(smc_host, REG_IDIE, SDXC_IDMAC_RECEIVE_INTERRUPT);
> +
> +       mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_FIX_BURST | SDXC_IDMAC_IDMA_ON);
> +
> +       return 0;
> +}
> +
> +static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host,
> +                                      struct mmc_request *req)
> +{
> +       u32 cmd_val = SDXC_START | SDXC_RESPONSE_EXPIRE | SDXC_STOP_ABORT_CMD
> +                       | SDXC_CHECK_RESPONSE_CRC | MMC_STOP_TRANSMISSION;
> +       u32 ri = 0;
> +       unsigned long expire = jiffies + msecs_to_jiffies(1000);
> +
> +       mci_writel(host, REG_CARG, 0);
> +       mci_writel(host, REG_CMDR, cmd_val);
> +
> +       do {
> +               ri = mci_readl(host, REG_RINTR);
> +       } while (!(ri & (SDXC_COMMAND_DONE | SDXC_INTERRUPT_ERROR_BIT)) &&
> +                time_before(jiffies, expire));
> +
> +       if (ri & SDXC_INTERRUPT_ERROR_BIT) {
> +               dev_err(mmc_dev(host->mmc), "send stop command failed\n");
> +               if (req->stop)
> +                       req->stop->resp[0] = -ETIMEDOUT;
> +       } else {
> +               if (req->stop)
> +                       req->stop->resp[0] = mci_readl(host, REG_RESP0);
> +       }
> +
> +       mci_writel(host, REG_RINTR, 0xffff);
> +}
> +
> +static void sunxi_mmc_dump_errinfo(struct sunxi_mmc_host *smc_host)
> +{
> +       struct mmc_command *cmd = smc_host->mrq->cmd;
> +       struct mmc_data *data = smc_host->mrq->data;
> +
> +       /* For some cmds timeout is normal with sd/mmc cards */
> +       if ((smc_host->int_sum & SDXC_INTERRUPT_ERROR_BIT) == SDXC_RESPONSE_TIMEOUT &&
> +                       (cmd->opcode == SD_IO_SEND_OP_COND || cmd->opcode == SD_IO_RW_DIRECT))
> +               return;
> +
> +       dev_err(mmc_dev(smc_host->mmc),
> +               "smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n",
> +               smc_host->mmc->index, cmd->opcode,
> +               data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "",
> +               smc_host->int_sum & SDXC_RESPONSE_ERROR     ? " RE"     : "",
> +               smc_host->int_sum & SDXC_RESPONSE_CRC_ERROR  ? " RCE"    : "",
> +               smc_host->int_sum & SDXC_DATA_CRC_ERROR  ? " DCE"    : "",
> +               smc_host->int_sum & SDXC_RESPONSE_TIMEOUT ? " RTO"    : "",
> +               smc_host->int_sum & SDXC_DATA_TIMEOUT ? " DTO"    : "",
> +               smc_host->int_sum & SDXC_FIFO_RUN_ERROR  ? " FE"     : "",
> +               smc_host->int_sum & SDXC_HARD_WARE_LOCKED ? " HL"     : "",
> +               smc_host->int_sum & SDXC_START_BIT_ERROR ? " SBE"    : "",
> +               smc_host->int_sum & SDXC_END_BIT_ERROR   ? " EBE"    : ""
> +               );
> +}
> +
> +static void sunxi_mmc_finalize_request(struct sunxi_mmc_host *host)
> +{
> +       struct mmc_request *mrq;
> +       unsigned long iflags;
> +
> +       spin_lock_irqsave(&host->lock, iflags);
> +
> +       mrq = host->mrq;
> +       if (!mrq) {
> +               spin_unlock_irqrestore(&host->lock, iflags);
> +               dev_err(mmc_dev(host->mmc), "no request to finalize\n");
> +               return;
> +       }
> +
> +       if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) {
> +               sunxi_mmc_dump_errinfo(host);
> +               mrq->cmd->error = -ETIMEDOUT;
> +               if (mrq->data)
> +                       mrq->data->error = -ETIMEDOUT;
> +               if (mrq->stop)
> +                       mrq->stop->error = -ETIMEDOUT;
> +       } else {
> +               if (mrq->cmd->flags & MMC_RSP_136) {
> +                       mrq->cmd->resp[0] = mci_readl(host, REG_RESP3);
> +                       mrq->cmd->resp[1] = mci_readl(host, REG_RESP2);
> +                       mrq->cmd->resp[2] = mci_readl(host, REG_RESP1);
> +                       mrq->cmd->resp[3] = mci_readl(host, REG_RESP0);
> +               } else {
> +                       mrq->cmd->resp[0] = mci_readl(host, REG_RESP0);
> +               }
> +               if (mrq->data)
> +                       mrq->data->bytes_xfered =
> +                               mrq->data->blocks * mrq->data->blksz;
> +       }
> +
> +       if (mrq->data) {
> +               struct mmc_data *data = mrq->data;
> +               u32 temp;
> +
> +               mci_writel(host, REG_IDST, 0x337);
> +               mci_writel(host, REG_DMAC, 0);
> +               temp = mci_readl(host, REG_GCTRL);
> +               mci_writel(host, REG_GCTRL, temp|SDXC_DMA_RESET);
> +               temp &= ~SDXC_DMA_ENABLE_BIT;
> +               mci_writel(host, REG_GCTRL, temp);
> +               temp |= SDXC_FIFO_RESET;
> +               mci_writel(host, REG_GCTRL, temp);
> +               dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
> +                                    sunxi_mmc_get_dma_dir(data));
> +       }
> +
> +       mci_writel(host, REG_RINTR, 0xffff);
> +
> +       dev_dbg(mmc_dev(host->mmc), "req done, resp %08x %08x %08x %08x\n",
> +               mrq->cmd->resp[0], mrq->cmd->resp[1],
> +               mrq->cmd->resp[2], mrq->cmd->resp[3]);
> +
> +       host->mrq = NULL;
> +       host->int_sum = 0;
> +       host->wait_dma = 0;
> +
> +       spin_unlock_irqrestore(&host->lock, iflags);
> +
> +       if (mrq->data && mrq->data->error) {
> +               dev_err(mmc_dev(host->mmc),
> +                       "data error, sending stop command\n");
> +               sunxi_mmc_send_manual_stop(host, mrq);
> +       }
> +
> +       mmc_request_done(host->mmc, mrq);
> +}
> +
> +static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id)
> +{
> +       struct sunxi_mmc_host *host = dev_id;
> +       u32 finalize = 0;
> +       u32 sdio_int = 0;
> +       u32 msk_int;
> +       u32 idma_int;
> +
> +       spin_lock(&host->lock);
> +
> +       idma_int  = mci_readl(host, REG_IDST);
> +       msk_int   = mci_readl(host, REG_MISTA);
> +
> +       dev_dbg(mmc_dev(host->mmc), "irq: rq %p mi %08x idi %08x\n",
> +               host->mrq, msk_int, idma_int);
> +
> +       if (host->mrq) {
> +               if (idma_int & SDXC_IDMAC_RECEIVE_INTERRUPT)
> +                       host->wait_dma = 0;
> +
> +               host->int_sum |= msk_int;
> +
> +               /* Wait for COMMAND_DONE on RESPONSE_TIMEOUT before finishing the req */
> +               if ((host->int_sum & SDXC_RESPONSE_TIMEOUT) &&
> +                               !(host->int_sum & SDXC_COMMAND_DONE))
> +                       mci_writel(host, REG_IMASK,
> +                                  host->sdio_imask | SDXC_COMMAND_DONE);
> +               else if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT)
> +                       finalize = 1; /* Don't wait for dma on error */
> +               else if (host->int_sum & SDXC_INTERRUPT_DONE_BIT && !host->wait_dma)
> +                       finalize = 1; /* Done */
> +
> +               if (finalize) {
> +                       mci_writel(host, REG_IMASK, host->sdio_imask);
> +                       mci_writel(host, REG_IDIE, 0);
> +               }
> +       }
> +
> +       if (msk_int & SDXC_SDIO_INTERRUPT)
> +               sdio_int = 1;
> +
> +       mci_writel(host, REG_RINTR, msk_int);
> +       mci_writel(host, REG_IDST, idma_int);
> +
> +       spin_unlock(&host->lock);
> +
> +       if (finalize)
> +               tasklet_schedule(&host->tasklet);
> +
> +       if (sdio_int)
> +               mmc_signal_sdio_irq(host->mmc);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void sunxi_mmc_tasklet(unsigned long data)
> +{
> +       struct sunxi_mmc_host *smc_host = (struct sunxi_mmc_host *) data;
> +       sunxi_mmc_finalize_request(smc_host);
> +}
> +
> +static void sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
> +{
> +       unsigned long expire = jiffies + msecs_to_jiffies(2000);
> +       u32 rval;
> +
> +       rval = mci_readl(host, REG_CLKCR);
> +       rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON);
> +
> +       if (oclk_en)
> +               rval |= SDXC_CARD_CLOCK_ON;
> +
> +       if (!host->io_flag)
> +               rval |= SDXC_LOW_POWER_ON;
> +
> +       mci_writel(host, REG_CLKCR, rval);
> +
> +       rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER;
> +       if (host->voltage_switching)
> +               rval |= SDXC_VOLTAGE_SWITCH;
> +       mci_writel(host, REG_CMDR, rval);
> +
> +       do {
> +               rval = mci_readl(host, REG_CMDR);
> +       } while (time_before(jiffies, expire) && (rval & SDXC_START));
> +
> +       if (rval & SDXC_START) {
> +               dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n");
> +               host->ferror = 1;
> +       }
> +}
> +
> +static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *smc_host,
> +                                 u32 oclk_dly, u32 sclk_dly)
> +{
> +       unsigned long iflags;
> +       struct clk_hw *hw = __clk_get_hw(smc_host->clk_mod);
> +
> +       spin_lock_irqsave(&smc_host->lock, iflags);
> +       clk_sunxi_mmc_phase_control(hw, sclk_dly, oclk_dly);
> +       spin_unlock_irqrestore(&smc_host->lock, iflags);
> +}
> +
> +struct sunxi_mmc_clk_dly mmc_clk_dly[MMC_CLK_MOD_NUM] = {
> +       { MMC_CLK_400K, 0, 7 },
> +       { MMC_CLK_25M, 0, 5 },
> +       { MMC_CLK_50M, 3, 5 },
> +       { MMC_CLK_50MDDR, 2, 4 },
> +       { MMC_CLK_50MDDR_8BIT, 2, 4 },
> +       { MMC_CLK_100M, 1, 4 },
> +       { MMC_CLK_200M, 1, 4 },
> +};
> +
> +static void sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *smc_host,
> +                                  unsigned int rate)
> +{
> +       u32 newrate;
> +       u32 src_clk;
> +       u32 oclk_dly;
> +       u32 sclk_dly;
> +       u32 temp;
> +       struct sunxi_mmc_clk_dly *dly = NULL;
> +
> +       newrate = clk_round_rate(smc_host->clk_mod, rate);
> +       if (smc_host->clk_mod_rate == newrate) {
> +               dev_dbg(mmc_dev(smc_host->mmc), "clk already %d, rounded %d\n",
> +                       rate, newrate);
> +               return;
> +       }
> +
> +       dev_dbg(mmc_dev(smc_host->mmc), "setting clk to %d, rounded %d\n",
> +               rate, newrate);
> +
> +       /* setting clock rate */
> +       clk_disable(smc_host->clk_mod);
> +       clk_set_rate(smc_host->clk_mod, newrate);
> +       clk_enable(smc_host->clk_mod);
> +       smc_host->clk_mod_rate = newrate = clk_get_rate(smc_host->clk_mod);
> +       dev_dbg(mmc_dev(smc_host->mmc), "clk is now %d\n", newrate);
> +
> +       sunxi_mmc_oclk_onoff(smc_host, 0);
> +       /* clear internal divider */
> +       temp = mci_readl(smc_host, REG_CLKCR);
> +       temp &= ~0xff;
> +       mci_writel(smc_host, REG_CLKCR, temp);
> +
> +       /* determine delays */
> +       if (rate <= 400000) {
> +               dly = &mmc_clk_dly[MMC_CLK_400K];
> +       } else if (rate <= 25000000) {
> +               dly = &mmc_clk_dly[MMC_CLK_25M];
> +       } else if (rate <= 50000000) {
> +               if (smc_host->ddr) {
> +                       if (smc_host->bus_width == 8)
> +                               dly = &mmc_clk_dly[MMC_CLK_50MDDR_8BIT];
> +                       else
> +                               dly = &mmc_clk_dly[MMC_CLK_50MDDR];
> +               } else {
> +                       dly = &mmc_clk_dly[MMC_CLK_50M];
> +               }
> +       } else if (rate <= 104000000) {
> +               dly = &mmc_clk_dly[MMC_CLK_100M];
> +       } else if (rate <= 208000000) {
> +               dly = &mmc_clk_dly[MMC_CLK_200M];
> +       } else {
> +               dly = &mmc_clk_dly[MMC_CLK_50M];
> +       }
> +
> +       oclk_dly = dly->oclk_dly;
> +       sclk_dly = dly->sclk_dly;
> +
> +       src_clk = clk_get_rate(clk_get_parent(smc_host->clk_mod));
> +
> +       if (src_clk >= 300000000 && src_clk <= 400000000) {
> +               if (oclk_dly)
> +                       oclk_dly--;
> +               if (sclk_dly)
> +                       sclk_dly--;
> +       }
> +
> +       sunxi_mmc_set_clk_dly(smc_host, oclk_dly, sclk_dly);
> +       sunxi_mmc_oclk_onoff(smc_host, 1);
> +
> +       /* oclk_onoff sets various irq status bits, clear these */
> +       mci_writel(smc_host, REG_RINTR,
> +                  mci_readl(smc_host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT);
> +}
> +
> +static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +       struct sunxi_mmc_host *host = mmc_priv(mmc);
> +       u32 temp;
> +       s32 err;
> +
> +       /* Set the power state */
> +       switch (ios->power_mode) {
> +       case MMC_POWER_ON:
> +               break;
> +
> +       case MMC_POWER_UP:
> +               if (!IS_ERR(host->vmmc)) {
> +                       mmc_regulator_set_ocr(host->mmc, host->vmmc, ios->vdd);
> +                       udelay(200);
> +               }
> +
> +               err = sunxi_mmc_init_host(mmc);
> +               if (err) {
> +                       host->ferror = 1;
> +                       return;
> +               }
> +               enable_irq(host->irq);
> +
> +               dev_dbg(mmc_dev(host->mmc), "power on!\n");
> +               host->ferror = 0;
> +               break;
> +
> +       case MMC_POWER_OFF:
> +               dev_dbg(mmc_dev(host->mmc), "power off!\n");
> +               disable_irq(host->irq);
> +               sunxi_mmc_exit_host(host);
> +               if (!IS_ERR(host->vmmc))
> +                       mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
> +               host->ferror = 0;
> +               break;
> +       }
> +
> +       /* set bus width */
> +       switch (ios->bus_width) {
> +       case MMC_BUS_WIDTH_1:
> +               mci_writel(host, REG_WIDTH, SDXC_WIDTH1);
> +               host->bus_width = 1;
> +               break;
> +       case MMC_BUS_WIDTH_4:
> +               mci_writel(host, REG_WIDTH, SDXC_WIDTH4);
> +               host->bus_width = 4;
> +               break;
> +       case MMC_BUS_WIDTH_8:
> +               mci_writel(host, REG_WIDTH, SDXC_WIDTH8);
> +               host->bus_width = 8;
> +               break;
> +       }
> +
> +       /* set ddr mode */
> +       temp = mci_readl(host, REG_GCTRL);
> +       if (ios->timing == MMC_TIMING_UHS_DDR50) {
> +               temp |= SDXC_DDR_MODE;
> +               host->ddr = 1;
> +       } else {
> +               temp &= ~SDXC_DDR_MODE;
> +               host->ddr = 0;
> +       }
> +       mci_writel(host, REG_GCTRL, temp);
> +
> +       /* set up clock */
> +       if (ios->clock && ios->power_mode) {
> +               dev_dbg(mmc_dev(host->mmc), "ios->clock: %d\n", ios->clock);
> +               sunxi_mmc_clk_set_rate(host, ios->clock);
> +               usleep_range(50000, 55000);
> +       }
> +}
> +
> +static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> +       struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
> +       unsigned long flags;
> +       u32 imask;
> +
> +       spin_lock_irqsave(&smc_host->lock, flags);
> +       imask = mci_readl(smc_host, REG_IMASK);
> +       if (enable) {
> +               smc_host->sdio_imask = SDXC_SDIO_INTERRUPT;
> +               imask |= SDXC_SDIO_INTERRUPT;
> +       } else {
> +               smc_host->sdio_imask = 0;
> +               imask &= ~SDXC_SDIO_INTERRUPT;
> +       }
> +       mci_writel(smc_host, REG_IMASK, imask);
> +       spin_unlock_irqrestore(&smc_host->lock, flags);
> +}
> +
> +static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
> +{
> +       struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
> +       mci_writel(smc_host, REG_HWRST, 0);
> +       udelay(10);
> +       mci_writel(smc_host, REG_HWRST, 1);
> +       udelay(300);
> +}
> +
> +static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> +       struct sunxi_mmc_host *host = mmc_priv(mmc);
> +       struct mmc_command *cmd = mrq->cmd;
> +       struct mmc_data *data = mrq->data;
> +       unsigned long iflags;
> +       u32 imask = SDXC_INTERRUPT_ERROR_BIT;
> +       u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
> +       u32 byte_cnt = 0;
> +       int ret;
> +
> +       if (!mmc_gpio_get_cd(mmc) || host->ferror) {
> +               dev_dbg(mmc_dev(host->mmc), "no medium present\n");
> +               mrq->cmd->error = -ENOMEDIUM;
> +               mmc_request_done(mmc, mrq);
> +               return;
> +       }
> +
> +       if (data) {
> +               byte_cnt = data->blksz * data->blocks;
> +               mci_writel(host, REG_BLKSZ, data->blksz);
> +               mci_writel(host, REG_BCNTR, byte_cnt);
> +               ret = sunxi_mmc_prepare_dma(host, data);
> +               if (ret < 0) {
> +                       dev_err(mmc_dev(host->mmc), "prepare DMA failed\n");
> +                       cmd->error = ret;
> +                       cmd->data->error = ret;
> +                       mmc_request_done(host->mmc, mrq);
> +                       return;
> +               }
> +       }
> +
> +       if (cmd->opcode == MMC_GO_IDLE_STATE) {
> +               cmd_val |= SDXC_SEND_INIT_SEQUENCE;
> +               imask |= SDXC_COMMAND_DONE;
> +       }
> +
> +       if (cmd->opcode == SD_SWITCH_VOLTAGE) {
> +               cmd_val |= SDXC_VOLTAGE_SWITCH;
> +               imask |= SDXC_VOLTAGE_CHANGE_DONE;
> +               host->voltage_switching = 1;
> +               sunxi_mmc_oclk_onoff(host, 1);
> +       }
> +
> +       if (cmd->flags & MMC_RSP_PRESENT) {
> +               cmd_val |= SDXC_RESPONSE_EXPIRE;
> +               if (cmd->flags & MMC_RSP_136)
> +                       cmd_val |= SDXC_LONG_RESPONSE;
> +               if (cmd->flags & MMC_RSP_CRC)
> +                       cmd_val |= SDXC_CHECK_RESPONSE_CRC;
> +
> +               if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) {
> +                       cmd_val |= SDXC_DATA_EXPIRE | SDXC_WAIT_PRE_OVER;
> +                       if (cmd->data->flags & MMC_DATA_STREAM) {
> +                               imask |= SDXC_AUTO_COMMAND_DONE;
> +                               cmd_val |= SDXC_SEQUENCE_MODE | SDXC_SEND_AUTO_STOP;
> +                       }
> +                       if (cmd->data->stop) {
> +                               imask |= SDXC_AUTO_COMMAND_DONE;
> +                               cmd_val |= SDXC_SEND_AUTO_STOP;
> +                       } else
> +                               imask |= SDXC_DATA_OVER;
> +
> +                       if (cmd->data->flags & MMC_DATA_WRITE)
> +                               cmd_val |= SDXC_WRITE;
> +                       else
> +                               host->wait_dma = 1;
> +               } else
> +                       imask |= SDXC_COMMAND_DONE;
> +       } else
> +               imask |= SDXC_COMMAND_DONE;
> +
> +       dev_dbg(mmc_dev(host->mmc), "cmd %d(%08x) arg %x ie 0x%08x len %d\n",
> +               cmd_val & 0x3f, cmd_val, cmd->arg, imask,
> +               mrq->data ? mrq->data->blksz * mrq->data->blocks : 0);
> +
> +       spin_lock_irqsave(&host->lock, iflags);
> +       host->mrq = mrq;
> +       mci_writel(host, REG_IMASK, host->sdio_imask | imask);
> +       spin_unlock_irqrestore(&host->lock, iflags);
> +
> +       mci_writel(host, REG_CARG, cmd->arg);
> +       mci_writel(host, REG_CMDR, cmd_val);
> +}
> +
> +static const struct of_device_id sunxi_mmc_of_match[] = {
> +       { .compatible = "allwinner,sun4i-a10-mmc", },
> +       { .compatible = "allwinner,sun5i-a13-mmc", },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
> +
> +static struct mmc_host_ops sunxi_mmc_ops = {
> +       .request         = sunxi_mmc_request,
> +       .set_ios         = sunxi_mmc_set_ios,
> +       .get_ro          = mmc_gpio_get_ro,
> +       .get_cd          = mmc_gpio_get_cd,
> +       .enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
> +       .hw_reset        = sunxi_mmc_hw_reset,
> +};
> +
> +static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
> +                                     struct platform_device *pdev)
> +{
> +       struct device_node *np = pdev->dev.of_node;
> +       int ret;
> +
> +       if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc"))
> +               host->idma_des_size_bits = 13;
> +       else
> +               host->idma_des_size_bits = 16;
> +
> +       host->vmmc = devm_regulator_get_optional(&pdev->dev, "vmmc");
> +       if (IS_ERR(host->vmmc) && PTR_ERR(host->vmmc) == -EPROBE_DEFER)
> +               return -EPROBE_DEFER;
> +
> +       host->reg_base = devm_ioremap_resource(&pdev->dev,
> +                             platform_get_resource(pdev, IORESOURCE_MEM, 0));
> +       if (IS_ERR(host->reg_base))
> +               return PTR_ERR(host->reg_base);
> +
> +       host->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
> +       if (IS_ERR(host->clk_ahb)) {
> +               dev_err(&pdev->dev, "Could not get ahb clock\n");
> +               return PTR_ERR(host->clk_ahb);
> +       }
> +
> +       host->clk_mod = devm_clk_get(&pdev->dev, "mod");
> +       if (IS_ERR(host->clk_mod)) {
> +               dev_err(&pdev->dev, "Could not get mod clock\n");
> +               return PTR_ERR(host->clk_mod);
> +       }
> +
> +       /* Make sure the controller is in a sane state before enabling irqs */
> +       ret = sunxi_mmc_init_host(host->mmc);
> +       if (ret)
> +               return ret;
> +
> +       host->irq = platform_get_irq(pdev, 0);
> +       ret = devm_request_irq(&pdev->dev, host->irq, sunxi_mmc_irq, 0,
> +                              "sunxi-mci", host);
> +       if (ret == 0)
> +               disable_irq(host->irq);
> +
> +       /* And put it back in reset */
> +       sunxi_mmc_exit_host(host);
> +
> +       return ret;
> +}
> +
> +static int sunxi_mmc_probe(struct platform_device *pdev)
> +{
> +       struct sunxi_mmc_host *host;
> +       struct mmc_host *mmc;
> +       int ret;
> +
> +       mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);
> +       if (!mmc) {
> +               dev_err(&pdev->dev, "mmc alloc host failed\n");
> +               return -ENOMEM;
> +       }
> +
> +       ret = mmc_of_parse(mmc);
> +       if (ret)
> +               goto error_free_host;
> +
> +       host = mmc_priv(mmc);
> +       host->mmc = mmc;
> +       spin_lock_init(&host->lock);
> +       tasklet_init(&host->tasklet, sunxi_mmc_tasklet, (unsigned long)host);
> +
> +       ret = sunxi_mmc_resource_request(host, pdev);
> +       if (ret)
> +               goto error_free_host;
> +
> +       host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
> +                                         &host->sg_dma, GFP_KERNEL);
> +       if (!host->sg_cpu) {
> +               dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n");
> +               ret = -ENOMEM;
> +               goto error_free_host;
> +       }
> +
> +       mmc->ops                = &sunxi_mmc_ops;
> +       mmc->max_blk_count      = 8192;
> +       mmc->max_blk_size       = 4096;
> +       mmc->max_segs           = PAGE_SIZE / sizeof(struct sunxi_idma_des);
> +       mmc->max_seg_size       = (1 << host->idma_des_size_bits);
> +       mmc->max_req_size       = mmc->max_seg_size * mmc->max_segs;
> +       /* 400kHz ~ 50MHz */
> +       mmc->f_min              =   400000;
> +       mmc->f_max              = 50000000;
> +       /* available voltages */
> +       if (!IS_ERR(host->vmmc))
> +               mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vmmc);
> +       else
> +               mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
> +
> +       mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
> +               MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
> +               MMC_CAP_UHS_DDR50 | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL |

I think you can drop MMC_CAP_NEEDS_POLL, now that you're using mmc core GPIO
functions, which use GPIO irqs when possible, and sets MMC_CAP_NEEDS_POLL if
that fails.


Cheers
ChenYu

> +               MMC_CAP_DRIVER_TYPE_A;
> +
> +       if (host->bus_width == 4)
> +               mmc->caps |= MMC_CAP_4_BIT_DATA;
> +
> +       mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP;
> +
> +       ret = mmc_add_host(mmc);
> +
> +       if (ret)
> +               goto error_free_dma;
> +
> +       dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
> +       platform_set_drvdata(pdev, mmc);
> +       return 0;
> +
> +error_free_dma:
> +       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> +error_free_host:
> +       mmc_free_host(mmc);
> +       return ret;
> +}
> +
> +static int sunxi_mmc_remove(struct platform_device *pdev)
> +{
> +       struct mmc_host *mmc = platform_get_drvdata(pdev);
> +       struct sunxi_mmc_host *host = mmc_priv(mmc);
> +
> +       mmc_remove_host(mmc);
> +       sunxi_mmc_exit_host(host);
> +       tasklet_disable(&host->tasklet);
> +       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> +       mmc_free_host(mmc);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver sunxi_mmc_driver = {
> +       .driver = {
> +               .name   = "sunxi-mci",
> +               .owner  = THIS_MODULE,
> +               .of_match_table = of_match_ptr(sunxi_mmc_of_match),
> +       },
> +       .probe          = sunxi_mmc_probe,
> +       .remove         = sunxi_mmc_remove,
> +};
> +module_platform_driver(sunxi_mmc_driver);
> +
> +MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("David Lanzend?rfer <david.lanzendoerfer@o2s.ch>");
> +MODULE_ALIAS("platform:sunxi-mmc");
[..]

^ permalink raw reply

* [PATCH 08/14] net: axienet: Removed checkpatch errors/warnings
From: Michal Simek @ 2014-02-13  7:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1392251494.2214.11.camel@joe-AO722>

Hi Joe,

On 02/13/2014 01:31 AM, Joe Perches wrote:
> On Wed, 2014-02-12 at 16:55 +0100, Michal Simek wrote:
>> From: Srikanth Thokala <srikanth.thokala@xilinx.com>
> 
> trivia:
> 
>> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> 
>> +			netdev_err(lp->ndev,
>> +				"axienet_device_reset DMA reset timeout!\n");
> 
> could you please align multi-line arguments to the
> appropriate open parenthesis?
> 
> 			netdev_err(lp->ndev,
> 				   "axienet_device_reset DMA reset timeout!\n");
> 
> or maybe:
> 
> 			netdev_err(lp->ndev, "%s: "DMA reset timeout!\n",
> 				   __func__);

ok.

> 
>> @@ -484,8 +484,8 @@ static void axienet_device_reset(struct net_device *ndev)
>>  	}
>>
>>  	if (axienet_dma_bd_init(ndev)) {
>> -		dev_err(&ndev->dev, "axienet_device_reset descriptor "
>> -			"allocation failed\n");
>> +		netdev_err(ndev,
>> +			"axienet_device_reset descriptor allocation failed\n");
> 
> etc, et al.


ok.

> 
>> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> []
>> @@ -161,19 +161,19 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
>>
>>  	np1 = of_find_node_by_name(NULL, "cpu");
>>  	if (!np1) {
>> -		printk(KERN_WARNING "%s(): Could not find CPU device node.",
>> -		       __func__);
>> -		printk(KERN_WARNING "Setting MDIO clock divisor to "
>> -		       "default %d\n", DEFAULT_CLOCK_DIVISOR);
>> +		netdev_warn(lp->ndev, "Could not find CPU device node.");
> 
> missing trailing "\n" to terminate message.

ok.

> 
>> +		netdev_warn(lp->ndev,
>> +			 "Could not find clock ethernet controller property.");
> 
> here too. (and alignment)

This is problematic. I would like to keep 80 char limits and keeping
this align just break it. That's why I was using tab alignment.
Probably the solution is just to shorten message.

Thanks for your comments,
Michal

^ permalink raw reply

* [PATCH v2 2/2] net: axienet: Fix compilation warnings
From: Michal Simek @ 2014-02-13  7:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392275432.git.michal.simek@xilinx.com>

Warning log:
xilinx_axienet_main.c: In function 'axienet_start_xmit_done':
xilinx_axienet_main.c:617:16: warning: operation on 'lp->tx_bd_ci' may be undefined [-Wsequence-point]
xilinx_axienet_main.c: In function 'axienet_start_xmit':
xilinx_axienet_main.c:703:18: warning: operation on 'lp->tx_bd_tail' may be undefined [-Wsequence-point]
xilinx_axienet_main.c:719:17: warning: operation on 'lp->tx_bd_tail' may be undefined [-Wsequence-point]
xilinx_axienet_main.c: In function 'axienet_recv':
xilinx_axienet_main.c:792:16: warning: operation on 'lp->rx_bd_ci' may be undefined [-Wsequence-point]
xilinx_axienet_main.c: In function 'axienet_of_probe':
xilinx_axienet_main.c:1501:21: warning: unused variable 'rc' [-Wunused-variable]

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

Changes in v2:
- Separate bug fixes from features and cleanups

 drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 9fb8ab2..4bfdf8c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -601,7 +601,8 @@ static void axienet_start_xmit_done(struct net_device *ndev)
 		size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
 		packets++;

-		lp->tx_bd_ci = ++lp->tx_bd_ci % TX_BD_NUM;
+		++lp->tx_bd_ci;
+		lp->tx_bd_ci %= TX_BD_NUM;
 		cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
 		status = cur_p->status;
 	}
@@ -687,7 +688,8 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 				     skb_headlen(skb), DMA_TO_DEVICE);

 	for (ii = 0; ii < num_frag; ii++) {
-		lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM;
+		++lp->tx_bd_tail;
+		lp->tx_bd_tail %= TX_BD_NUM;
 		cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
 		frag = &skb_shinfo(skb)->frags[ii];
 		cur_p->phys = dma_map_single(ndev->dev.parent,
@@ -703,7 +705,8 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail;
 	/* Start the transfer */
 	axienet_dma_out32(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p);
-	lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM;
+	++lp->tx_bd_tail;
+	lp->tx_bd_tail %= TX_BD_NUM;

 	return NETDEV_TX_OK;
 }
@@ -775,7 +778,8 @@ static void axienet_recv(struct net_device *ndev)
 		cur_p->status = 0;
 		cur_p->sw_id_offset = (u32) new_skb;

-		lp->rx_bd_ci = ++lp->rx_bd_ci % RX_BD_NUM;
+		++lp->rx_bd_ci;
+		lp->rx_bd_ci %= RX_BD_NUM;
 		cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
 	}

--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140213/57a09d0d/attachment.sig>

^ permalink raw reply related

* [PATCH v2 1/2] net: axienet: Fix compilation error
From: Michal Simek @ 2014-02-13  7:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1392275432.git.michal.simek@xilinx.com>

Add missing header to fix compilation error.
drivers/net/ethernet/xilinx/xilinx_axienet_main.c:1575:22:
 error: undefined identifier 'irq_of_parse_and_map'
drivers/net/ethernet/xilinx/xilinx_axienet_main.c:1576:22:
 error: undefined identifier 'irq_of_parse_and_map'

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

Changes in v2: None

 drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 1ec65fe..9fb8ab2 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -26,6 +26,7 @@
 #include <linux/netdevice.h>
 #include <linux/of_mdio.h>
 #include <linux/of_platform.h>
+#include <linux/of_irq.h>
 #include <linux/of_address.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140213/d1311498/attachment.sig>

^ permalink raw reply related

* [PATCH v2 0/2] Xilinx axi ethernet bug fixes
From: Michal Simek @ 2014-02-13  7:10 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Based on our discussion I am sending 2 bug fixes for axi ethernet
driver against your net tree.

Thanks,
Michal

Changes in v2:
- Separate bug fixes from features and cleanups

Michal Simek (2):
  net: axienet: Fix compilation error
  net: axienet: Fix compilation warnings

 drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140213/f8f12a4c/attachment-0001.sig>

^ permalink raw reply

* [PATCH 0/14] Xilinx axi ethernet patches
From: Michal Simek @ 2014-02-13  7:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140212.191846.1464564509609498703.davem@davemloft.net>

On 02/13/2014 01:18 AM, David Miller wrote:
> From: Michal Simek <michal.simek@xilinx.com>
> Date: Wed, 12 Feb 2014 16:55:34 +0100
> 
>> I have exctracted patches which I have in our
>> xilinx git tree which are missing in the mainline.
>>
>> The first two patches fix compilation error and
>> warnings. Then 5 feature patches
>> and the rest is OF cleanup and with kernel-doc
>> and checkpatch problems.
> 
> You should not combine bug fix and feature patches.
> 
> Rather, you should submit bug fixes against the 'net' tree.  Then when
> those bug fixes get applied and propagate to the 'net-next' tree you
> can submit the feature patches and cleanups against the 'net-next'
> tree.

Ok. I will.

Thanks,
Michal

^ permalink raw reply

* [PATCH 4/4] phy: miphy365x: Provide support for the MiPHY356x Generic PHY
From: Kishon Vijay Abraham I @ 2014-02-13  6:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1392220985-28189-4-git-send-email-lee.jones@linaro.org>

Hi,

On Wednesday 12 February 2014 09:33 PM, Lee Jones wrote:
> The MiPHY365x is a Generic PHY which can serve various SATA or PCIe
> devices. It has 2 ports which it can use for either; both SATA, both

various SATA or PCIe devices in STMicroelectronics STiH41x SoC series?
> PCIe or one of each in any configuration.
> 
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/phy/Kconfig         |   8 +
>  drivers/phy/Makefile        |   1 +
>  drivers/phy/phy-miphy365x.c | 634 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 643 insertions(+)
>  create mode 100644 drivers/phy/phy-miphy365x.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 330ef2d..bb2706a 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -21,6 +21,14 @@ config PHY_EXYNOS_MIPI_VIDEO
>  	  Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
>  	  and EXYNOS SoCs.
>  
> +config PHY_MIPHY365X
> +	tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
> +	depends on ARCH_STI
> +	depends on GENERIC_PHY
depends on CONFIG_OF and HAS_IOMEM?
> +	help
> +	  Enable this to support the miphy transceiver (for SATA/PCIE)
> +	  that is part of STMicroelectronics STiH41x SoC series.
> +
>  config OMAP_USB2
>  	tristate "OMAP USB2 PHY Driver"
>  	depends on ARCH_OMAP2PLUS
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index d0caae9..5879639 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -5,5 +5,6 @@
>  obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
>  obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)	+= phy-exynos-dp-video.o
>  obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
> +obj-$(CONFIG_PHY_MIPHY365X)		+= phy-miphy365x.o
>  obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
>  obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c
> new file mode 100644
> index 0000000..c4124e3
> --- /dev/null
> +++ b/drivers/phy/phy-miphy365x.c
> @@ -0,0 +1,634 @@
> +/*
> + * Copyright (C) 2014 STMicroelectronics
> + *
> + * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
> + *
> + * Author: Alexandre Torgue <alexandre.torgue@st.com>

The author of this patch is not Alexandre Torgue?
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/clk.h>
> +#include <linux/phy/phy.h>
> +#include <linux/delay.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include <dt-bindings/phy/phy-miphy365x.h>
> +
> +#define HFC_TIMEOUT		50
> +
> +#define SYSCFG_2521		0x824
> +#define SYSCFG_2522		0x828
> +#define SYSCFG_PCIE_SATA_MASK	BIT(1)
> +#define SYSCFG_PCIE_SATA_POS	1
> +
> +/* MiPHY365x register definitiona */
> +#define RESET_REG		0x00
> +#define RST_PLL			BIT(1)

There are quite a few alignment problems with these macros. It needs to be fixed.
> +#define RST_PLL_CAL		BIT(2)
> +#define RST_RX			BIT(4)
> +#define RST_MACRO		BIT(7)
> +
> +#define STATUS_REG		0x01
> +#define IDLL_RDY		BIT(0)
> +#define PLL_RDY			BIT(1)
> +#define DES_BIT_LOCK		BIT(2)
> +#define DES_SYMBOL_LOCK		BIT(3)
> +
> +#define CTRL_REG		0x02
> +#define TERM_EN			BIT(0)
> +#define PCI_EN			BIT(2)
> +#define DES_BIT_LOCK_EN		BIT(3)
> +#define TX_POL			BIT(5)
> +
> +#define INT_CTRL_REG		0x03
> +
> +#define BOUNDARY1_REG		0x10
> +#define SPDSEL_SEL		BIT(0)
> +
> +#define BOUNDARY3_REG		0x12
> +#define TX_SPDSEL_GEN1_VAL	0
> +#define TX_SPDSEL_GEN2_VAL	0x01
> +#define TX_SPDSEL_GEN3_VAL	0x02
> +#define RX_SPDSEL_GEN1_VAL	0
> +#define RX_SPDSEL_GEN2_VAL	(0x01 << 3)
> +#define RX_SPDSEL_GEN3_VAL	(0x02 << 3)
> +
> +#define PCIE_REG		0x16
> +
> +#define BUF_SEL_REG		0x20
> +#define CONF_GEN_SEL_GEN3	0x02
> +#define CONF_GEN_SEL_GEN2	0x01
> +#define PD_VDDTFILTER		BIT(4)
> +
> +#define TXBUF1_REG		0x21
> +#define SWING_VAL		0x04
> +#define SWING_VAL_GEN1		0x03
> +#define PREEMPH_VAL		(0x3 << 5)
> +
> +#define TXBUF2_REG		0x22
> +#define TXSLEW_VAL		0x2
> +#define TXSLEW_VAL_GEN1		0x4
> +
> +#define RXBUF_OFFSET_CTRL_REG	0x23
> +
> +#define RXBUF_REG		0x25
> +#define SDTHRES_VAL		0x01
> +#define EQ_ON3			(0x03 << 4)
> +#define EQ_ON1			(0x01 << 4)
> +
> +#define COMP_CTRL1_REG		0x40
> +#define START_COMSR		BIT(0)
> +#define START_COMZC		BIT(1)
> +#define COMP_AUTO_LOAD		BIT(4)
> +
> +#define COMP_CTRL2_REG		0x41
> +#define COMP_2MHZ_RAT_GEN1	0x1e
> +#define COMP_2MHZ_RAT		0xf
> +
> +#define COMP_CTRL3_REG		0x42
> +#define COMSR_COMP_REF		0x33
> +
> +#define COMP_IDLL_REG		0x47
> +#define COMZC_IDLL		0x2a
> +
> +#define PLL_CTRL1_REG		0x50
> +#define PLL_START_CAL		BIT(0)
> +#define BUF_EN			BIT(2)
> +#define SYNCHRO_TX		BIT(3)
> +#define SSC_EN			BIT(6)
> +#define CONFIG_PLL		BIT(7)
> +
> +#define PLL_CTRL2_REG		0x51
> +#define BYPASS_PLL_CAL		BIT(1)
> +
> +#define PLL_RAT_REG		0x52
> +
> +#define PLL_SSC_STEP_MSB_REG	0x56
> +#define PLL_SSC_STEP_MSB_VAL	0x03
> +
> +#define PLL_SSC_STEP_LSB_REG	0x57
> +#define PLL_SSC_STEP_LSB_VAL	0x63
> +
> +#define PLL_SSC_PER_MSB_REG	0x58
> +#define PLL_SSC_PER_MSB_VAL	0
> +
> +#define PLL_SSC_PER_LSB_REG	0x59
> +#define PLL_SSC_PER_LSB_VAL	0xf1
> +
> +#define IDLL_TEST_REG		0x72
> +#define START_CLK_HF		BIT(6)
> +
> +#define DES_BITLOCK_REG		0x86
> +#define BIT_LOCK_LEVEL		0x01
> +#define BIT_LOCK_CNT_512	(0x03 << 5)
> +
> +static u8 ports[] = { MIPHY_PORT_0, MIPHY_PORT_1 };
> +
> +struct miphy365x_phy {
> +	struct phy *phy;
> +	void __iomem *base;
> +	void __iomem *sata;
> +	void __iomem *pcie;
> +	u8 type;
> +	u8 port;
> +};
> +
> +struct miphy365x_dev {
> +	struct device *dev;
> +	struct mutex miphy_mutex;
> +	struct miphy365x_phy phys[ARRAY_SIZE(ports)];
> +	bool pcie_tx_pol_inv;
> +	bool sata_tx_pol_inv;
> +	u32 sata_gen;
> +	struct regmap *regmap;
> +};
> +
> +enum miphy_sata_gen {
> +	SATA_GEN1,
> +	SATA_GEN2,
> +	SATA_GEN3
> +};
> +
> +static u8 rx_tx_spd[] = {
> +	TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
> +	TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
> +	TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
> +};
> +
> +#define miphy365x_phy_to_dev(inst) \
> +	container_of((inst), struct miphy365x_dev, phys[(inst)->port]);
> +
> +/*
> + * This function selects the system configuration,
> + * either two SATA, one SATA and one PCIe, or two PCIe lanes.
> + */
> +static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
> +			      struct miphy365x_dev *miphy_dev)
> +{
> +	u8 config = miphy_phy->type | miphy_phy->port;
> +	u32 mask  = SYSCFG_PCIE_SATA_MASK;
> +	u32 reg;
> +	bool sata;
> +
> +	switch (config) {
> +	case MIPHY_SATA_PORT0:
> +		reg = SYSCFG_2521;
> +		sata = true;

How do we configure PORT1 for SATA here? Do we really support all the system
configuration?
> +		break;
> +	case MIPHY_PCIE_PORT1:
> +		reg = SYSCFG_2522;
> +		sata = false;
> +		break;
> +	default:
> +		dev_err(miphy_dev->dev, "Configuration not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	return regmap_update_bits(miphy_dev->regmap, reg, mask,
> +				  sata << SYSCFG_PCIE_SATA_POS);
> +}
> +
> +static void miphy365x_phy_init_pcie_port(struct miphy365x_phy *miphy_phy,
> +					 struct miphy365x_dev *miphy_dev)
> +{
> +	u8 val;
> +
> +	if (!miphy_dev->pcie_tx_pol_inv)
> +		return;
> +
> +	/* Invert Tx polarity and clear pci_txdetect_pol bit */
> +	val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
> +	writeb_relaxed(val, miphy_phy->base + CTRL_REG);
> +	writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
> +}
> +
> +static inline int miphy365x_phy_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
> +					    struct miphy365x_dev *miphy_dev)
> +{
> +	int timeout = HFC_TIMEOUT;
> +	u8 mask = IDLL_RDY | PLL_RDY;
> +	u8 regval;
> +
> +	do {
> +		regval = readb_relaxed(miphy_phy->base + STATUS_REG);
> +		usleep_range(2000, 2500);

Any comment on how this delay value is obtained?
> +	} while (timeout-- && (regval & mask));
> +
> +	if (timeout < 0) {
> +		dev_err(miphy_dev->dev, "HFC ready timeout!\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline int miphy365x_phy_rdy(struct miphy365x_phy *miphy_phy,
> +				    struct miphy365x_dev *miphy_dev)
> +{
> +	int timeout = HFC_TIMEOUT;
> +	u8 mask = mask = IDLL_RDY | PLL_RDY;

just u8 mask = IDLL_RDY | PLL_RDY; would suffice.
> +	u8 regval;
> +
> +	do {
> +		regval = readb_relaxed(miphy_phy->base + STATUS_REG);
> +		usleep_range(2000, 2500);

same here.
> +	} while (timeout-- && ((regval & mask) != mask));
> +
> +	if (timeout < 0) {
> +		dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void miphy365x_phy_set_comp(struct miphy365x_phy *miphy_phy,
> +					  struct miphy365x_dev *miphy_dev)
> +{
> +	u8 val, mask;
> +
> +	if (miphy_dev->sata_gen == SATA_GEN1)
> +		writeb_relaxed(COMP_2MHZ_RAT_GEN1,
> +			       miphy_phy->base + COMP_CTRL2_REG);
> +	else
> +		writeb_relaxed(COMP_2MHZ_RAT,
> +			       miphy_phy->base + COMP_CTRL2_REG);
> +
> +	if (miphy_dev->sata_gen != SATA_GEN3) {
> +		writeb_relaxed(COMSR_COMP_REF,
> +			       miphy_phy->base + COMP_CTRL3_REG);
> +		/*
> +		 * Force VCO current to value defined by address 0x5A
> +		 * and disable PCIe100Mref bit
> +		 * Enable auto load compensation for pll_i_bias
> +		 */
> +		writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
> +		writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
> +	}
> +
> +	/*
> +	 * Force restart compensation and enable auto load
> +	 * for Comzc_Tx, Comzc_Rx and Comsr on macro
> +	 */
> +	val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
> +	writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
> +
> +	mask = DES_BIT_LOCK | DES_SYMBOL_LOCK;
> +	while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask)	!= mask)
> +		cpu_relax();

Don't we need to break from here at some point if the LOCK's are never set?
> +}
> +
> +static inline void miphy365x_phy_set_ssc(struct miphy365x_phy *miphy_phy,
> +					 struct miphy365x_dev *miphy_dev)
> +{
> +	u8 val;
> +
> +	/*
> +	 * SSC Settings. SSC will be enabled through Link
> +	 * SSC Ampl. = 0.4%
> +	 * SSC Freq = 31KHz
> +	 */
> +	writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
> +			miphy_phy->base + PLL_SSC_STEP_MSB_REG);
> +	writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
> +			miphy_phy->base + PLL_SSC_STEP_LSB_REG);
> +	writeb_relaxed(PLL_SSC_PER_MSB_VAL,
> +			miphy_phy->base + PLL_SSC_PER_MSB_REG);
> +	writeb_relaxed(PLL_SSC_PER_LSB_VAL,
> +			miphy_phy->base + PLL_SSC_PER_LSB_REG);
> +
> +	/* SSC Settings complete */
> +	if (miphy_dev->sata_gen == SATA_GEN1) {
> +		val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
> +		writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
> +	} else {
> +		val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
> +		writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
> +	}
> +}
> +
> +static int miphy365x_phy_init_sata_port(struct miphy365x_phy *miphy_phy,
> +					struct miphy365x_dev *miphy_dev)
> +{
> +	int ret;
> +	u8 val;
> +
> +	/*
> +	 * Force PHY macro reset, PLL calibration reset, PLL reset
> +	 * and assert Deserializer Reset
> +	 */
> +	val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
> +	writeb_relaxed(val, miphy_phy->base + RESET_REG);
> +
> +	if (miphy_dev->sata_tx_pol_inv)
> +		writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
> +
> +	/*
> +	 * Force macro1 to use rx_lspd, tx_lspd
> +	 * Force Rx_Clock on first I-DLL phase
> +	 * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
> +	 */
> +	writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
> +	writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
> +	val = rx_tx_spd[miphy_dev->sata_gen];
> +	writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
> +
> +	/* Wait for HFC_READY = 0 */
> +	ret = miphy365x_phy_hfc_not_rdy(miphy_phy, miphy_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Compensation Recalibration */
> +	miphy365x_phy_set_comp(miphy_phy, miphy_dev);
> +
> +	switch (miphy_dev->sata_gen) {
> +	case SATA_GEN3:
> +		/*
> +		 * TX Swing target 550-600mv peak to peak diff
> +		 * Tx Slew target 90-110ps rising/falling time
> +		 * Rx Eq ON3, Sigdet threshold SDTH1
> +		 */
> +		val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
> +		writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
> +		val = SWING_VAL | PREEMPH_VAL;
> +		writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
> +		writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
> +		writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
> +		val = SDTHRES_VAL | EQ_ON3;
> +		writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
> +		break;
> +	case SATA_GEN2:
> +		/*
> +		 * conf gen sel=0x1 to program Gen2 banked registers
> +		 * VDDT filter ON
> +		 * Tx Swing target 550-600mV peak-to-peak diff
> +		 * Tx Slew target 90-110 ps rising/falling time
> +		 * RX Equalization ON1, Sigdet threshold SDTH1
> +		 */
> +		writeb_relaxed(CONF_GEN_SEL_GEN2,
> +			       miphy_phy->base + BUF_SEL_REG);
> +		writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
> +		writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
> +		val = SDTHRES_VAL | EQ_ON1;
> +		writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
> +		break;
> +	case SATA_GEN1:
> +		/*
> +		 * conf gen sel = 00b to program Gen1 banked registers
> +		 * VDDT filter ON
> +		 * Tx Swing target 500-550mV peak-to-peak diff
> +		 * Tx Slew target120-140 ps rising/falling time
> +		 */
> +		writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
> +		writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
> +		writeb_relaxed(TXSLEW_VAL_GEN1,	miphy_phy->base + TXBUF2_REG);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Force Macro1 in partial mode & release pll cal reset */
> +	writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
> +	usleep_range(100, 150);
> +
> +	miphy365x_phy_set_ssc(miphy_phy, miphy_dev);
> +
> +	/* Wait for phy_ready */
> +	ret = miphy365x_phy_rdy(miphy_phy, miphy_dev);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Enable macro1 to use rx_lspd & tx_lspd
> +	 * Release Rx_Clock on first I-DLL phase on macro1
> +	 * Assert deserializer reset
> +	 * des_bit_lock_en is set
> +	 * bit lock detection strength
> +	 * Deassert deserializer reset
> +	 */
> +	writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
> +	writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
> +	writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
> +	val = miphy_dev->sata_tx_pol_inv ?
> +		(TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
> +	writeb_relaxed(val, miphy_phy->base + CTRL_REG);
> +
> +	val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
> +	writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
> +	writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
> +
> +	return 0;
> +}
> +
> +static int miphy365x_phy_init(struct phy *phy)
> +{
> +	int ret = 0;
> +	struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
> +	struct miphy365x_dev *miphy_dev = miphy365x_phy_to_dev(miphy_phy);
> +
> +	mutex_lock(&miphy_dev->miphy_mutex);
> +
> +	ret = miphy365x_set_path(miphy_phy, miphy_dev);
> +	if (ret) {
> +		mutex_unlock(&miphy_dev->miphy_mutex);
> +		return ret;
> +	}
> +
> +	/* Initialise Miphy for PCIe or SATA */
> +	if (miphy_phy->type == MIPHY_TYPE_PCIE)
> +		miphy365x_phy_init_pcie_port(miphy_phy, miphy_dev);
> +	else
> +		ret = miphy365x_phy_init_sata_port(miphy_phy, miphy_dev);
> +
> +	mutex_unlock(&miphy_dev->miphy_mutex);
> +
> +	return ret;
> +}
> +
> +static int miphy365x_phy_power_on(struct phy *phy)
> +{
> +	return 0;
> +}
> +
> +static int miphy365x_phy_power_off(struct phy *phy)
> +{
> +	return 0;
> +}

Both these empty functions can be removed.
> +
> +static struct phy *miphy365x_phy_xlate(struct device *dev,
> +				       struct of_phandle_args *args)
> +{
> +	struct miphy365x_dev *state = dev_get_drvdata(dev);
> +	u8 port = args->args[0];
> +	u8 type = args->args[1];
> +
> +	if (WARN_ON(port >= ARRAY_SIZE(ports)))
> +		return ERR_PTR(-EINVAL);
> +
> +	if (type == MIPHY_TYPE_SATA)
> +		state->phys[port].base = state->phys[port].sata;
> +	else if (type == MIPHY_TYPE_PCIE)
> +		state->phys[port].base = state->phys[port].pcie;
> +	else {
> +		WARN(1, "Invalid type specified in DT");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	state->phys[port].type = type;
> +
> +	return state->phys[port].phy;
> +}
> +
> +static struct phy_ops miphy365x_phy_ops = {
> +	.init		= miphy365x_phy_init,
> +	.power_on	= miphy365x_phy_power_on,
> +	.power_off	= miphy365x_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static int miphy365x_phy_get_base_addr(struct platform_device *pdev,
> +				       struct miphy365x_phy *phy, u8 port)
> +{
> +	struct resource *res;
> +	char sata[16];
> +	char pcie[16];

It can be done with a single variable ;-)
> +
> +	sprintf(sata, "sata%d", port);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, sata);
> +	if (!res)
> +		return -ENODEV;
> +
> +	phy->sata = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!phy->sata)
> +		return -ENOMEM;
> +
> +	sprintf(pcie, "pcie%d", port);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pcie);
> +	if (!res)
> +		return -ENODEV;
> +
> +	phy->pcie = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!phy->pcie)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static int miphy365x_phy_of_probe(struct device_node *np,
> +				  struct miphy365x_dev *phy_dev)
> +{
> +	const char *sata_gen;
> +
> +	phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(phy_dev->regmap)) {
> +		dev_err(phy_dev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(phy_dev->regmap);
> +	}
> +
> +	/* Default */
> +	phy_dev->sata_gen = SATA_GEN1;
> +
> +	of_property_read_string(np, "st,sata_gen", &sata_gen);
> +	if (sata_gen) {
> +		if (!strcmp(sata_gen, "gen3"))
> +			phy_dev->sata_gen = SATA_GEN3;
> +		else if (!strcmp(sata_gen, "gen2"))
> +			phy_dev->sata_gen = SATA_GEN2;
> +	}
> +
> +	phy_dev->pcie_tx_pol_inv =
> +		of_property_read_bool(np, "st,pcie_tx_pol_inv");
> +
> +	phy_dev->sata_tx_pol_inv =
> +		of_property_read_bool(np, "st,sata_tx_pol_inv");
> +
> +	return 0;
> +}
> +
> +static int miphy365x_phy_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct miphy365x_dev *phy_dev;
> +	struct device *dev = &pdev->dev;
> +	struct phy_provider *provider;
> +	u8 port;
> +	int ret;
> +
> +	if (!np) {
> +		dev_err(dev, "No DT found\n");
> +		return -EINVAL;
> +	}
> +
> +	phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
> +	if (!phy_dev)
> +		return -ENOMEM;
> +
> +	ret = miphy365x_phy_of_probe(np, phy_dev);
> +	if (ret)
> +		return ret;
> +
> +	phy_dev->dev = dev;
> +
> +	dev_set_drvdata(dev, phy_dev);
> +
> +	mutex_init(&phy_dev->miphy_mutex);
> +
> +	provider = devm_of_phy_provider_register(dev, miphy365x_phy_xlate);
> +	if (IS_ERR(provider))
> +		return PTR_ERR(provider);

Phy provider register should be the last step in registering the PHY.

Thanks
Kishon

^ permalink raw reply

* [PATCH] input: sirfsoc-onkey - report onkey untouch event by detecting pin status
From: Barry Song @ 2014-02-13  6:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <892ec8be-638e-48d6-9e7d-5175694a7eaf@email.android.com>

2014-02-13 11:41 GMT+08:00 Dmitry Torokhov <dmitry.torokhov@gmail.com>:
> On February 12, 2014 6:32:03 PM PST, Barry Song <21cnbao@gmail.com> wrote:
>>2014-02-13 7:11 GMT+08:00 Dmitry Torokhov <dmitry.torokhov@gmail.com>:
>>> Hi Barry,
>>>
>>> On Mon, Feb 10, 2014 at 06:07:39PM +0800, Barry Song wrote:
>>>>
>>>>  static int sirfsoc_pwrc_remove(struct platform_device *pdev)
>>>>  {
>>>> +     struct sirfsoc_pwrc_drvdata *pwrcdrv =
>>dev_get_drvdata(&pdev->dev);
>>>> +
>>>>       device_init_wakeup(&pdev->dev, 0);
>>>>
>>>> +     cancel_delayed_work_sync(&pwrcdrv->work);
>>>> +
>>>
>>> This is racy: interrupt is freed later and can schedule work again.
>>
>>thanks, Dmitry. i will do a manual devm_free_irq() before cancelling
>>the work and before devres removes the resources.
>
> Another option would be to use devm custom action to ensure that work is canceled after freeing IRQ.

yes. you did a great job to have a devm_add_action() API.

>
>
> --
> Dmitry

-barry

^ permalink raw reply

* [PATCH 0/3] clk: mmp: bug fix for clk-frac
From: Chao Xie @ 2014-02-13  6:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390445262-27486-1-git-send-email-chao.xie@marvell.com>

hi, Mike
How about these fix patches?

On Thu, Jan 23, 2014 at 10:47 AM, Chao Xie <chao.xie@marvell.com> wrote:
> From: Chao Xie <chao.xie@marvell.com>
>
> When use the clk-frac type of mmp, some bugs are found.
> The following 3 patches are bug fix for clk-frac
>
> Chao Xie (3):
>   clk: mmp: fix wrong mask when calculate denominator
>   clk: mmp: fix the wrong calculation formula
>   clk: mmp: try to use closer one when do round rate
>
>  drivers/clk/mmp/clk-frac.c | 21 +++++++++++++--------
>  1 file changed, 13 insertions(+), 8 deletions(-)
>
> --
> 1.8.3.2
>

^ permalink raw reply

* [PATCH] ARM: shmobile: koelsch: Enable SDHI, GPIO and regulators in defconfig
From: Simon Horman @ 2014-02-13  6:39 UTC (permalink / raw)
  To: linux-arm-kernel

Enable the following:
* regulators (needed for SDHI)
* SDHI
* GPIO
* GPIO keys
* GPIO LEDs

Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 arch/arm/configs/koelsch_defconfig | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/configs/koelsch_defconfig b/arch/arm/configs/koelsch_defconfig
index 9561139..86faab5 100644
--- a/arch/arm/configs/koelsch_defconfig
+++ b/arch/arm/configs/koelsch_defconfig
@@ -59,6 +59,7 @@ CONFIG_SH_ETH=y
 # CONFIG_NET_VENDOR_VIA is not set
 # CONFIG_NET_VENDOR_WIZNET is not set
 # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_KEYBOARD_GPIO=y
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_SH_SCI=y
@@ -68,13 +69,21 @@ CONFIG_I2C=y
 CONFIG_I2C_RCAR=y
 CONFIG_SPI=y
 CONFIG_SPI_RSPI=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_RCAR=y
 # CONFIG_HWMON is not set
 CONFIG_THERMAL=y
 CONFIG_RCAR_THERMAL=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
 # CONFIG_HID is not set
 # CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_SDHI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
 # CONFIG_IOMMU_SUPPORT is not set
 # CONFIG_DNOTIFY is not set
 CONFIG_TMPFS=y
-- 
1.8.5.2

^ permalink raw reply related

* [PATCH v4 3/8] at91: dt: Add at91sam9261 dt SoC support
From: Jean-Jacques Hiblot @ 2014-02-13  6:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140212210446.GF11498@piout.net>

2014-02-12 22:04 GMT+01:00 Alexandre Belloni
<alexandre.belloni@free-electrons.com>:
> Sorry, one more comment on that patch:
>
> On 12/02/2014 at 11:06:42 +0100, Jean-Jacques Hiblot wrote :
>> +                     pinctrl at fffff400 {
>> +                             #address-cells = <1>;
>> +                             #size-cells = <1>;
>> +                             compatible = "atmel,at91rm9200-pinctrl", "simple-bus";
>> +                             ranges = <0xfffff400 0xfffff400 0xa00>;
>> +
> I believe you got the range wrong, shouldn't it be:
>                                 ranges = <0xfffff400 0xfffff400 0x600>;
>
Yes you're right.
Thanks for the review
>
> --
> Alexandre Belloni, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com

^ permalink raw reply

* [PATCH 0/2] Add Ether DT support for R8A7791/Koelsch reference board
From: Simon Horman @ 2014-02-13  6:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201402070110.18561.sergei.shtylyov@cogentembedded.com>

On Fri, Feb 07, 2014 at 01:10:17AM +0300, Sergei Shtylyov wrote:
> Hello.
> 
>    Here's the set of 2 patches against Simon Horman's 'renesas.git' repo,
> 'renesas-devel-v3.14-rc1-20140206v2' tag. Here we add the Ether device tree
> support working on the R8A7791/Koelsch reference board. The patchset requires
> the 'sh_eth' driver device tree support posted earlier in order to work.
> 
> [1/2] ARM: shmobile: r8a7791: add Ether DT support
> [2/2] ARM: shmobile: koelsch: add Ether DT support

Thanks. Please repost this series once the bindings have been accepted.

^ permalink raw reply

* [PATCH 0/2] Add Ether DT support for R8A7790/Lager reference board
From: Simon Horman @ 2014-02-13  6:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201402070054.39989.sergei.shtylyov@cogentembedded.com>

On Fri, Feb 07, 2014 at 12:54:38AM +0300, Sergei Shtylyov wrote:
> Hello.
> 
>    Here's the set of 2 patches against Simon Horman's 'renesas.git' repo,
> 'renesas-devel-v3.14-rc1-20140206v2' tag. Here we add the Ether device tree
> support working on the R8A7790/Lager reference board. The patchset requires the
> 'sh_eth' driver device tree support posted earlier in order to work.
> 
> [1/2] ARM: shmobile: r8a7790: add Ether DT support
> [2/2] ARM: shmobile: lager: add Ether DT support

Thanks. Please repost this series once the bindings have been accepted.

^ permalink raw reply

* [PATCH] ARM: shmobile: r8a7778 dtsi: Remove duplicate i2c nodes
From: Simon Horman @ 2014-02-13  5:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1392039739-22571-1-git-send-email-geert+renesas@linux-m68k.org>

On Mon, Feb 10, 2014 at 02:42:19PM +0100, Geert Uytterhoeven wrote:
> "ARM: shmobile: r8a7778: add I2C support on DTSI" was applied twice:
> 
>     commit ae4273ec7b25c8b9c895a4aae31f2fced980b7bf
>     commit 3acb51b9215bd99da403ecf8200f8425176b1926
> 
> Signed-off-by: Geert Uytterhoeven <geert+renesas@linux-m68k.org>

Thanks, I have queued this up.

^ permalink raw reply


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