После дисплея вторая по важности задача — научить телефон разговаривать с компьютером. Без этого загрузчик бесполезен: прошить новую версию, посмотреть лог, залить файл в память — всё это нужно как-то делать.
У меня три требования, и они несовместимы по умолчанию:
- fastboot — чтобы прошивать разделы
- консоль — чтобы видеть, что происходит, без UART-кабеля
- UMS — чтобы заливать файлы как на обычную флешку
С USB-кабелем из телефона можно выжать всё это сразу. Именно с этим возилась вторая неделя проекта.
Строка совместимости, которой нет
Та же история, что с SD-картой, только наоборот. Реальный DT телефона говорит:
compatible = "mediatek,mt6789-usb20"
А в форке drivers/usb/musb-new/ лежит ровно один драйвер MUSB — mt85xx.c — и он биндится на одну строку:
/* drivers/usb/musb-new/mt85xx.c:432 */
{ .compatible = "mediatek,mt8518-musb" },
Драйвера под mt6789-usb20 в форке нет вообще. Это другое семейство контроллеров — ядро MUSB у них общее, обвязка PHY разная. Поэтому в DTS осталась строка, на которую драйвер реально подписывается:
usb0: usb0@11200000 {
compatible = "mediatek,mt8518-musb", "mediatek,mtk-musb";
Заодно пришлось чинить сам узел. В формовом mt6789.dtsi у USB были две ошибки:
reg описывал два региона (mac и ippc) — драйвер второй не использует вообще, оставил один. И clock-names были main, mcu, univpll, а нужно usbpll, usbmcu, usb — иначе тактируется не то.
С PHY отдельная история: в форме был generic-tphy-v2 с кривой схемой адресации. Заменил на mt6789-tphy с нормальными ranges — после этого PHY начал зондироваться:
MUSB: PHY probed OK
DMA, которого нет
Дальше — настройка. По умолчанию musb-new работает через DMA. Собрал. Линковщик выдал:
undefined reference to `dma_init'
undefined reference to `dma_poll_complete'
undefined reference to `dma_unmap_single'
Модуль musb_dma в форке дописан не для MT6789. Попробовал CONFIG_ARCH_DMA_PADDR_LEN=64 — та же ошибка.
Тут я честно говорю: дописать musb_dma под этот SoC — работа не меньше, чем весь остальной USB-проект. Забил и перешёл на PIO:
CONFIG_USB_MUSB_GADGET=y
CONFIG_USB_MUSB_PIO_ONLY=y /* DMA отключён намеренно */
CONFIG_USB_MUSB_MT85XX=y
Работает. Скорость ниже (USB 2.0 high-speed против DMA), но для консоли хватает, для fastboot хватает, для UMS — терпимо.
Два дня, когда ПК не видел телефон
Собралось. Запускается. PHY probed OK, MUSB initialized. И всё. lsusb пуст, fastboot devices пуст. Компьютер не видит ничего.
Читаю лог по UART, вижу:
musb_gadget: musb_pullup() gadget D+ pullup off
musb_gadget: reset available
Контроллер сбрасывается, и после сброса softconnect остаётся выключенным. Пулл-ап не возвращается — устройство не перечисляется. Простое исправление в musb_gadget.c:
#ifdef __UBOOT__
/* Re-enable pullup after reset if it was on before */
if (musb->softconnect)
musb_pullup(musb, 1);
#endif
После этого телефон появился.
Когда виновата была не USB
Дальше началось интереснее. В какой-то момент я добавил отладочную печать в код SD-карты — printf в mmc_get_op_cond(). И USB отвалился. Телефон перестал перечисляться.
Потратил два дня, гадая, где в USB сломалось. Выяснилось: нигде. Это печать. Причём ломала она перечисление так, что в логе не было ни слова — просто устройство пропадало, как будто его не было.
Убрал печать в v177 — оба интерфейса заработали одновременно. С тех пор правило: отладочный вывод в U-Boot пишется не через printf, а в буфер в памяти.
Обратная сторона: из-за этой же истории автозапуск ACM в preboot пришлось убрать. Теперь:
preboot=setenv stdin serial,button-kbd; setenv stdout serial,vidconsole; \
setenv stderr serial,vidconsole; scsi reset; scsi scan
Консоль по USB включается пунктом меню вручную. Менее удобно, зато не мешает загрузке.
Три функции из одного контроллера
Теперь то, ради чего всё затевалось. Все три функции работают параллельно:
fastboot — стандартный, ничего особенного:
fastboot -l $fastboot_addr_r usb 0
Консоль — CDC ACM на stdin/stdout/stderr. Появляется порт на компьютере, к нему подключаешься и видишь меню U-Boot. Кабель не нужен вообще.
UMS — и тут есть деталь, на которой я завис на минуту:
ums 0 scsi 2
Почему scsi? Потому что разделы lk_a и lk_b на UFS у MediaTek доступны не как обычные флеш-разделы, а через SCSI-слой. Отсюда и настройки fastboot:
CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME="scsi"
CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID=2
LUN 2 — это sdb, boot-раздел. sdc (LUN 3) — это 238 гигов пользовательской памяти.
Где у USB PHY регистры
Отдельно запомнил, потому что искал долго. Регистры PHY — не по базовому адресу 0x11f40000, а со смещением:
0x318 PHY control 1
0x31c PHY control 2
0x320 PHY control 3
0x368 PHY analog control
0x36c PHY calibration (eye, терминация, дискавери)
То есть реальный банк начинается с 0x11f40300+. Если читать по нулю — получишь мусор и потратишь вечер, как я.
Что получилось
| Функция | Статус |
|---|---|
| fastboot по USB | работает |
| консоль CDC ACM | работает |
| UMS по UFS | работает |
Телефон без единого провода виден компьютеру три способами сразу. Загрузчик стал полноценным инструментом, а не чёрным ящиком.
Минус один, и я его принимаю сознательно: PIO медленнее DMA, поэтому UMS по USB идёт не так быстро, как мог бы. Если кто-то допишет musb_dma под MT6789 — это первое, что стоит попробовать.
Код и разбор: docs/hardware/usb.md ·
общее состояние проекта: README
Дальше — про драйвер, которого в форке не было вообще: часы MediaTek. Файл лежал, три драйвера были объявлены, а собрать его было нельзя.