Deep MMC Mod

Cleanup Required!
This page or section needs cleanup. You can edit this page to fix wiki markup, redundant content or outdated information.

This mod does not work on the older LEDE and Chaos Calmer 15.05 (since the revision r47045). But it works completely on the newest kernel 4.14+(ath79) and does not need to delete anything.
If you use older version of branch then you need to discard/remove this three changes to work property with this mod:

git rm target/linux/generic/patches-3.18/092-01-spi-Check-to-see-if-the-device-is-processing-a-messa.patch
git rm target/linux/generic/patches-3.18/092-02-spi-Pump-transfers-inside-calling-context-for-spi_sy.patch
git rm target/linux/generic/patches-3.18/092-03-spi-Only-idle-the-message-pump-in-the-worker-kthread.patch

Note: Instead of path patches-3.18, new versions already use pending-3.18 path. You can also do remove those patches in same way for other branches…


Or you can use old Barrier Breaker 14.07 branch or Chaos Calmer 15.05 branch (up to revision r47042).

For example, to reset trunk branch to old revision r47042, use this command:

git reset --hard d8d8d594442086ce07b0401d472468b0c6a3c4ce

You can also do this in same way for other branches…

(Where the long parameter is a commit of necessary revision (git log −−grep 47042)).

Scheme for First way
First connection scheme
This connection scheme was successfully tested on TP-LINK MR3220 v1 device
(NO GPIO needed for this scheme, but this circuit using Inverter Gate IC: 74LVC1G04 or 74LVC1G14)
Two schems below for Second or Third way
Second connection scheme
This connection scheme was successfully tested on TP-LINK MR3220 v1 device
(with GPIO power bus = 2.6V)
Third connection scheme
This connection scheme was successfully tested on TP-LINK WR841N v7.1 device
(with GPIO power bus = FIXME)

There are three ways to modify the device and the source code for this device by using the same(common) CS0 and share it with SD-card /or/ any unused GPIO as CS1 /or/ Internal CS1 (it used by SPI controller).
And two ways for installing the required modules for detect and use the SD memory card is connected to the SPI bus of device.

At software level, this modification is almost no different from mmc_over_gpio, the only difference in the Independent slave SPI configuration of controller - that means we are using CS0, CS1…CSn line for each slave SPI-device.
On any platform with Atheros SoC we can see serial NOR-flash chip which is connected to SPI controller and it is used as primary boot device.

How to build your own firmware is covered here: toolchain.
If you already have experience with compile own firmware, and you want to easy apply these changes as patch(es), you can just use this Linux console command:

Svn patch
file format:
patch -u -p0 < patchfile.patch
Git patch
file format:
git apply patchfile.patch

OR

patch -u -p1 < patchfile.patch

Important! Where patchfile.patch must be placed in the trunk or other branches directory.

Note: Keep in mind, very often the unused GPIOs a pulled-down to the ground or pulled-up to the power bus via resistor - this may affect on detection of SD memory cards.



Important! Using this method, you need to purchase a Single (or Schmitt-Trigger) Inverter Gate (which can operates on voltage 3.3V, 74LVC1G04 or 74LVC1G14 was tested and it works).

Main changes in the source code of kernel:

Old patch for Openwrt (before LEDE project)

