Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Переключение «налету» между LAN-портами и SD-картой в микрокомпьютерах на базе SOC MT7688AN

Из документации на SOC MT7688AN следует, что линии портов ETH3, ETH4 мультиплексированы с линиями интерфейса SDXC, используемого для подключения SD-карты. Всё, в общем-то, довольно просто, но, как говорится, есть нюансы. Мы с Virtual-ом провели на эту тему небольшое исследование, по результатам которого я написал небольшую утилитку, позволяющую переключаться «налету» между SD-картой и LAN-портами. О результатах этой работы и пойдёт речь в сегодняшней статье.

Особенности мультиплексирования линий ETH (LAN-портов) и SDXC (SD-карты)

Итак, главный сюрприз при работе с портами ETH заключается в том, что независимый ETH-порт в контроллере всего один — это ETH0. Линии портов ETH1, ETH2, ETH3 и ETH4 могут переключаться между аналоговым режимом (который используется для ethernet) и цифровым режимом (который используется для GPIO, SDXC, UART2, PWM) только все вместе! То есть, несмотря на то, что линии интерфейса SDXC мультиплексированы только с линиями портов ETH3, ETH4, — вы не можете использовать одновременно, скажем, порт ETH2 и SD-карту, или, например, интерфейс UART2 и порт ETH1.

Именно этим объясняется существование у микрокомпьютеров SKW92 двух модификаций:

  • IoT — модификация, в которой доступен только порт ETH0 (используемый в качестве WAN), но при этом больше GPIO, есть интерфейсы SD-карты и UART2
  • Router — когда помимо ETH0 активны остальные 4 ETH-порта (используемые в этом случае для LAN), но меньше GPIO, а также нет интерфейсов SDXC и UART2.

Понятно, что в данном случае разделение на WAN и LAN — это всего лишь скайлабовская условность, но далее по ходу статьи я тоже буду пользоваться таким разделением ETH-портов на WAN (ETH0) и LAN (ETH1, ETH2, ETH3, ETH4), поскольку так удобнее и лучше подчёркивает неравнозначность ETH0 и остальных ETH-портов.

Кстати, ровно по этой же причине не существует модификаций микрокомпьютеров Omega2 или LinkIt Smart хотя бы с двумя ETH-портами, несмотря на то, что в самом SOC-контроллере их аж 5 штук. Просто неиспользуемые ETH-порты можно задействовать только все вместе, но тогда отвалится большое количество других интерфейсов.

Ну и как же быть, если очень хочется поиметь всё и сразу?

Алгоритм переключения «налету» между LAN-портами и SD-картой

Внимательное изучение документации на регистры MT7688AN показало, что за выбор режимов работы отвечает регистр AGPIO_CFG (0x1000003C) модуля SYSCTL (+ 0x10000000), а точнее биты AGPIO_CFG[17:20]. Так вот, согласно документации, если хотя бы один из этих четырёх битов установлен в единицу, то линии всех LAN-портов переключаются в цифровой режим (и тогда линии портов ETH3, ETH4 могут использоваться для подключения SD-карты). Соответственно, если все четыре бита сброшены в ноль, то линии всех LAN-портов переключаются в аналоговый режим, в котором к ним можно подключать согласующие трансформаторы, разъёмы RJ45 и кабели сети Ethernet.

Тут, кстати, важно понимать, что есть у вас к какому либо порту ETH подключены трансформаторы Ethernet, то при переключении соответствующих выводов контроллера в цифровой режим, легко можно устроить КЗ через этот самый трансформатор, если эти выводы каким-либо образом окажутся работающими на выход, а не на вход!

Дальнейшие исследования показали, что одного только сброса или установки битов в регистре AGPIO_CFG для нормального переключения между SD-картой и LAN-портами недостаточно. Во-первых, за работу с SD-картой отвечают такие модули ядра линукс, как mmc_core.ko, mmc_block.ko и mtk_sd.ko и если при переключении в аналоговый режим работы выводов предварительно не выгрузить указанные модули, то это может привести к зависанию микрокомпьютера. И во-вторых, если при таком переключении не ресетнуть физику интерфейса ETH 24-м битом (ETHY_RST) регистра RSTCTL (0x10000034) модуля SYSCTL, то могут наглухо зависнуть сами LAN-порты. Ну и плюс, естественно, желательно перед переключением отмонтировать SD-карту. При обратном переключении из аналогового режима работы ног в цифровой достаточно просто загрузить отвечающие за работу с SD-картой модули ядра, остальное они сделают сами (при загрузке модули сами переключат нужные биты).

