Сам по себе интерфейс VLYNQ, если отбросить его инициализацию, представляет собой аналогию шины: он прозрачно транслирует чтение/запись в определенном окне адресного пространства в пакетные обращения по шине, плюс еще умеет делать некие конфигурационные обращения. В общем, структурно даже несколько на PCI похоже, только без hotplug. То есть, после того, как сам VLYNQ поднят, можно вычислить базовый адрес регистров UART 16550 и он ничем не будет отличаться от такого же UART, встроенного в процессор. То есть, казалось бы, можно как-то приживить к работе с таким UART-ом обычные драйвера, вроде serial8250.
Теоретически, правда, есть пара нюансов:
1. Tсли линк у VLYNQ пропадет, то чтение через VLYNQ завешивает систему. То есть, по хорошему перед чтением чего-то нужно проверять линк на исправность (статус в локальном регистре контроллера VLYNQ). Но вот на практике никогда у нас линк не падал пока.
2. Адресное пространство шины VLYNQ вообще-то тоже 32-битное. Но так как оно доступно через окошко в адресном пространстве процессора, то единовременно видно только около 64М адресов, которые мапятся через регистр страниц. То есть, по хорошему, обращение драйверов к регистрам устройства должно вестись через spinlock, ассоциированный с драйвером шины VLYNQ.
3. Прерывания по VLYNQ идут как специальный пакетик между пакетами данных. Хотя контроллер VLYNQ поддерживает 32 бита флагов прерываний и умеет сам выделять наиболее приоритетный флаг, процессору он показывает только одно прерывание, собственно от контроллера VLYNQ. То есть, нужно как-то уметь воткнуть между обработчиком прерывания стандартного драйвера UART и собственно UARTом обработчик прерывания VLYNQ.
Почитав linux device drivers 3-го издания я так и не смог до конца уяснить, требует ли новая шина написания новых драйверов, если устройства имеют стандартный интерфейс управления. Точнее говоря, из повествования следует, что требует, если я устройство зарегистрирую именно на этой шине. С другой стороны, теоретически же можно сам драйвер шины написать так, что он найдет устройства UART на шине VLYNQ, но зарегистриует их как простые platform_device. И тогда они окажутся на шине platfrom_bus, где подбор драйверов идет просто по имени. Так делать можно, или это грабли?
По поводу прерывания. В ядре 2.6.37 есть исходничек drivers/vlynq/vlynq.c. К сожалению, он нам не подходит целиком, так ка заточен на одно устройство peer-to-peer, да и применялся он, судя по всему, только для arch/mips/ar7. Но вот там прерывания интересно сделаны. 32 прерывания от VLYNQ просто замаплены на диапазон номеров прерываний выше контроллера прерываний.
Код
static irqreturn_t vlynq_irq(int irq, void *dev_id)
{
struct vlynq_device *dev = dev_id;
u32 status;
int virq = 0;
status = readl(&dev->local->int_status);
writel(status, &dev->local->int_status);
if (unlikely(!status))
spurious_interrupt();
while (status) {
if (status & 1)
do_IRQ(dev->irq_start + virq);
status >>= 1;
virq++;
}
return IRQ_HANDLED;
}
{
struct vlynq_device *dev = dev_id;
u32 status;
int virq = 0;
status = readl(&dev->local->int_status);
writel(status, &dev->local->int_status);
if (unlikely(!status))
spurious_interrupt();
while (status) {
if (status & 1)
do_IRQ(dev->irq_start + virq);
status >>= 1;
virq++;
}
return IRQ_HANDLED;
}
Короче, обработчик прерывания VLYNQ, похоже, дергает сам обработчики виртуальных прерываний. То есть, можно зарегистрировать несколько platform_device, в ресурсах которых заказать прерывания с номером, соответствующим диапазону vlynq, и обработчик основного прерывания от vlynq сам передаст прерывание драйверу UART. И тогда, по идее, стандартный драйвер должен отработать.
Еще есть вопрос, можно ли иным способом повторно использовать код обычного драйвера UART? Ну, например, написать обертки ко всем его функциям, чтобы учитывать специфику шины (spinlock'и, проверки линка и пр).