Old patch for Openwrt (before LEDE project)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index 9323b31..2c51142 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -50,7 +50,7 @@ static struct ath79_spi_platform_data ath79_spi_data;
 void __init ath79_register_m25p80(struct flash_platform_data *pdata)
 {
 	ath79_spi_data.bus_num = 0;
-	ath79_spi_data.num_chipselect = 1;
+	ath79_spi_data.num_chipselect = 2;
 	ath79_spi0_cdata.is_flash = true;
 	ath79_spi_info[0].platform_data = pdata;
 	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
index 5924ac5..5961732 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
@@ -9,6 +9,9 @@
  */
 
 #include <linux/gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
 
 #include <asm/mach-ath79/ath79.h>
 
@@ -17,6 +20,7 @@
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-m25p80.h"
+#include "dev-spi.h"
 #include "dev-usb.h"
 #include "machtypes.h"
 
@@ -32,6 +36,37 @@
 #define TL_MR3X20_KEYS_POLL_INTERVAL	20	/* msecs */
 #define TL_MR3X20_KEYS_DEBOUNCE_INTERVAL (3 * TL_MR3X20_KEYS_POLL_INTERVAL)
 
+static struct mmc_spi_platform_data ath79_mmc_pdata = {
+	.detect_delay	= 250, /* card detection delay in msec */
+	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
+};
+
+
+/*
+ *  Need to change cs_line to 2 (for AR72xx, AR933x)
+ *  or 5 (for AR934x, QCA95xx), this GPIO num. is equivalent of
+ *  internal CS0 line.
+ *  In its core, this is a dummy, because internal CS0 line
+ *  is locked by HW and SPI driver, and we can not use it at all.
+ *  We use this to simply switch SPI bus to another device.
+ */
+static struct ath79_spi_controller_data ath79_spi1_cdata = {
+	.cs_type = ATH79_SPI_CS_TYPE_GPIO,
+	.cs_line = 2,
+	.is_flash = false,
+};
+
+static struct spi_board_info ath79_spi_info[] __initdata = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 1,
+		.max_speed_hz	= 25000000,
+		.modalias	= "mmc_spi",
+		.platform_data	= &ath79_mmc_pdata,
+		.controller_data = &ath79_spi1_cdata,
+	}
+};
+
 static const char *tl_mr3x20_part_probes[] = {
 	"tp-link",
 	NULL,
@@ -82,6 +117,9 @@ static void __init tl_ap99_setup(void)
 
 	ath79_register_m25p80(&tl_mr3x20_flash_data);
 
+	spi_register_board_info(ath79_spi_info,
+				ARRAY_SIZE(ath79_spi_info));
+
 	ath79_register_gpio_keys_polled(-1, TL_MR3X20_KEYS_POLL_INTERVAL,
 					 ARRAY_SIZE(tl_mr3x20_gpio_keys),
 					 tl_mr3x20_gpio_keys);

Patch for Openwrt/LEDE (4.9, ar71xx platform, mach-*.c files)

Patch for Openwrt/LEDE (4.9, ar71xx platform, mach-*.c files)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index e53d97d..269e469 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -7,8 +7,10 @@
  */
 
 #include <linux/init.h>
+#include <linux/mmc/host.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
+#include <linux/spi/mmc_spi.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/concat.h>
@@ -16,6 +18,11 @@
 #include "dev-spi.h"
 #include "dev-m25p80.h"
 
+static struct mmc_spi_platform_data ath79_mmc_data = {
+	.detect_delay = 100, /* msecs */
+	.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+};
+
 static struct spi_board_info ath79_spi_info[] = {
 	{
 		.bus_num	= 0,
@@ -27,18 +34,25 @@ static struct spi_board_info ath79_spi_info[] = {
 		.bus_num	= 0,
 		.chip_select	= 1,
 		.max_speed_hz   = 25000000,
-		.modalias	= "m25p80",
+		.modalias	= "mmc_spi",
+		.platform_data	= &ath79_mmc_data,
 	}
 };
 
+static int device_spi_cs_gpios[2] = {
+	-ENOENT,
+	2, /* GPIO Number */
+};
+
 static struct ath79_spi_platform_data ath79_spi_data;
 
 void __init ath79_register_m25p80(struct flash_platform_data *pdata)
 {
 	ath79_spi_data.bus_num = 0;
-	ath79_spi_data.num_chipselect = 1;
+	ath79_spi_data.num_chipselect = 2;
+	ath79_spi_data.cs_gpios = device_spi_cs_gpios;
 	ath79_spi_info[0].platform_data = pdata;
-	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
+	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 2);
 }
 
 static struct flash_platform_data *multi_pdata;

Patch for Openwrt (4.14+, ath79 platform, dts files)

Patch for Openwrt (4.14+, ath79 platform, dts files)

diff --git a/target/linux/ath79/dts/ar7241_tp-link.dtsi b/target/linux/ath79/dts/ar7241_tp-link.dtsi
index 3846d811ac..d845e06951 100644
--- a/target/linux/ath79/dts/ar7241_tp-link.dtsi
+++ b/target/linux/ath79/dts/ar7241_tp-link.dtsi
@@ -48,7 +48,19 @@
 
 &spi {
 	status = "okay";
-	num-cs = <1>;
+	num-cs = <2>;
+	/*
+	 * First value is default pin for flash, second value
+	 * (via comma) the GPIO number for sdcard. But we used
+	 * gpio 2 and it's equivalent of internal CS0 line. And
+	 * we use it because we use common bus SPI0.0 for two
+	 * devices (default Low polarity chip-select, custom
+	 * High polarity chip-select).
+	 * In its core, this is a dummy, because internal CS0 line
+	 * is locked by HW and SPI driver, and we can not use it at all.
+	 * We use this to simply switch SPI bus to another device.
+	 */
+	cs-gpios = <0>, <&gpio 2 GPIO_ACTIVE_HIGH>;
 
 	flash@0 {
 		#address-cells = <1>;
@@ -80,6 +92,13 @@
 			};
 		};
 	};
