Расширенный моддинг MMC
Способ подключения
![]() |
![]() |
Схема подключения успешно опробована на шине питания GPIO = 2.6V
Существует два способа модификации с использованием разных GPIO для CS1, а также два варианта установки необходимого ПО для непосредственного использования карточки памяти подключенной к устройству.
Примечание: Эта модификация, как и модификация mmc_over_gpio - не поддерживает SDHC карточки памяти на уровне драйвера.
Более подробно мод расписан в соседней английской версии.
Подключение MMC/SD карты памяти к шине spi0.1
Основные изменения в прошивке
<HTML><p style=“border:2px dashed red;”>Первый способ.</p></HTML>
Вариант GPIO как CS1
В качестве CS для MMC/SD карты памяти, предпочтительнее использовать любой свободный GPIO пин.
Использование GPIO 7 - как CS1 для общей шины:
Index: target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c =================================================================== --- target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c (revision 34914) +++ target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c (working copy) @@ -50,7 +50,7 @@ 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); Index: target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c =================================================================== --- target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c (revision 34914) +++ target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c (working copy) @@ -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> @@ -16,12 +19,14 @@ #include "dev-ap9x-pci.h" #include "dev-gpio-buttons.h" #include "dev-leds-gpio.h" +#include "dev-spi.h" #include "dev-m25p80.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,26 @@ #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_data = { + .get_ro = NULL, + .get_cd = NULL, + .detect_delay = 100, /* msecs */ + .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, +}; + +static struct spi_board_info ath79_spi_info[] = { + { + .bus_num = 0, + .chip_select = 1, + .max_speed_hz = 25000000, + .modalias = "mmc_spi", + .platform_data = &ath79_mmc_data, + .controller_data = &ath79_spi1_cdata, + } +}; + static const char *tl_mr3x20_part_probes[] = { "tp-link", NULL, @@ -97,6 +122,9 @@ ath79_register_eth(0); ap91_pci_init(ee, mac); + + spi_register_board_info(ath79_spi_info, + ARRAY_SIZE(ath79_spi_info)); } static void __init tl_mr3x20_usb_setup(void)
<HTML><p style=“border:2px dashed red;”>Второй способ.</p></HTML>
Вариант Internal CS1
Internal CS | AR724X | AR933X |
---|---|---|
CS1 | QSS LED (GPIO0) | UART In (GPIO9) |
CS2 | SYS LED (GPIO1) | UART Out (GPIO10) |
На устройствах AR724X: Для того, чтобы использовать внутренний CS1 - необходимо отключить QSS LED.
На устройствах AR933X: Для того, чтобы использовать внутренний CS1 - необходимо отключить UART.
Index: target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c =================================================================== --- target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c (revision 34914) +++ target/linux/ar71xx/files/arch/mips/ath79/dev-m25p80.c (working copy) @@ -50,7 +50,7 @@ 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); Index: target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c =================================================================== --- target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c (revision 34914) +++ target/linux/ar71xx/files/arch/mips/ath79/mach-tl-mr3x20.c (working copy) @@ -9,13 +9,19 @@ */ #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-spi.h" #include "dev-m25p80.h" #include "dev-usb.h" #include "machtypes.h" @@ -32,6 +38,26 @@ #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_data = { + .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, +}; + +static struct spi_board_info ath79_spi_info[] = { + { + .bus_num = 0, + .chip_select = 1, + .max_speed_hz = 25000000, + .modalias = "mmc_spi", + .platform_data = &ath79_mmc_data, + .controller_data = &ath79_spi1_cdata, + } +}; + static const char *tl_mr3x20_part_probes[] = { "tp-link", NULL, @@ -80,6 +106,9 @@ 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); ath79_register_gpio_keys_polled(-1, TL_MR3X20_KEYS_POLL_INTERVAL, @@ -97,6 +126,9 @@ ath79_register_eth(0); ap91_pci_init(ee, mac); + + spi_register_board_info(ath79_spi_info, + ARRAY_SIZE(ath79_spi_info)); } static void __init tl_mr3x20_usb_setup(void)
Переключение GPIO0 (QSS LED) в CS1 на AR724X:
/* Enabling internal CS1, disable GPIO 0 */ ath79_gpio_function_enable(AR724X_GPIO_FUNC_SPI_CS_EN1);
(Уже используется в верхнем примере.)
Отключение UART и переключение GPIO 9(Rx) в CS1 на AR933X:
/* 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);
Реализация подключение/извлечение SD-карточки памяти
Используется SD Socket/SD card holder/SD Memory Card Connector с дополнительными соответствующими контактами/кнопками CD (Card Detect) и WP (Write Protect)
Всего используем два свободных GPIO7→CS1, GPIO18→CD. Создаем дополнительную кнопку по примеру:
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..892d66f 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,45 @@ #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, +}; + +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 +99,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 = KEY_MMC_BUTTON, + .debounce_interval = TL_MR3X20_KEYS_DEBOUNCE_INTERVAL, + .gpio = TL_MR3X20_GPIO_BTN_MMC, + .active_low = 1, } }; @@ -97,6 +131,9 @@ static void __init tl_ap99_setup(void) ath79_register_eth(0); ap91_pci_init(ee, mac); + + spi_register_board_info(ath79_spi_info, + ARRAY_SIZE(ath79_spi_info)); } static void __init tl_mr3x20_usb_setup(void)
После компиляции, создана дополнительная кнопка GPIO18→mmc. Далее, необходимо создать конфигурацию для кнопки:
uci add system button uci set system.@button[-1].button=mmc uci set system.@button[-1].action=pressed uci set system.@button[-1].handler='echo "spi0.1" > /sys/bus/spi/drivers/mmc_spi/bind' uci add system button uci set system.@button[-1].button=mmc uci set system.@button[-1].action=released uci set system.@button[-1].handler='echo "spi0.1" > /sys/bus/spi/drivers/mmc_spi/unbind' uci commit system
Примечание: До извлечения SD-карты памяти, рекомендуется использовать команды sync и unmount <раздел>
- для правильного размонтирования разделов SD-карты памяти, т.к. при извлечении карты памяти (после записи на нее), некоторые данные могут остаться в буфере.
Подключение/извлечение SD карты памяти:
<HTML> <pre class=“code”> ... [ 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 <b><font size=4>[ 1980.340000] mmc0: SPI card removed</font></b> [ 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 ... </pre> </HTML>
Модули
<HTML><p style=“border:2px dashed red;”>Первый вариант.</p></HTML>
Использование внешних модулей
kmod-mmc
и kmod-mmc-spi
- пока не позволяет корректно смонтировать /overlay
или /
разделы через extroot_configuration, данная ошибка описана в https://dev.openwrt.org/ticket/7768. Это даже с учетом того, что использование этих модулей, позволяет полноценно использовать карточку памяти и монтировать ее в другие точки монтирования (кроме extroot_configuration).
Обновление: Проблема сохранилась по состоянию ревизии прошивки r37142.
Для extroot_configuration, используйте Второй вариант.
<HTML><p style=“border:2px dashed red;”>Второй вариант.</p></HTML>
На уровне ядра (kernel):
Index: target/linux/ar71xx/config-3.7 =================================================================== --- target/linux/ar71xx/config-3.7 (revision 35363) +++ target/linux/ar71xx/config-3.7 (working copy) @@ -240,6 +240,10 @@ # CONFIG_SPI_RB4XX is not set # CONFIG_SPI_RB4XX_CPLD is not set # CONFIG_SPI_VSC7385 is not set +CONFIG_EXT4_FS=y +CONFIG_MMC=y +CONFIG_MMC_SPI=y +CONFIG_MMC_BLOCK=y CONFIG_SWCONFIG=y CONFIG_SWCONFIG_LEDS=y CONFIG_SYS_HAS_CPU_MIPS32_R2=y
Помимо MMC модулей, здесь дополнительно включена возможность использовать EXT4 файловую систему на уровне ядра.
Также следует учесть, что Linux-ядро регулярно обновляется, а этот конфигурационный файл в названии носит версию ядра.
В конечном счете, если ядро Trank'а обновилось, необходимо найти этот же конфиг. файл (но уже новый, например: config-3.8
) и изменить его в соответствии с представленными здесь изменениями.
Интеграция необходимых модулей в ядро, позволяет использовать обнаруженную карточку памяти на ровне с флеш-памятью.
При этом, можно использовать карточку памяти в качестве расширенной файловой системы - extroot_configuration.
Измерения
Тест скоростей spi0.1:
Скорость чтения/записи утилитой 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:~#
Результат утилиты 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 во время чтения/записи:
<HTML> <pre class=“code”> root@OpenWrt:~# top Mem: 28224K used, 1120K free, 0K shrd, 5784K buff, 5592K cached CPU: 0% usr <font color=“red”>99% sys</font> 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]
... </pre> </HTML>
Итог: Скорость чтения/записи по шине SPI, прямо зависит от частоты процессора, т.к. используется Bit-banging.
Тест одновременного использования spi0.0 и spi0.1:
Здесь стоит уточнить способ выполнения теста.
Посылается две параллельные команды чтение\запись с разделительным command оператором “&
” (запуск команды в фоне).
Результат следующий: при одновременном чтении или записи spi0.0 и spi0.1 (процесс не имеет значения), предпочтение дается шине spi0.1, т.е. пока не закончится операция чтении/записи блока на шине spi0.1 - на шине spi0.0 не начнется запрашиваемая операция, выполнение происходит в приоритетном порядке и не является ошибкой.
Питание:
Режим | Значение |
---|---|
Спящий режим (простой) | ~1mA |
Чтение данных | ~18mA |
Запись данных | ~23mA |
Логи
Лог загрузки и инициализации карточки:
<HTML> <pre class=“code”> ... <b><font size=4>[ 0.730000] ath79-spi ath79-spi: master is unqueued, this is deprecated</font></b> [ 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] <b><font size=4>[ 3.050000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff</font></b> [ 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 <b><font size=4>[ 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</font></b> ... </pre> </HTML>
Вид ошибки в случае НЕ обнаружения карточки памяти на уровне ядра:
... [ 6.070000] mmc_spi spi0.1: SD/MMC host mmc0, no DMA, no WP, no poweroff ... [ 8.710000] mmc0: error -145 whilst initialising SD card ... [ 10.710000] mmc0: error -145 whilst initialising MMC card ...
Обнаруженные разделы:
<HTML> <pre class=“code”> root@OpenWrt:~# ls /dev/mmc* <font color=“magenta”>/dev/mmcblk0 /dev/mmcblk0p1</font> root@OpenWrt:~# </pre> </HTML>
Debug информация:
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:~#