Итак, окончательно алгоритмы выглядят следующим образом:

Переключение из цифрового режима в аналоговый (отключаем SD-карту, включаем ETH1, ETH2, ETH3, ETH4):

  1. Выгружаем модули ядра, отвечающие за работу с sd-картой:
         rmmod mtk_sd.ko
         rmmod mmc_block.ko
         rmmod mmc_core.ko
  2. Сбрасываем биты AGPIO_CFG[17:20]
  3. Устанавливаем бит RSTCTL[24] — вызываем сброс физики ETH
  4. Сбрасываем бит RSTCTL[24] — отменяем сброс физики ETH

Переключение из аналогового режима в цифровой (отключаем ETH1, ETH2, ETH3, ETH4, включаем SD-карту):

  1. Загружаем модули ядра, отвечающие за работу с sd-картой:
         insmod mmc_core.ko
         insmod mmc_block.ko
         insmod mtk_sd.ko

Допиливаем утилиту omega2-ctrl для переключения «налету» между LAN-портами и SD-картой

Ну, что ж, — алгоритм у нас есть, осталось только реализовать его программно.

Естественно, самый первый и простой путь — использовать отладочный пакет devmem, позволяющий править данные в памяти прямо из командной строки. Собственно, мы именно с его помощью все эти алгоритмы и отлаживали. Однако, пакет всё же отладочный и в релизы его, как правило не включают, поэтому теперь, когда всё отлажено, мы пойдём другим путём.

В статье о сборке альтернативных прошивок для SKW92 я рассказывал как добавить в любую систему на базе SOC MT7688AN утилиту omega2-ctrl от Onion, а также о том, что и как нужно исправить в исходниках этой утилиты. Утилита эта, как раз позволяет «налету» переключать различные муксеры, подключая или отключая таким образом те или иные интерфейсы на определённых выводах. Логично было бы просто дополнить эту утилиту возможностью переключать «налету» LAN-порты и SD-карту. Именно так мы сейчас и сделаем.