+
+	sdcard@0 {
+		compatible = "mmc-spi-slot";
+		reg = <1>;
+		spi-max-frequency = <25000000>;
+		voltage-ranges = <3200 3400>;
+	};
 };
 
 &pcie {



Recommended to use any unused GPIO-pin as CS1 for MMC/SD memory card.

Main changes in the source code of kernel:

Old patch for Openwrt (before LEDE project)

Old patch for Openwrt (before LEDE project)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index e5831d4..80760d5 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -50,7 +50,7 @@ static struct ath79_spi_platform_data ath79_spi_data;
 void __init ath79_register_m25p80(struct flash_platform_data *pdata)
 {
 	ath79_spi_data.bus_num = 0;
-	ath79_spi_data.num_chipselect = 1;
+	ath79_spi_data.num_chipselect = 2;
 	ath79_spi0_cdata.is_flash = true;
 	ath79_spi_info[0].platform_data = pdata;
 	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
index 5924ac5..4d4ac42 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
@@ -9,6 +9,9 @@
  */
 
 #include <linux/gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
 
 #include <asm/mach-ath79/ath79.h>
 
@@ -17,11 +20,13 @@
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-m25p80.h"
+#include "dev-spi.h"
 #include "dev-usb.h"
 #include "machtypes.h"
 
 #define TL_MR3X20_GPIO_LED_QSS		0
 #define TL_MR3X20_GPIO_LED_SYSTEM	1
+#define TL_MR3X20_GPIO_CS1_MMC		7
 #define TL_MR3X20_GPIO_LED_3G		8
 
 #define TL_MR3X20_GPIO_BTN_RESET	11
@@ -32,6 +37,28 @@
 #define TL_MR3X20_KEYS_POLL_INTERVAL	20	/* msecs */
 #define TL_MR3X20_KEYS_DEBOUNCE_INTERVAL (3 * TL_MR3X20_KEYS_POLL_INTERVAL)
 
+static struct mmc_spi_platform_data ath79_mmc_pdata = {
+	.detect_delay	= 250, /* card detection delay in msec */
+	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
+};
+
+static struct ath79_spi_controller_data ath79_spi1_cdata = {
+	.cs_type = ATH79_SPI_CS_TYPE_GPIO,
+	.cs_line = TL_MR3X20_GPIO_CS1_MMC,
+	.is_flash = false,
+};
+
+static struct spi_board_info ath79_spi_info[] __initdata = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 1,
+		.max_speed_hz	= 25000000,
+		.modalias	= "mmc_spi",
+		.platform_data	= &ath79_mmc_pdata,
+		.controller_data = &ath79_spi1_cdata,
+	}
+};
+
 static const char *tl_mr3x20_part_probes[] = {
 	"tp-link",
 	NULL,
@@ -82,6 +109,9 @@ static void __init tl_ap99_setup(void)
 
 	ath79_register_m25p80(&tl_mr3x20_flash_data);
 
+	spi_register_board_info(ath79_spi_info,
+				ARRAY_SIZE(ath79_spi_info));
+
 	ath79_register_gpio_keys_polled(-1, TL_MR3X20_KEYS_POLL_INTERVAL,
 					 ARRAY_SIZE(tl_mr3x20_gpio_keys),
 					 tl_mr3x20_gpio_keys);

Patch for Openwrt (4.14+, ath79 platform, dts files)

Patch for Openwrt (4.14+, ath79 platform, dts files)

diff --git a/target/linux/ath79/dts/ar7241_tp-link.dtsi b/target/linux/ath79/dts/ar7241_tp-link.dtsi
index 3846d811ac..58399eb4de 100644
--- a/target/linux/ath79/dts/ar7241_tp-link.dtsi
+++ b/target/linux/ath79/dts/ar7241_tp-link.dtsi
@@ -48,7 +48,12 @@
 
 &spi {
 	status = "okay";
-	num-cs = <1>;
+	num-cs = <2>;
+	/*
+	 * Where first parameter <0> is default CS0 pin for flash
+	 * and second parameter is GPIO Number for sdcard.
+	 */
+	cs-gpios = <0>, <&gpio 7 GPIO_ACTIVE_HIGH>;
 
 	flash@0 {
 		#address-cells = <1>;
@@ -80,6 +85,13 @@
 			};
 		};
 	};
