diff --git a/repos/base-hw/src/core/include/spec/rpi/pic.h b/repos/base-hw/src/core/include/spec/rpi/pic.h index 69b2094a5d..bda9a2f577 100644 --- a/repos/base-hw/src/core/include/spec/rpi/pic.h +++ b/repos/base-hw/src/core/include/spec/rpi/pic.h @@ -26,8 +26,111 @@ namespace Genode * Programmable interrupt controller for core */ class Pic; + + class Usb_dwc_otg; } + +class Genode::Usb_dwc_otg : Mmio +{ + private: + + struct Core_irq_status : Register<0x14, 32> + { + struct Sof : Bitfield<3, 1> { }; + }; + + struct Guid : Register<0x3c, 32> + { + struct Num : Bitfield<0, 14> { }; + + /* + * The USB driver set 'Num' to a defined value + */ + struct Num_valid : Bitfield<31, 1> { }; + + /* + * Filter is not used, overridden by the USB driver + */ + struct Kick : Bitfield<30, 1> { }; + }; + + struct Host_frame_number : Register<0x408, 32> + { + struct Num : Bitfield<0, 14> { }; + }; + + bool _is_sof() const + { + return read(); + } + + static bool _need_trigger_sof(uint32_t host_frame, + uint32_t scheduled_frame) + { + uint32_t const max_frame = 0x3fff; + + if (host_frame < scheduled_frame) { + if (scheduled_frame - host_frame < max_frame / 2) + return false; /* scheduled frame not reached yet */ + else + return true; /* scheduled frame passed, host frame wrapped */ + } else { + if (host_frame - scheduled_frame < max_frame / 2) + return true; /* scheduled frame passed */ + else + return false; /* scheduled frame wrapped, not reached */ + } + } + + public: + + Usb_dwc_otg() : Mmio(Board::USB_DWC_OTG_BASE) + { + write(0); + write(false); + write(false); + } + + bool handle_sof() + { + if (!_is_sof()) + return false; + + static int cnt, stat_cnt, filter_cnt, trigger_cnt, kick_cnt; + + stat_cnt++; + if (stat_cnt == 8000) { + PLOG("kicked: %d filtered: %d triggered: %d", kick_cnt, filter_cnt, trigger_cnt); + stat_cnt = 0; + } + + cnt++; + if (cnt == 8*20) { + cnt = 0; + return false; + } + + if (read()) + kick_cnt++; + + if (!read() || read()) + return false; + + if (_need_trigger_sof(read(), read())) { + trigger_cnt++; + return false; + } + + filter_cnt++; + + write(1); + + return true; + } +}; + + class Genode::Pic : Mmio { public: @@ -51,6 +154,8 @@ class Genode::Pic : Mmio struct Irq_disable_gpu_2 : Register<0x20, 32> { }; struct Irq_disable_basic : Register<0x24, 32> { }; + Usb_dwc_otg _usb; + /** * Return true if specified interrupt is pending */ @@ -89,6 +194,12 @@ class Genode::Pic : Mmio continue; irq = Board_base::GPU_IRQ_BASE + i; + + /* handle SOF interrupts locally, filter from the user land */ + if (irq == Board_base::DWC_IRQ) + if (_usb.handle_sof()) + return false; + return true; } diff --git a/repos/base-hw/src/core/spec/rpi/platform_support.cc b/repos/base-hw/src/core/spec/rpi/platform_support.cc index de83f4d67d..86de095f2a 100644 --- a/repos/base-hw/src/core/spec/rpi/platform_support.cc +++ b/repos/base-hw/src/core/spec/rpi/platform_support.cc @@ -51,6 +51,9 @@ Native_region * Platform::_core_only_mmio_regions(unsigned const i) /* IRQ controller */ { Board::IRQ_CONTROLLER_BASE, Board::IRQ_CONTROLLER_SIZE }, + + /* DWC OTG USB controller (used for in-kernel SOF IRQ handling) */ + { Board::USB_DWC_OTG_BASE, Board::USB_DWC_OTG_SIZE }, }; return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; } diff --git a/repos/base/include/platform/rpi/drivers/board_base.h b/repos/base/include/platform/rpi/drivers/board_base.h index 9c04d34517..2cca3f64d6 100644 --- a/repos/base/include/platform/rpi/drivers/board_base.h +++ b/repos/base/include/platform/rpi/drivers/board_base.h @@ -48,6 +48,9 @@ namespace Genode IRQ_CONTROLLER_BASE = 0x2000b200, IRQ_CONTROLLER_SIZE = 0x100, + USB_DWC_OTG_BASE = 0x20980000, + USB_DWC_OTG_SIZE = 0x20000, + /* timer */ TIMER_IRQ = 0,