Итак, переходим в папку с исходниками omega2-ctrl и далее выполняем следующее:

  1. Открываем файл main.c и в конец функции usage, перед закрывающей фигурной скобкой дописываем:
    	//*******************************************************
    	// дополнение для переключения физики lan-портов "налету"
    	fprintf(stderr, "\n");							// выводим в поток stderr перевод каретки на новую строку
    	fprintf(stderr, "Usage: %s eth-lan [enable|disable]\n", progname);	// выводим в stderr строку с синтаксисом использования 
    										// (в progname передаётся нулевой аргумент командной строки, который равен omega2-ctrl)
    										// вместо %s подставляется строка progname
    	fprintf(stderr, "\n");
    	fprintf(stderr, "Functionality:\n");
    	fprintf(stderr, "\twithout option - get eth-lan status\n");
    	fprintf(stderr, "\twith any option - set eth-lan to respective status\n");
    	fprintf(stderr, "\n");
    	//*******************************************************

    Эта часть будет объяснять пользователям как пользоваться нашим дополнением.

  2. Там же, в файле main.c, в функции main находим кусок кода, выполняющийся если первый аргумент командной строки = «refclk»:
    		else if (strcmp(argv[1], "refclk") == 0) {
    			...
    			refclk_mmap_close();		
    		}

    И после закрывающей фигурной скобки дописываем:

    		//*******************************************************
    		// дополнение для переключения физики lan-портов "налету"
    		else if (strcmp(argv[1], "eth-lan") == 0)		// если первый аргумент eth-lan, то
    		{	if(gpiomux_mmap_open() == EXIT_FAILURE)		// если не удалоть открыть файл для отображения в память
    			{	return EXIT_FAILURE;			// выходим с ошибкой
    			}
    			status = gpiomux_lan(argv[2]);			// пытаемся выполнить переключение, в качестве второго аргумента передаём нужное состояние
    			gpiomux_mmap_close();				// закрываем файл, отображаемый в память
    		}
    		//*******************************************************

    Эта часть кода будет анализировать аргументы командной строки, определять что юзер захотел переключиться между LAN-портами и SD-картой и вызывать функцию, выполняющую само это переключение.

  3. Открываем файл gpiomux.c и перед строкой int gpiomux_mmap_open(void) дописываем свою функцию, которая и будет выполнять само переключение:
    //*******************************************************
    // дополнение для переключения физики lan-портов "налету"
    int gpiomux_lan(char *state)
    {	// на вход получаем состояние, которое нужно установить
    	volatile uint32_t *reg = (volatile uint32_t*) (gpio_mmap_reg + 0x3C);		// получаем указатель на адрес регистра AGPIO_CFG
    	volatile uint32_t *reg2 = (volatile uint32_t*) (gpio_mmap_reg + 0x34);		// получаем указатель на адрес регистра RSTCTL
    	unsigned int v;									// переменная для чтения/записи данных памяти
    
    	if(state == NULL)								// если третьего аргумента (который нам передают в state) не существует
    	{	v = *reg;								// читаем данные из памяти (из регистра AGPIO_CFG)
    		if(v & (0xF << 17))							// если хотя бы 1 бит из [17..20] установлен - результат операции не ноль
    			fprintf(stderr, "AGPIO_CFG = %X , eth-lan disabled\n", v);	// сообщаем, что lan выключен (среди битов [17..20] есть ненулевые)
    		else
    			fprintf(stderr, "AGPIO_CFG = %X , eth-lan enabled\n", v);	// сообщаем, что lan включен (все биты [17..20] сброшены)
    		v = *reg2;								// читаем данные из памяти (из регистра RSTCTL)
    		fprintf(stderr, "RSTCTL = %X\n", v);					// выводим содержимое регистра RSTCTL
    	}
    	else if(!strcmp(state,"enable"))						// если нужно включить lan-порты
    	{	system("rmmod mtk_sd.ko");						// пытаемся выгрузить модуль mtk_sd.ko
    		system("rmmod mmc_block.ko");						// пытаемся выгрузить модуль mmc_block.ko
    		system("rmmod mmc_core.ko");						// пытаемся выгрузить модуль mmc_core.ko
    		v = *reg;								// читаем данные из памяти (из регистра AGPIO_CFG)
    		v &= ~(0xF << 17);							// сбрасываем биты с 17-го по 20-й
    		*(volatile uint32_t*) (reg) = v;					// пишем всё обратно
    
    		v = *reg2;								// читаем данные из памяти (из регистра RSTCTL)
    		v |= (0x1 << 24);							// устанавливаем 24-й бит (ресет физики эзернет)
    		*(volatile uint32_t*) (reg2) = v;					// пишем в память
    		v &= ~(0x1 << 24);							// сбрасываем 24-й бит
    		*(volatile uint32_t*) (reg2) = v;					// пишем в память
    	}
    	else if(!strcmp(state,"disable"))						// если нужно выключить lan-порты
    	{	system("insmod mmc_core.ko");						// пытаемся загрузить модуль mmc_core.ko
    		system("insmod mmc_block.ko");						// пытаемся загрузить модуль mmc_block.ko
    		system("insmod mtk_sd.ko");						// пытаемся загрузить модуль tk_sd.ko
    		// биты [17..20] в регистре AGPIO_CFG установятся автоматически
    	}
    	else										// если ни один из трёх вариантов не подходит
    	{	fprintf(stderr, "unknown command combination");				// сообщаем, что не понимаем, что от нас хотят
    		return EXIT_FAILURE;							// выходим с ошибкой
    	}
    	return EXIT_SUCCESS;
    }
    //*******************************************************
  4. Наконец последнее, что нужно сделать - это прописать нашу новую функцию в хидер. Открываем файл ./include/gpiomux.h, находим строку
    int 			gpiomux_get					(void);

    и дописываем после неё следующий код:

    //************************************************
    // дополнение для переключения lan-портов "налету"
    int			gpiomux_lan					(char *state);

Вот и всё, теперь после компиляции к функционалу утилиты omega2-ctrl добавиться возможность "налету" переключаться между LAN-портами и SD-картой.

Для переключения в режим LAN-портов достаточно будет написать в командной строке:

omega2-ctrl eth-lan enable

Чтобы выключить LAN-порты и перейти в режим использования SD-карты нужно будет написать:

omega2-ctrl eth-lan disable

Наконец, если вы просто наберёте без параметров:

omega2-ctrl eth-lan

то в ответ утилита сообщит вам о текущем статусе соответствующих выводов.

На сегодня, на этом всё. Пока.

Добавить комментарий