+
+	sdcard@0 {
+		compatible = "mmc-spi-slot";
+		reg = <1>;
+		spi-max-frequency = <25000000>;
+		voltage-ranges = <3200 3400>;
+	};
 };
 
 &pcie {



Internal CS AR71XX AR724X AR933X AR934X
CS1 usualy LED or BUTTON (GPIO0) UART In (GPIO9) any unused GPIOs(11-22)
(Software configurable
multiplexer)
CS2 usualy LED or BUTTON (GPIO1) UART Out (GPIO10)

On platforms with AR71XX SoC: If you want to use the internal CS1 - you need to turn off GPIO0.

On platforms with AR724X SoC: If you want to use the internal CS1 - you need to turn off GPIO0.

On platforms with AR933X SoC: If you want to use the internal CS1 - you need to turn off UART.

On platforms with AR934X SoC: If you want to use the internal CS1 - you need to configure GPIO to CS1.

Main changes in the source code of kernel:

Old patch for Openwrt (before LEDE project)

Old patch for Openwrt (before LEDE project)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index e5831d4..80760d5 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -50,7 +50,7 @@ static struct ath79_spi_platform_data ath79_spi_data;
 void __init ath79_register_m25p80(struct flash_platform_data *pdata)
 {
 	ath79_spi_data.bus_num = 0;
-	ath79_spi_data.num_chipselect = 1;
+	ath79_spi_data.num_chipselect = 2;
 	ath79_spi0_cdata.is_flash = true;
 	ath79_spi_info[0].platform_data = pdata;
 	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
index 5924ac5..1c6428f 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
@@ -9,19 +9,26 @@
  */
 
 #include <linux/gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
 
 #include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
 
+#include "common.h"
 #include "dev-eth.h"
 #include "dev-ap9x-pci.h"
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-m25p80.h"
+#include "dev-spi.h"
 #include "dev-usb.h"
 #include "machtypes.h"
 
 #define TL_MR3X20_GPIO_LED_QSS		0
 #define TL_MR3X20_GPIO_LED_SYSTEM	1
+#define TL_MR3X20_GPIO_CS1_MMC		7
 #define TL_MR3X20_GPIO_LED_3G		8
 
 #define TL_MR3X20_GPIO_BTN_RESET	11
@@ -32,6 +39,28 @@
 #define TL_MR3X20_KEYS_POLL_INTERVAL	20	/* msecs */
 #define TL_MR3X20_KEYS_DEBOUNCE_INTERVAL (3 * TL_MR3X20_KEYS_POLL_INTERVAL)
 
+static struct mmc_spi_platform_data ath79_mmc_pdata = {
+	.detect_delay	= 250, /* card detection delay in msec */
+	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
+};
+
+static struct ath79_spi_controller_data ath79_spi1_cdata = {
+	.cs_type = ATH79_SPI_CS_TYPE_INTERNAL,
+	.cs_line = 1,
+	.is_flash = false,
+};
+
+static struct spi_board_info ath79_spi_info[] __initdata = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 1,
+		.max_speed_hz	= 25000000,
+		.modalias	= "mmc_spi",
+		.platform_data	= &ath79_mmc_pdata,
+		.controller_data = &ath79_spi1_cdata,
+	}
+};
+
 static const char *tl_mr3x20_part_probes[] = {
 	"tp-link",
 	NULL,
@@ -80,8 +109,14 @@ static void __init tl_ap99_setup(void)
 	u8 *mac = (u8 *) KSEG1ADDR(0x1f01fc00);
 	u8 *ee = (u8 *) KSEG1ADDR(0x1fff1000);
 
+	/* Enabling internal CS1, disable GPIO 0 */
+	ath79_gpio_function_enable(AR724X_GPIO_FUNC_SPI_CS_EN1);
+
 	ath79_register_m25p80(&tl_mr3x20_flash_data);
 
+	spi_register_board_info(ath79_spi_info,
+				ARRAY_SIZE(ath79_spi_info));
+
 	ath79_register_gpio_keys_polled(-1, TL_MR3X20_KEYS_POLL_INTERVAL,
 					 ARRAY_SIZE(tl_mr3x20_gpio_keys),
 					 tl_mr3x20_gpio_keys);

Patch for Openwrt (4.14+, ath79 platform, dts files)

Patch for Openwrt (4.14+, ath79 platform, dts files)

diff --git a/target/linux/ath79/dts/ar7241_tp-link.dtsi b/target/linux/ath79/dts/ar7241_tp-link.dtsi
index 3846d811ac..ea915872dc 100644
--- a/target/linux/ath79/dts/ar7241_tp-link.dtsi
+++ b/target/linux/ath79/dts/ar7241_tp-link.dtsi
@@ -48,7 +48,10 @@
 
 &spi {
 	status = "okay";
-	num-cs = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&enable_cs1_bit>;
+	num-cs = <2>;
+	cs-gpios = <0>, <0>;
 
 	flash@0 {
 		#address-cells = <1>;
@@ -80,6 +83,30 @@
 			};
 		};
 	};
+
+	sdcard@0 {
+		compatible = "mmc-spi-slot";
+		reg = <1>;
+		spi-max-frequency = <25000000>;
+		voltage-ranges = <3200 3400>;
+	};
+};
+
+&pinmux {
+	enable_cs1_bit: pinmux_enable_cs1_bit {
+		pinctrl-single,bits = <
+			/*
+			 * Set AR724X_GPIO_FUNC_SPI_CS_EN1 BIT(13) to 1,
+			 * i.e. enable hardware CS1 but it turn-off GPIO0;
+			 * Formula: *addr = (*addr & ~sub-mask) |
+			 * (value & sub-mask);
+			 * Where: addr is 0x18000000 + 0x40000 + 0x28 is
+			 * base address of AR724X GPIO functions;
+			 * <addr_offset value sub-mask>
+			 */
+			0x0 0x2000 0x2000
+		>;
+	};
 };
 
 &pcie {


Switching GPIO0 to CS1 on AR71XX SoC:

Instruction for old Openwrt (including LEDE project)

Instruction for old Openwrt (including LEDE project)

	/* Enabling internal CS1, disable GPIO 0 */
	ath79_gpio_function_enable(AR71XX_GPIO_FUNC_SPI_CS1_EN);


Switching GPIO0 to CS1 on AR724X SoC:

Instruction for old Openwrt (including LEDE project)

Instruction for old Openwrt (including LEDE project)

	/* Enabling internal CS1, disable GPIO 0 */
	ath79_gpio_function_enable(AR724X_GPIO_FUNC_SPI_CS_EN1);

Instruction for Openwrt (4.14+, ath79 platform, dts files)

Instruction for Openwrt (4.14+, ath79 platform, dts files)

	...
	...
	/* Add this two lines to spi enum */
	pinctrl-names = "default";
	pinctrl-0 = <&enable_cs1_bit>;
	...
	...
&pinmux {
	enable_cs1_bit: pinmux_enable_cs1_bit {
		pinctrl-single,bits = <
			/*
			 * Set AR724X_GPIO_FUNC_SPI_CS_EN1 BIT(13) to 1,
			 * i.e. enable hardware CS1 but it turn-off GPIO0;
			 * Formula: *addr = (*addr & ~sub-mask) |
			 * (value & sub-mask);
			 * Where: addr is 0x18000000 + 0x40000 + 0x28 is
			 * base address of AR724X GPIO functions;
			 * <addr_offset value sub-mask>
			 */
			0x0 0x2000 0x2000
		>;
	};
 };

Note: Already used in the example above.

Turn off UART and switching GPIO9(Rx) to CS1 on AR933X SoC:

Instruction for old Openwrt (including LEDE project)

Instruction for old Openwrt (including LEDE project)

	/* Disable UART, enabling GPIO 9 and GPIO 10 */
	ath79_gpio_function_disable(AR933X_GPIO_FUNC_UART_EN);
	/* Enabling internal CS1, disable GPIO 9 */
	ath79_gpio_function_enable(AR933X_GPIO_FUNC_SPI_CS_EN1);


Configure GPIO11 to CS1 on AR934X SoC:

Instruction for old Openwrt (including LEDE project)

Instruction for old Openwrt (including LEDE project)

	/* We chose GPIO11 - for this example. You can use any unused GPIOs(11-22) */
	#define DEVICE_GPIO_CS1_MMC	11
	/* custom register for CS2 (AR934X SoC), TODO in future ar71xx_regs.h */
	#define AR934X_GPIO_OUT_SPI_CS2		8
	...
	/* Configure GPIO to MUX Select (CS1) */
	ath79_gpio_output_select(DEVICE_GPIO_CS1_MMC, AR934X_GPIO_OUT_SPI_CS1);

Instruction for Openwrt (4.14+, ath79 platform, dts files)

Instruction for Openwrt (4.14+, ath79 platform, dts files)

	...
	...
	/* Add this two lines to spi enum */
	pinctrl-names = "default";
	pinctrl-0 = <&gpio11_to_cs1_bit>;
	...
	...
&pinmux {
	gpio11_to_cs1_bit: pinmux_gpio11_to_cs1_bit {
		pinctrl-single,bits = <
			/*
			 * Set AR934X_GPIO_OUT_SPI_CS1 7 value to GPIO11,
			 * i.e. enable hardware CS1 but it turn-off GPIO11;
			 * Formula: *addr = (*addr & ~sub-mask) |
			 * (value & sub-mask);
			 * Where: addr is 0x18000000 + 0x40000 + 0x2C is
			 * base address of AR934X GPIO Output functions;
			 * <addr_offset value sub-mask>;
			 */
			/*
			 * 0x2c+0x8 addr_offset for GPIO11(Bit 31:24);
			 * 0x2c+0xc addr_offset for GPIO12-GPIO15;
			 * 0x2c+0x10 addr_offset for GPIO16-GPIO19;
			 * 0x7 value (with Bitwise Left Shift) for mux CS1
			 * use 0x8 value for mux CS2;
			 */
			0x8 0x7000000 0xff000000
		>;
	};
 };



Connection scheme
We use SD Socket/SD card holder/SD Memory Card Connector with special integrated buttons -
CD (Card Detect) and WP (Write Protect)

Total we use two unused GPIOs: GPIO7→CS1, GPIO18→CD.
Create a new additional button on this example:

Old patch for Openwrt (before LEDE project)

Old patch for Openwrt (before LEDE project)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
index e5831d4..80760d5 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c
@@ -50,7 +50,7 @@ static struct ath79_spi_platform_data ath79_spi_data;
 void __init ath79_register_m25p80(struct flash_platform_data *pdata)
 {
 	ath79_spi_data.bus_num = 0;
-	ath79_spi_data.num_chipselect = 1;
+	ath79_spi_data.num_chipselect = 2;
 	ath79_spi0_cdata.is_flash = true;
 	ath79_spi_info[0].platform_data = pdata;
 	ath79_register_spi(&ath79_spi_data, ath79_spi_info, 1);
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
index 5924ac5..9990d05 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c
@@ -9,6 +9,9 @@
  */
 
 #include <linux/gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
 
 #include <asm/mach-ath79/ath79.h>
 
@@ -17,21 +20,46 @@
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-m25p80.h"
+#include "dev-spi.h"
 #include "dev-usb.h"
 #include "machtypes.h"
 
 #define TL_MR3X20_GPIO_LED_QSS		0
 #define TL_MR3X20_GPIO_LED_SYSTEM	1
+#define TL_MR3X20_GPIO_CS1_MMC		7
 #define TL_MR3X20_GPIO_LED_3G		8
 
 #define TL_MR3X20_GPIO_BTN_RESET	11
 #define TL_MR3X20_GPIO_BTN_QSS		12
+#define TL_MR3X20_GPIO_BTN_MMC		18
 
 #define TL_MR3X20_GPIO_USB_POWER	6
 
 #define TL_MR3X20_KEYS_POLL_INTERVAL	20	/* msecs */
 #define TL_MR3X20_KEYS_DEBOUNCE_INTERVAL (3 * TL_MR3X20_KEYS_POLL_INTERVAL)
 
+static struct mmc_spi_platform_data ath79_mmc_pdata = {
+	.detect_delay	= 250, /* card detection delay in msec */
+	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
+};
+
+static struct ath79_spi_controller_data ath79_spi1_cdata = {
+	.cs_type = ATH79_SPI_CS_TYPE_GPIO,
+	.cs_line = TL_MR3X20_GPIO_CS1_MMC,
+	.is_flash = false,
+};
+
+static struct spi_board_info ath79_spi_info[] __initdata = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 1,
+		.max_speed_hz	= 25000000,
+		.modalias	= "mmc_spi",
+		.platform_data	= &ath79_mmc_pdata,
+		.controller_data = &ath79_spi1_cdata,
+	}
+};
+
 static const char *tl_mr3x20_part_probes[] = {
 	"tp-link",
 	NULL,
@@ -72,6 +100,13 @@ static struct gpio_keys_button tl_mr3x20_gpio_keys[] __initdata = {
 		.debounce_interval = TL_MR3X20_KEYS_DEBOUNCE_INTERVAL,
 		.gpio		= TL_MR3X20_GPIO_BTN_QSS,
 		.active_low	= 1,
+	}, {
+		.desc		= "mmc",
+		.type		= EV_KEY,
+		.code		= BTN_0,
+		.debounce_interval = TL_MR3X20_KEYS_DEBOUNCE_INTERVAL,
+		.gpio		= TL_MR3X20_GPIO_BTN_MMC,
+		.active_low	= 1,
 	}
 };
 
@@ -82,6 +117,11 @@ static void __init tl_ap99_setup(void)
 
 	ath79_register_m25p80(&tl_mr3x20_flash_data);
 
+	spi_register_board_info(ath79_spi_info,
+				ARRAY_SIZE(ath79_spi_info));
+
+	gpio_direction_input(TL_MR3X20_GPIO_BTN_MMC);
+
 	ath79_register_gpio_keys_polled(-1, TL_MR3X20_KEYS_POLL_INTERVAL,
 					 ARRAY_SIZE(tl_mr3x20_gpio_keys),
 					 tl_mr3x20_gpio_keys);

Patch for Openwrt (4.14+, ath79 platform, dts files)

Patch for Openwrt (4.14+, ath79 platform, dts files)

diff --git a/target/linux/ath79/dts/ar7241_tp-link.dtsi b/target/linux/ath79/dts/ar7241_tp-link.dtsi
index 3846d811ac..d99bafcb2b 100644
--- a/target/linux/ath79/dts/ar7241_tp-link.dtsi
+++ b/target/linux/ath79/dts/ar7241_tp-link.dtsi
@@ -22,13 +22,18 @@
 			gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
 			debounce-interval = <60>;
 		};
-
 		qss {
 			label = "qss";
 			linux,code = <KEY_WPS_BUTTON>;
 			gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
 			debounce-interval = <60>;
 		};
+		mmc {
+			label = "mmc";
+			linux,code = <BTN_0>;
+			gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+			debounce-interval = <60>;
+		};
 	};
 
 	gpio_leds: gpio-leds {
@@ -48,7 +53,12 @@
 
 &spi {
 	status = "okay";
-	num-cs = <1>;
+	num-cs = <2>;
+	/*
+	 * Where first parameter <0> is default CS0 pin for flash
+	 * and second parameter is GPIO Number for sdcard.
+	 */
+	cs-gpios = <0>, <&gpio 7 GPIO_ACTIVE_HIGH>;
 
 	flash@0 {
 		#address-cells = <1>;
@@ -80,6 +90,30 @@
 			};
 		};
 	};
+
+	sdcard@0 {
+		compatible = "mmc-spi-slot";
+		reg = <1>;
+		/*
+		 * If you need to use GPIO18 as Card Detect and
+		 * IRQ trigger (as standard implementation) - just
+		 * uncomment line below (but don't forget delete
+		 * the equivalent gpio-key). We don't use this
+		 * property because it doesn't detect removing of
+		 * SD Card.
+		 */
+		//gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+		/*
+		 * By default, Card Detect use "active low" state.
+		 * Using the "cd-inverted" property means, that the
+		 * CD line is active high, i.e. it is high, when a
+		 * card is inserted.
+		 * (uncomment line below to enable this option)
+		 */
+		//cd-inverted;
+		spi-max-frequency = <25000000>;
+		voltage-ranges = <3200 3400>;
+	};
 };
 
 &pcie {

We could use GPIO to IRQ event as card detection (implemented in mmc_spi module) or GPIO to IRQ event as new hotplug SPI-device (implemented in standart SPI bus driver) - but in all this cases, it was found that it not support hotplug detection with removing the SD-card or SPI-device via the IRQ trigger. IRQ trigger needed if your gpio controller could be in sleep/idle state (and those CD gpio switch could be ignored by the system) - in that cause use standard implementation(not buttons).
Or FIXME

The solution was found without the use of IRQ trigger: After build and compiling the firmware, we just added additional button with the name mmc.
Next, you need to create a configuration(/etc/hotplug.d/button/00-button) for the button mmc:

uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=pressed
uci set system.@button[-1].handler='echo "spi0.1" > /sys/bus/spi/drivers/mmc_spi/unbind'
uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=released
uci set system.@button[-1].handler='echo "spi0.1" > /sys/bus/spi/drivers/mmc_spi/bind'
uci commit system

Note: Action pressed/released dependent on default state of GPIO (Input), i.e., sometimes you need to swap actions. Before removing SD-card from the slot, it is recommended to use the console commands sync and unmount <partition> - for correctly unmounting of a partition. Because if we remove SD-card(after recording on it) without using unmount commands - we risk losing some not recorded data.

Log of hotplug process:
...
[ 1968.380000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff
[ 1968.550000] mmc0: SD Status: Invalid Allocation Unit size.
[ 1968.560000] mmc0: host does not support reading read-only switch. assuming write-enable.
[ 1968.570000] mmc0: new SD card on SPI
[ 1968.570000] mmcblk0: mmc0:0000 00000 1.90 GiB 
[ 1968.580000]  mmcblk0: p1
[ 1980.340000] mmc0: SPI card removed
[ 2008.900000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff
[ 2009.070000] mmc0: SD Status: Invalid Allocation Unit size.
[ 2009.080000] mmc0: host does not support reading read-only switch. assuming write-enable.
[ 2009.090000] mmc0: new SD card on SPI
[ 2009.090000] mmcblk0: mmc0:0000 00000 1.90 GiB
[ 2009.100000]  mmcblk0: p1
...
By using this variant - NO need to install external modules kmod-mmc, kmod-mmc-spi and kmod-fs-ext4.
You can use the SD memory card as extroot_configuration (block-mount) with no problem.
Integrating modules in kernel:

When we build own firmware we use standard menu make menuconfig, after that we need to use special menu make kernel_menuconfig.
In this kernel menu, select the asterisks following items:

(Use Arrows UP and DOWN to find and select)
	Device Drivers --->				(Press ENTER)
		<*> MMC/SD/SDIO card support ----	(Find this item and press SPACE twice from the <M> to turn the <*> and then press ENTER)
		 ├<*> MMC block device driver		(Find this item and press SPACE twice from the <M> to turn the <*>)
		 └<*> MMC/SD/SDIO over SPI		(Find this item and press SPACE twice from the <M> to turn the <*>)
(Press ESC twice to go to the previous menu)
	File systems --->				(Press ENTER)
		<*> The Extended 4 (ext4) filesystem	(Find this item and press SPACE twice from the <M> to turn the <*>)
(Press ESC twice)
(Use TAB or Arrows LEFT and RIGHT to select < Exit >)
	< Exit >
		< Yes >

Besides MMC modules, we include the EXT4 module on the kernel level.

The integration of the required modules into the kernel, allows us to use detected SD-card on one level with NOR flash memory. For example: we can mount root-fs at kernel level by setting: CONFIG_CMDLINE=“rootfstype=squashfs,jffs2,ext3 noinitrd root=/dev/mmcblk0p1 rootwait”

Second way, using external modules

Important! If we are using external modules kmod-mmc and kmod-mmc-spi - this does not allow to correctly mount /overlay
or / (root) partitions through extroot_configuration. This problem is described in this old ticket.
It's even considering that the use of these external modules, allow us to fully use the SD-card and mount it in the other mount points (except extroot_configuration).

Update: The problem remained in r37142 firmware revision.
For extroot_configuration, use First way.

(AR71xx) Speed testing spi0.1:

~ the same as AR724x tests

(AR724x) Speed testing spi0.1:

Read/write speed by using utility coreutils-dd:

root@OpenWrt:~# /usr/bin/dd count=14 bs=1M if=/dev/mmcblk0p1 of=/dev/null
14+0 records in
14+0 records out
14680064 bytes (15 MB) copied, 20.9663 s, 700 kB/s
root@OpenWrt:~# /usr/bin/dd count=14 bs=1M if=/dev/zero of=/dev/mmcblk0p1
14+0 records in
14+0 records out
14680064 bytes (15 MB) copied, 25.2994 s, 580 kB/s
root@OpenWrt:~#

Result hdparm:

root@OpenWrt:~# hdparm -Tt /dev/mmcblk0p1

/dev/mmcblk0p1:
 Timing cached reads:     2 MB in  2.96 seconds = 691.37 kB/sec
 Timing buffered disk reads:   2 MB in  3.05 seconds = 670.43 kB/sec
root@OpenWrt:~#
CPU load during read/write process:
root@OpenWrt:~# top
Mem: 28224K used, 1120K free, 0K shrd, 5784K buff, 5592K cached
CPU:   0% usr  99% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 1.21 0.88 0.60 2/60 28540
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
 2230     2 root     RW       0   0%  55% [kworker/u:2]
 2180     2 root     SW       0   0%  36% [kworker/u:0]
  559     2 root     RW       0   0%   6% [mmcqd/0]
...

Conclusion: operations read/write speed via [ath79-spi↔spi-bitbang↔mmc_spi], directly depends on the frequency of the processor, because is used Bit-banging method to read/write the word.

(AR933x) Speed testing spi0.1:

FIXME TODO

(AR934x) Speed testing spi0.1:

FIXME TODO

Access to spi0.0 and spi0.1 at the one time:

Here it is necessary to specify method of performing the test.
We send two parallel commands(operations) read\write command with the division operator & (execute the console command in the background).

Result next: while reading or writing spi0.0 and spi0.1 at the same time (the process does not matter), priority given to spi0.1 bus, i.e. until the end of read/write operation on the spi0.1 bus - on the spi0.0 bus will not begin the requested operation, all operations occurs as a priority and not a bug.

Power
Operation Probe
Idle ~1mA
Read data ~18mA
Write data ~23mA
Log with loading and initializing SD-card:
...
[    0.730000] ath79-spi ath79-spi: master is unqueued, this is deprecated
[    0.740000] m25p80 spi0.0: found en25f32, expected m25p80
[    0.750000] m25p80 spi0.0: en25f32 (4096 Kbytes)
[    0.750000] 5 tp-link partitions found on MTD device spi0.0
[    0.760000] Creating 5 MTD partitions on "spi0.0":
[    0.760000] 0x000000000000-0x000000020000 : "u-boot"
[    0.770000] 0x000000020000-0x00000013de00 : "kernel"
[    0.770000] mtd: partition "kernel" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only
[    0.790000] 0x00000013de00-0x0000003f0000 : "rootfs"
[    0.790000] mtd: partition "rootfs" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only
[    0.810000] mtd: partition "rootfs" set to be root filesystem
[    0.810000] mtd: partition "rootfs_data" created automatically, ofs=370000, len=80000
[    0.820000] 0x000000370000-0x0000003f0000 : "rootfs_data"
[    0.830000] 0x0000003f0000-0x000000400000 : "art"
[    0.840000] 0x000000020000-0x0000003f0000 : "firmware"
[    0.860000] libphy: ag71xx_mdio: probed
[    0.860000] eth0: Atheros AG71xx at 0xba000000, irq 5, mode:GMII
[    1.420000] eth0: Found an AR7240/AR9330 built-in switch
[    2.450000] eth1: Atheros AG71xx at 0xb9000000, irq 4, mode:MII
[    3.000000] ag71xx ag71xx.0 eth1: connected to PHY at ag71xx-mdio.1:04 [uid=004dd041, driver=Generic PHY]
[    3.050000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff
[    3.060000] TCP: cubic registered
[    3.060000] NET: Registered protocol family 17
[    3.070000] 8021q: 802.1Q VLAN Support v1.8
[    3.080000] VFS: Mounted root (squashfs filesystem) readonly on device 31:2.
[    3.090000] Freeing unused kernel memory: 268k freed
[    3.310000] mmc0: host does not support reading read-only switch. assuming write-enable.
[    3.320000] mmc0: new SD card on SPI
[    3.480000] mmcblk0: mmc0:0000 00000 1.90 GiB
[    3.490000]  mmcblk0: p1
...
Error type in the case that system has NOT detected the SD-card:
...
[    6.070000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff
...
[    8.710000] mmc0: error -22 whilst initialising SD card
...
[   10.710000] mmc0: error -22 whilst initialising MMC card
...
After detecting the SD-card we can find partitions:
root@OpenWrt:~# ls /dev/mmc*
/dev/mmcblk0    /dev/mmcblk0p1
root@OpenWrt:~#
Debug info:
root@OpenWrt:~# cat /sys/kernel/debug/mmc0/ios
clock:          25000000 Hz
vdd:            20 (3.2 ~ 3.3 V)
bus mode:       2 (push-pull)
chip select:    1 (active high)
power mode:     2 (on)
bus width:      0 (1 bits)
timing spec:    0 (legacy)
signal voltage: 1 (3.30 V)
root@OpenWrt:~#
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2021/10/15 09:06
  • by bobafetthotmail