1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244 |
- From a29a51d9320d44124fe13457c45663d3051a9452 Mon Sep 17 00:00:00 2001
- From: popcornmix <popcornmix@gmail.com>
- Date: Wed, 3 Jul 2013 00:46:42 +0100
- Subject: [PATCH 025/114] Add FIQ patch to dwc_otg driver. Enable with
- dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks
- to Gordon and Costas
- Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005.
- Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh
- Make sure we wait for the reset to finish
- dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel
- memory corruption, escalating to OOPS under high USB load.
- dwc_otg: Fix unsafe access of QTD during URB enqueue
- In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the
- transaction could complete almost immediately after the qtd was assigned
- to a host channel during URB enqueue, which meant the qtd pointer was no
- longer valid having been completed and removed. Usually, this resulted in
- an OOPS during URB submission. By predetermining whether transactions
- need to be queued or not, this unsafe pointer access is avoided.
- This bug was only evident on the Pi model A where a device was attached
- that had no periodic endpoints (e.g. USB pendrive or some wlan devices).
- dwc_otg: Fix incorrect URB allocation error handling
- If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS
- because for some reason a member of the *unallocated* struct was set to
- zero. Error handling changed to fail correctly.
- dwc_otg: fix potential use-after-free case in interrupt handler
- If a transaction had previously aborted, certain interrupts are
- enabled to track error counts and reset where necessary. On IN
- endpoints the host generates an ACK interrupt near-simultaneously
- with completion of transfer. In the case where this transfer had
- previously had an error, this results in a use-after-free on
- the QTD memory space with a 1-byte length being overwritten to
- 0x00.
- dwc_otg: add handling of SPLIT transaction data toggle errors
- Previously a data toggle error on packets from a USB1.1 device behind
- a TT would result in the Pi locking up as the driver never handled
- the associated interrupt. Patch adds basic retry mechanism and
- interrupt acknowledgement to cater for either a chance toggle error or
- for devices that have a broken initial toggle state (FT8U232/FT232BM).
- dwc_otg: implement tasklet for returning URBs to usbcore hcd layer
- The dwc_otg driver interrupt handler for transfer completion will spend
- a very long time with interrupts disabled when a URB is completed -
- this is because usb_hcd_giveback_urb is called from within the handler
- which for a USB device driver with complicated processing (e.g. webcam)
- will take an exorbitant amount of time to complete. This results in
- missed completion interrupts for other USB packets which lead to them
- being dropped due to microframe overruns.
- This patch splits returning the URB to the usb hcd layer into a
- high-priority tasklet. This will have most benefit for isochronous IN
- transfers but will also have incidental benefit where multiple periodic
- devices are active at once.
- dwc_otg: fix NAK holdoff and allow on split transactions only
- This corrects a bug where if a single active non-periodic endpoint
- had at least one transaction in its qh, on frnum == MAX_FRNUM the qh
- would get skipped and never get queued again. This would result in
- a silent device until error detection (automatic or otherwise) would
- either reset the device or flush and requeue the URBs.
- Additionally the NAK holdoff was enabled for all transactions - this
- would potentially stall a HS endpoint for 1ms if a previous error state
- enabled this interrupt and the next response was a NAK. Fix so that
- only split transactions get held off.
- dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler
- usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it
- asynchronously in the tasklet was not safe (regression in
- c4564d4a1a0a9b10d4419e48239f5d99e88d2667).
- This change unlinks it from the endpoint prior to queueing it for handling in
- the tasklet, and also adds a check to ensure the urb is OK to be unlinked
- before doing so.
- NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb
- when a USB device was unplugged/replugged during data transfer. This effect
- was reproduced using automated USB port power control, hundreds of replug
- events were performed during active transfers to confirm that the problem was
- eliminated.
- USB fix using a FIQ to implement split transactions
- This commit adds a FIQ implementaion that schedules
- the split transactions using a FIQ so we don't get
- held off by the interrupt latency of Linux
- dwc_otg: fix device attributes and avoid kernel warnings on boot
- dcw_otg: avoid logging function that can cause panics
- See: https://github.com/raspberrypi/firmware/issues/21
- Thanks to cleverca22 for fix
- dwc_otg: mask correct interrupts after transaction error recovery
- The dwc_otg driver will unmask certain interrupts on a transaction
- that previously halted in the error state in order to reset the
- QTD error count. The various fine-grained interrupt handlers do not
- consider that other interrupts besides themselves were unmasked.
- By disabling the two other interrupts only ever enabled in DMA mode
- for this purpose, we can avoid unnecessary function calls in the
- IRQ handler. This will also prevent an unneccesary FIQ interrupt
- from being generated if the FIQ is enabled.
- dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ
- In the case of a transaction to a device that had previously aborted
- due to an error, several interrupts are enabled to reset the error
- count when a device responds. This has the side-effect of making the
- FIQ thrash because the hardware will generate multiple instances of
- a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK
- on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the
- associated interrupts.
- Additionally, on non-split transactions make sure that only unmasked
- interrupts are cleared. This caused a hard-to-trigger but serious
- race condition when you had the combination of an endpoint awaiting
- error recovery and a transaction completed on an endpoint - due to
- the sequencing and timing of interrupts generated by the dwc_otg core,
- it was possible to confuse the IRQ handler.
- Fix function tracing
- dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue
- dwc_otg: prevent OOPSes during device disconnects
- The dwc_otg_urb_enqueue function is thread-unsafe. In particular the
- access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and
- friends does not occur within a critical section and so if a device
- was unplugged during activity there was a high chance that the
- usbcore hub_thread would try to disable the endpoint with partially-
- formed entries in the URB queue. This would result in BUG() or null
- pointer dereferences.
- Fix so that access of urb->hcpriv, enqueuing to the hardware and
- adding to usbcore endpoint URB lists is contained within a single
- critical section.
- dwc_otg: prevent BUG() in TT allocation if hub address is > 16
- A fixed-size array is used to track TT allocation. This was
- previously set to 16 which caused a crash because
- dwc_otg_hcd_allocate_port would read past the end of the array.
- This was hit if a hub was plugged in which enumerated as addr > 16,
- due to previous device resets or unplugs.
- Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows
- to a large size if 128 hub addresses are supported. This field is
- for debug only for tracking which frame an allocate happened in.
- dwc_otg: make channel halts with unknown state less damaging
- If the IRQ received a channel halt interrupt through the FIQ
- with no other bits set, the IRQ would not release the host
- channel and never complete the URB.
- Add catchall handling to treat as a transaction error and retry.
- dwc_otg: fiq_split: use TTs with more granularity
- This fixes certain issues with split transaction scheduling.
- - Isochronous multi-packet OUT transactions now hog the TT until
- they are completed - this prevents hubs aborting transactions
- if they get a periodic start-split out-of-order
- - Don't perform TT allocation on non-periodic endpoints - this
- allows simultaneous use of the TT's bulk/control and periodic
- transaction buffers
- This commit will mainly affect USB audio playback.
- dwc_otg: fix potential sleep while atomic during urb enqueue
- Fixes a regression introduced with eb1b482a. Kmalloc called from
- dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have
- the GPF_ATOMIC flag set. Force this flag when inside the larger
- critical section.
- dwc_otg: make fiq_split_enable imply fiq_fix_enable
- Failing to set up the FIQ correctly would result in
- "IRQ 32: nobody cared" errors in dmesg.
- dwc_otg: prevent crashes on host port disconnects
- Fix several issues resulting in crashes or inconsistent state
- if a Model A root port was disconnected.
- - Clean up queue heads properly in kill_urbs_in_qh_list by
- removing the empty QHs from the schedule lists
- - Set the halt status properly to prevent IRQ handlers from
- using freed memory
- - Add fiq_split related cleanup for saved registers
- - Make microframe scheduling reclaim host channels if
- active during a disconnect
- - Abort URBs with -ESHUTDOWN status response, informing
- device drivers so they respond in a more correct fashion
- and don't try to resubmit URBs
- - Prevent IRQ handlers from attempting to handle channel
- interrupts if the associated URB was dequeued (and the
- driver state was cleared)
- dwc_otg: prevent leaking URBs during enqueue
- A dwc_otg_urb would get leaked if the HCD enqueue function
- failed for any reason. Free the URB at the appropriate points.
- dwc_otg: Enable NAK holdoff for control split transactions
- Certain low-speed devices take a very long time to complete a
- data or status stage of a control transaction, producing NAK
- responses until they complete internal processing - the USB2.0
- spec limit is up to 500mS. This causes the same type of interrupt
- storm as seen with USB-serial dongles prior to c8edb238.
- In certain circumstances, usually while booting, this interrupt
- storm could cause SD card timeouts.
- dwc_otg: Fix for occasional lockup on boot when doing a USB reset
- dwc_otg: Don't issue traffic to LS devices in FS mode
- Issuing low-speed packets when the root port is in full-speed mode
- causes the root port to stop responding. Explicitly fail when
- enqueuing URBs to a LS endpoint on a FS bus.
- Fix ARM architecture issue with local_irq_restore()
- If local_fiq_enable() is called before a local_irq_restore(flags) where
- the flags variable has the F bit set, the FIQ will be erroneously disabled.
- Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR.
- Also fix some of the hacks previously implemented for previous dwc_otg
- incarnations.
- ---
- arch/arm/Kconfig | 1 +
- arch/arm/include/asm/irqflags.h | 16 +-
- arch/arm/kernel/fiqasm.S | 4 +
- arch/arm/mach-bcm2708/armctrl.c | 19 +-
- arch/arm/mach-bcm2708/bcm2708.c | 29 +-
- arch/arm/mach-bcm2708/include/mach/irqs.h | 153 ++---
- .../usb/host/dwc_common_port/dwc_common_linux.c | 11 +
- drivers/usb/host/dwc_common_port/dwc_list.h | 14 +-
- drivers/usb/host/dwc_common_port/dwc_os.h | 2 +
- drivers/usb/host/dwc_otg/Makefile | 1 +
- drivers/usb/host/dwc_otg/dwc_otg_attr.c | 14 +-
- drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +-
- drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 1 +
- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 52 +-
- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 303 +++++++--
- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 37 +-
- drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 3 +-
- drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 5 +
- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 705 ++++++++++++++++++++-
- drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 159 +++--
- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 53 +-
- drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 ++++
- drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 ++
- drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 3 +
- drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 2 +-
- 25 files changed, 1544 insertions(+), 251 deletions(-)
- create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
- create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
- --- a/arch/arm/Kconfig
- +++ b/arch/arm/Kconfig
- @@ -395,6 +395,7 @@ config ARCH_BCM2708
- select ARM_ERRATA_411920
- select MACH_BCM2708
- select VC4
- + select FIQ
- help
- This enables support for Broadcom BCM2708 boards.
-
- --- a/arch/arm/include/asm/irqflags.h
- +++ b/arch/arm/include/asm/irqflags.h
- @@ -145,12 +145,22 @@ static inline unsigned long arch_local_s
- }
-
- /*
- - * restore saved IRQ & FIQ state
- + * restore saved IRQ state
- */
- static inline void arch_local_irq_restore(unsigned long flags)
- {
- - asm volatile(
- - " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore"
- + unsigned long temp = 0;
- + flags &= ~(1 << 6);
- + asm volatile (
- + " mrs %0, cpsr"
- + : "=r" (temp)
- + :
- + : "memory", "cc");
- + /* Preserve FIQ bit */
- + temp &= (1 << 6);
- + flags = flags | temp;
- + asm volatile (
- + " msr cpsr_c, %0 @ local_irq_restore"
- :
- : "r" (flags)
- : "memory", "cc");
- --- a/arch/arm/kernel/fiqasm.S
- +++ b/arch/arm/kernel/fiqasm.S
- @@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs)
- mov r0, r0 @ avoid hazard prior to ARMv4
- ret lr
- ENDPROC(__get_fiq_regs)
- +
- +ENTRY(__FIQ_Branch)
- + mov pc, r8
- +ENDPROC(__FIQ_Branch)
- --- a/arch/arm/mach-bcm2708/armctrl.c
- +++ b/arch/arm/mach-bcm2708/armctrl.c
- @@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_
- 0
- };
-
- - unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
- - writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3]));
- + if (d->irq >= FIQ_START) {
- + writel(0, __io_address(ARM_IRQ_FAST));
- + } else {
- + unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
- + writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3]));
- + }
- }
-
- static void armctrl_unmask_irq(struct irq_data *d)
- @@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct ir
- 0
- };
-
- - unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
- - writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3]));
- + if (d->irq >= FIQ_START) {
- + unsigned int data =
- + (unsigned int)irq_get_chip_data(d->irq) - FIQ_START;
- + writel(0x80 | data, __io_address(ARM_IRQ_FAST));
- + } else {
- + unsigned int data = (unsigned int)irq_get_chip_data(d->irq);
- + writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3]));
- + }
- }
-
- #if defined(CONFIG_PM)
- @@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * b
- }
-
- armctrl_pm_register(base, irq_start, resume_sources);
- + init_FIQ(FIQ_START);
- return 0;
- }
- --- a/arch/arm/mach-bcm2708/bcm2708.c
- +++ b/arch/arm/mach-bcm2708/bcm2708.c
- @@ -321,12 +321,32 @@ static struct resource bcm2708_usb_resou
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- - .start = IRQ_USB,
- - .end = IRQ_USB,
- + .start = MPHI_BASE,
- + .end = MPHI_BASE + SZ_4K - 1,
- + .flags = IORESOURCE_MEM,
- + },
- + [2] = {
- + .start = IRQ_HOSTPORT,
- + .end = IRQ_HOSTPORT,
- .flags = IORESOURCE_IRQ,
- },
- };
-
- +bool fiq_fix_enable = true;
- +
- +static struct resource bcm2708_usb_resources_no_fiq_fix[] = {
- + [0] = {
- + .start = USB_BASE,
- + .end = USB_BASE + SZ_128K - 1,
- + .flags = IORESOURCE_MEM,
- + },
- + [1] = {
- + .start = IRQ_USB,
- + .end = IRQ_USB,
- + .flags = IORESOURCE_IRQ,
- + },
- +};
- +
- static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
-
- static struct platform_device bcm2708_usb_device = {
- @@ -681,6 +701,11 @@ void __init bcm2708_init(void)
- #endif
- bcm_register_device(&bcm2708_systemtimer_device);
- bcm_register_device(&bcm2708_fb_device);
- + if (!fiq_fix_enable)
- + {
- + bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix;
- + bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix);
- + }
- bcm_register_device(&bcm2708_usb_device);
- bcm_register_device(&bcm2708_uart1_device);
- bcm_register_device(&bcm2708_powerman_device);
- --- a/arch/arm/mach-bcm2708/include/mach/irqs.h
- +++ b/arch/arm/mach-bcm2708/include/mach/irqs.h
- @@ -106,87 +106,90 @@
- #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1)
- #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2)
-
- +#define FIQ_START HARD_IRQS
- +
- /*
- * FIQ interrupts definitions are the same as the INT definitions.
- */
- -#define FIQ_TIMER0 INT_TIMER0
- -#define FIQ_TIMER1 INT_TIMER1
- -#define FIQ_TIMER2 INT_TIMER2
- -#define FIQ_TIMER3 INT_TIMER3
- -#define FIQ_CODEC0 INT_CODEC0
- -#define FIQ_CODEC1 INT_CODEC1
- -#define FIQ_CODEC2 INT_CODEC2
- -#define FIQ_JPEG INT_JPEG
- -#define FIQ_ISP INT_ISP
- -#define FIQ_USB INT_USB
- -#define FIQ_3D INT_3D
- -#define FIQ_TRANSPOSER INT_TRANSPOSER
- -#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0
- -#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1
- -#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2
- -#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3
- -#define FIQ_DMA0 INT_DMA0
- -#define FIQ_DMA1 INT_DMA1
- -#define FIQ_DMA2 INT_DMA2
- -#define FIQ_DMA3 INT_DMA3
- -#define FIQ_DMA4 INT_DMA4
- -#define FIQ_DMA5 INT_DMA5
- -#define FIQ_DMA6 INT_DMA6
- -#define FIQ_DMA7 INT_DMA7
- -#define FIQ_DMA8 INT_DMA8
- -#define FIQ_DMA9 INT_DMA9
- -#define FIQ_DMA10 INT_DMA10
- -#define FIQ_DMA11 INT_DMA11
- -#define FIQ_DMA12 INT_DMA12
- -#define FIQ_AUX INT_AUX
- -#define FIQ_ARM INT_ARM
- -#define FIQ_VPUDMA INT_VPUDMA
- -#define FIQ_HOSTPORT INT_HOSTPORT
- -#define FIQ_VIDEOSCALER INT_VIDEOSCALER
- -#define FIQ_CCP2TX INT_CCP2TX
- -#define FIQ_SDC INT_SDC
- -#define FIQ_DSI0 INT_DSI0
- -#define FIQ_AVE INT_AVE
- -#define FIQ_CAM0 INT_CAM0
- -#define FIQ_CAM1 INT_CAM1
- -#define FIQ_HDMI0 INT_HDMI0
- -#define FIQ_HDMI1 INT_HDMI1
- -#define FIQ_PIXELVALVE1 INT_PIXELVALVE1
- -#define FIQ_I2CSPISLV INT_I2CSPISLV
- -#define FIQ_DSI1 INT_DSI1
- -#define FIQ_PWA0 INT_PWA0
- -#define FIQ_PWA1 INT_PWA1
- -#define FIQ_CPR INT_CPR
- -#define FIQ_SMI INT_SMI
- -#define FIQ_GPIO0 INT_GPIO0
- -#define FIQ_GPIO1 INT_GPIO1
- -#define FIQ_GPIO2 INT_GPIO2
- -#define FIQ_GPIO3 INT_GPIO3
- -#define FIQ_I2C INT_I2C
- -#define FIQ_SPI INT_SPI
- -#define FIQ_I2SPCM INT_I2SPCM
- -#define FIQ_SDIO INT_SDIO
- -#define FIQ_UART INT_UART
- -#define FIQ_SLIMBUS INT_SLIMBUS
- -#define FIQ_VEC INT_VEC
- -#define FIQ_CPG INT_CPG
- -#define FIQ_RNG INT_RNG
- -#define FIQ_ARASANSDIO INT_ARASANSDIO
- -#define FIQ_AVSPMON INT_AVSPMON
- +#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0)
- +#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1)
- +#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2)
- +#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3)
- +#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0)
- +#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1)
- +#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2)
- +#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG)
- +#define FIQ_ISP (FIQ_START+INTERRUPT_ISP)
- +#define FIQ_USB (FIQ_START+INTERRUPT_USB)
- +#define FIQ_3D (FIQ_START+INTERRUPT_3D)
- +#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER)
- +#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0)
- +#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1)
- +#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2)
- +#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3)
- +#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0)
- +#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1)
- +#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2)
- +#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3)
- +#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4)
- +#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5)
- +#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6)
- +#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7)
- +#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8)
- +#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9)
- +#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10)
- +#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11)
- +#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12)
- +#define FIQ_AUX (FIQ_START+INTERRUPT_AUX)
- +#define FIQ_ARM (FIQ_START+INTERRUPT_ARM)
- +#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA)
- +#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT)
- +#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER)
- +#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX)
- +#define FIQ_SDC (FIQ_START+INTERRUPT_SDC)
- +#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0)
- +#define FIQ_AVE (FIQ_START+INTERRUPT_AVE)
- +#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0)
- +#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1)
- +#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0)
- +#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1)
- +#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1)
- +#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV)
- +#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1)
- +#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0)
- +#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1)
- +#define FIQ_CPR (FIQ_START+INTERRUPT_CPR)
- +#define FIQ_SMI (FIQ_START+INTERRUPT_SMI)
- +#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0)
- +#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1)
- +#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2)
- +#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3)
- +#define FIQ_I2C (FIQ_START+INTERRUPT_I2C)
- +#define FIQ_SPI (FIQ_START+INTERRUPT_SPI)
- +#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM)
- +#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO)
- +#define FIQ_UART (FIQ_START+INTERRUPT_UART)
- +#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS)
- +#define FIQ_VEC (FIQ_START+INTERRUPT_VEC)
- +#define FIQ_CPG (FIQ_START+INTERRUPT_CPG)
- +#define FIQ_RNG (FIQ_START+INTERRUPT_RNG)
- +#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO)
- +#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON)
-
- -#define FIQ_ARM_TIMER INT_ARM_TIMER
- -#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX
- -#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0
- -#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1
- -#define FIQ_VPU0_HALTED INT_VPU0_HALTED
- -#define FIQ_VPU1_HALTED INT_VPU1_HALTED
- -#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0
- -#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1
- -#define FIQ_PENDING1 INT_PENDING1
- -#define FIQ_PENDING2 INT_PENDING2
- +#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER)
- +#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX)
- +#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0)
- +#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1)
- +#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED)
- +#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED)
- +#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0)
- +#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1)
- +#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1)
- +#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2)
-
- #define HARD_IRQS (64 + 21)
- -#define GPIO_IRQ_START (HARD_IRQS)
- +#define FIQ_IRQS (64 + 21)
- +#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS)
- #define GPIO_IRQS (32*5)
- #define SPARE_ALLOC_IRQS 64
- #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS)
- --- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c
- +++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
- @@ -580,7 +580,13 @@ void DWC_WRITE_REG64(uint64_t volatile *
-
- void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
- {
- + unsigned long flags;
- +
- + local_irq_save(flags);
- + local_fiq_disable();
- writel((readl(reg) & ~clear_mask) | set_mask, reg);
- + local_fiq_enable();
- + local_irq_restore(flags);
- }
-
- #if 0
- @@ -995,6 +1001,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *ta
- tasklet_schedule(&task->t);
- }
-
- +void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
- +{
- + tasklet_hi_schedule(&task->t);
- +}
- +
-
- /* workqueues
- - run in process context (can sleep)
- --- a/drivers/usb/host/dwc_common_port/dwc_list.h
- +++ b/drivers/usb/host/dwc_common_port/dwc_list.h
- @@ -384,17 +384,17 @@ struct { \
- #define DWC_TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
- #define DWC_TAILQ_EMPTY(head) \
- - (TAILQ_FIRST(head) == TAILQ_END(head))
- + (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
-
- #define DWC_TAILQ_FOREACH(var, head, field) \
- - for((var) = TAILQ_FIRST(head); \
- - (var) != TAILQ_END(head); \
- - (var) = TAILQ_NEXT(var, field))
- + for ((var) = DWC_TAILQ_FIRST(head); \
- + (var) != DWC_TAILQ_END(head); \
- + (var) = DWC_TAILQ_NEXT(var, field))
-
- #define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- - for((var) = TAILQ_LAST(head, headname); \
- - (var) != TAILQ_END(head); \
- - (var) = TAILQ_PREV(var, headname, field))
- + for ((var) = DWC_TAILQ_LAST(head, headname); \
- + (var) != DWC_TAILQ_END(head); \
- + (var) = DWC_TAILQ_PREV(var, headname, field))
-
- /*
- * Tail queue functions.
- --- a/drivers/usb/host/dwc_common_port/dwc_os.h
- +++ b/drivers/usb/host/dwc_common_port/dwc_os.h
- @@ -982,6 +982,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t
- extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
- #define dwc_task_schedule DWC_TASK_SCHEDULE
-
- +extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
- +#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
-
- /** @name Timer
- *
- --- a/drivers/usb/host/dwc_otg/Makefile
- +++ b/drivers/usb/host/dwc_otg/Makefile
- @@ -36,6 +36,7 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_ci
- dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o
- dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o
- dwc_otg-objs += dwc_otg_adp.o
- +dwc_otg-objs += dwc_otg_mphi_fix.o
- ifneq ($(CFI),)
- dwc_otg-objs += dwc_otg_cfi.o
- endif
- --- a/drivers/usb/host/dwc_otg/dwc_otg_attr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c
- @@ -909,7 +909,7 @@ static ssize_t regdump_show(struct devic
- return sprintf(buf, "Register Dump\n");
- }
-
- -DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0);
- +DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0);
-
- /**
- * Dump global registers and either host or device registers (depending on the
- @@ -920,12 +920,12 @@ static ssize_t spramdump_show(struct dev
- {
- dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
-
- - dwc_otg_dump_spram(otg_dev->core_if);
- + //dwc_otg_dump_spram(otg_dev->core_if);
-
- return sprintf(buf, "SPRAM Dump\n");
- }
-
- -DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0);
- +DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0);
-
- /**
- * Dump the current hcd state.
- @@ -940,7 +940,7 @@ static ssize_t hcddump_show(struct devic
- return sprintf(buf, "HCD Dump\n");
- }
-
- -DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0);
- +DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0);
-
- /**
- * Dump the average frame remaining at SOF. This can be used to
- @@ -958,7 +958,7 @@ static ssize_t hcd_frrem_show(struct dev
- return sprintf(buf, "HCD Dump Frame Remaining\n");
- }
-
- -DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0);
- +DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0);
-
- /**
- * Displays the time required to read the GNPTXFSIZ register many times (the
- @@ -986,7 +986,7 @@ static ssize_t rd_reg_test_show(struct d
- RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
- }
-
- -DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0);
- +DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0);
-
- /**
- * Displays the time required to write the GNPTXFSIZ register many times (the
- @@ -1014,7 +1014,7 @@ static ssize_t wr_reg_test_show(struct d
- RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
- }
-
- -DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0);
- +DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0);
-
- #ifdef CONFIG_USB_DWC_OTG_LPM
-
- --- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- @@ -45,6 +45,7 @@
- #include "dwc_otg_driver.h"
- #include "dwc_otg_pcd.h"
- #include "dwc_otg_hcd.h"
- +#include "dwc_otg_mphi_fix.h"
-
- #ifdef DEBUG
- inline const char *op_state_str(dwc_otg_core_if_t * core_if)
- @@ -1318,7 +1319,7 @@ static int32_t dwc_otg_handle_lpm_intr(d
- /**
- * This function returns the Core Interrupt register.
- */
- -static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if)
- +static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk)
- {
- gahbcfg_data_t gahbcfg = {.d32 = 0 };
- gintsts_data_t gintsts;
- @@ -1335,26 +1336,45 @@ static inline uint32_t dwc_otg_read_comm
- gintmsk_common.b.lpmtranrcvd = 1;
- #endif
- gintmsk_common.b.restoredone = 1;
- - /** @todo: The port interrupt occurs while in device
- - * mode. Added code to CIL to clear the interrupt for now!
- - */
- - gintmsk_common.b.portintr = 1;
- -
- + if(dwc_otg_is_device_mode(core_if))
- + {
- + /** @todo: The port interrupt occurs while in device
- + * mode. Added code to CIL to clear the interrupt for now!
- + */
- + gintmsk_common.b.portintr = 1;
- + }
- gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- + {
- + unsigned long flags;
- +
- + // Re-enable the saved interrupts
- + local_irq_save(flags);
- + local_fiq_disable();
- + gintmsk.d32 |= gintmsk_common.d32;
- + gintsts_saved.d32 &= ~gintmsk_common.d32;
- + reenable_gintmsk->d32 = gintmsk.d32;
- + local_irq_restore(flags);
- + }
- +
- gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
-
- #ifdef DEBUG
- /* if any common interrupts set */
- if (gintsts.d32 & gintmsk_common.d32) {
- - DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
- + DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n",
- gintsts.d32, gintmsk.d32);
- }
- #endif
- - if (gahbcfg.b.glblintrmsk)
- + if (!fiq_fix_enable){
- + if (gahbcfg.b.glblintrmsk)
- + return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
- + else
- + return 0;
- + }
- + else {
- return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
- - else
- - return 0;
- + }
-
- }
-
- @@ -1386,6 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void
- {
- int retval = 0;
- gintsts_data_t gintsts;
- + gintmsk_data_t reenable_gintmsk;
- gpwrdn_data_t gpwrdn = {.d32 = 0 };
- dwc_otg_device_t *otg_dev = dev;
- dwc_otg_core_if_t *core_if = otg_dev->core_if;
- @@ -1407,7 +1428,7 @@ int32_t dwc_otg_handle_common_intr(void
- }
-
- if (core_if->hibernation_suspend <= 0) {
- - gintsts.d32 = dwc_otg_read_common_intr(core_if);
- + gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk);
-
- if (gintsts.b.modemismatch) {
- retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
- @@ -1504,8 +1525,12 @@ int32_t dwc_otg_handle_common_intr(void
- gintsts.b.portintr = 1;
- DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
- retval |= 1;
- + reenable_gintmsk.b.portintr = 1;
-
- }
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32);
- +
- } else {
- DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
-
- --- a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
- @@ -49,6 +49,7 @@ static inline uint32_t SET_DEBUG_LEVEL(c
- return old;
- }
-
- +#define DBG_USER (0x1)
- /** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
- #define DBG_CIL (0x2)
- /** When debug level has the DBG_CILV bit set, display CIL Verbose debug
- --- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
- @@ -64,6 +64,8 @@ bool microframe_schedule=true;
-
- static const char dwc_driver_name[] = "dwc_otg";
-
- +extern void* dummy_send;
- +
- extern int pcd_init(
- #ifdef LM_INTERFACE
- struct lm_device *_dev
- @@ -238,6 +240,14 @@ static struct dwc_otg_driver_module_para
- .adp_enable = -1,
- };
-
- +//Global variable to switch the fiq fix on or off (declared in bcm2708.c)
- +extern bool fiq_fix_enable;
- +// Global variable to enable the split transaction fix
- +bool fiq_split_enable = true;
- +//Global variable to switch the nak holdoff on or off
- +bool nak_holdoff_enable = true;
- +
- +
- /**
- * This function shows the Driver Version.
- */
- @@ -779,17 +789,33 @@ static int dwc_otg_driver_probe(
- _dev->resource->start,
- _dev->resource->end - _dev->resource->start + 1);
- #if 1
- - if (!request_mem_region(_dev->resource->start,
- - _dev->resource->end - _dev->resource->start + 1,
- + if (!request_mem_region(_dev->resource[0].start,
- + _dev->resource[0].end - _dev->resource[0].start + 1,
- "dwc_otg")) {
- dev_dbg(&_dev->dev, "error reserving mapped memory\n");
- retval = -EFAULT;
- goto fail;
- }
-
- - dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start,
- - _dev->resource->end -
- - _dev->resource->start+1);
- + dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start,
- + _dev->resource[0].end -
- + _dev->resource[0].start+1);
- + if (fiq_fix_enable)
- + {
- + if (!request_mem_region(_dev->resource[1].start,
- + _dev->resource[1].end - _dev->resource[1].start + 1,
- + "dwc_otg")) {
- + dev_dbg(&_dev->dev, "error reserving mapped memory\n");
- + retval = -EFAULT;
- + goto fail;
- + }
- +
- + dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start,
- + _dev->resource[1].end -
- + _dev->resource[1].start + 1);
- + dummy_send = (void *) kmalloc(16, GFP_ATOMIC);
- + }
- +
- #else
- {
- struct map_desc desc = {
- @@ -1044,6 +1070,12 @@ static int __init dwc_otg_driver_init(vo
- int retval = 0;
- int error;
- struct device_driver *drv;
- +
- + if(fiq_split_enable && !fiq_fix_enable) {
- + printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n");
- + fiq_fix_enable = 1;
- + }
- +
- printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name,
- DWC_DRIVER_VERSION,
- #ifdef LM_INTERFACE
- @@ -1063,6 +1095,9 @@ static int __init dwc_otg_driver_init(vo
- printk(KERN_ERR "%s retval=%d\n", __func__, retval);
- return retval;
- }
- + printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled");
- + printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled");
- + printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled");
-
- error = driver_create_file(drv, &driver_attr_version);
- #ifdef DEBUG
- @@ -1343,6 +1378,13 @@ MODULE_PARM_DESC(otg_ver, "OTG revision
- module_param(microframe_schedule, bool, 0444);
- MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
-
- +module_param(fiq_fix_enable, bool, 0444);
- +MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix");
- +module_param(nak_holdoff_enable, bool, 0444);
- +MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff");
- +module_param(fiq_split_enable, bool, 0444);
- +MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions");
- +
- /** @page "Module Parameters"
- *
- * The following parameters may be specified when starting the module.
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- @@ -40,10 +40,14 @@
- * header file.
- */
-
- +#include <linux/usb.h>
- +#include <linux/usb/hcd.h>
- +
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- +#include "dwc_otg_mphi_fix.h"
-
- -extern bool microframe_schedule;
- +extern bool microframe_schedule, nak_holdoff_enable;
-
- //#define DEBUG_HOST_CHANNELS
- #ifdef DEBUG_HOST_CHANNELS
- @@ -53,6 +57,13 @@ static int last_sel_trans_num_avail_hc_a
- static int last_sel_trans_num_avail_hc_at_end = 0;
- #endif /* DEBUG_HOST_CHANNELS */
-
- +extern int g_next_sched_frame, g_np_count, g_np_sent;
- +
- +extern haint_data_t haint_saved;
- +extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
- +extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
- +extern gintsts_data_t ginsts_saved;
- +
- dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
- {
- return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
- @@ -162,31 +173,43 @@ static void del_timers(dwc_otg_hcd_t * h
-
- /**
- * Processes all the URBs in a single list of QHs. Completes them with
- - * -ETIMEDOUT and frees the QTD.
- + * -ESHUTDOWN and frees the QTD.
- */
- static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
- {
- - dwc_list_link_t *qh_item;
- + dwc_list_link_t *qh_item, *qh_tmp;
- dwc_otg_qh_t *qh;
- dwc_otg_qtd_t *qtd, *qtd_tmp;
-
- - DWC_LIST_FOREACH(qh_item, qh_list) {
- + DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
- qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
- &qh->qtd_list, qtd_list_entry) {
- qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- if (qtd->urb != NULL) {
- hcd->fops->complete(hcd, qtd->urb->priv,
- - qtd->urb, -DWC_E_TIMEOUT);
- + qtd->urb, -DWC_E_SHUTDOWN);
- dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- }
-
- }
- + if(qh->channel) {
- + /* Using hcchar.chen == 1 is not a reliable test.
- + * It is possible that the channel has already halted
- + * but not yet been through the IRQ handler.
- + */
- + dwc_otg_hc_halt(hcd->core_if, qh->channel,
- + DWC_OTG_HC_XFER_URB_DEQUEUE);
- + if(microframe_schedule)
- + hcd->available_host_channels++;
- + qh->channel = NULL;
- + }
- + dwc_otg_hcd_qh_remove(hcd, qh);
- }
- }
-
- /**
- - * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
- + * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic
- * and periodic schedules. The QTD associated with each URB is removed from
- * the schedule and freed. This function may be called when a disconnect is
- * detected or when the HCD is being stopped.
- @@ -272,7 +295,8 @@ static int32_t dwc_otg_hcd_disconnect_cb
- */
- dwc_otg_hcd->flags.b.port_connect_status_change = 1;
- dwc_otg_hcd->flags.b.port_connect_status = 0;
- -
- + if(fiq_fix_enable)
- + local_fiq_disable();
- /*
- * Shutdown any transfers in process by clearing the Tx FIFO Empty
- * interrupt mask and status bits and disabling subsequent host
- @@ -368,8 +392,22 @@ static int32_t dwc_otg_hcd_disconnect_cb
- channel->qh = NULL;
- }
- }
- + if(fiq_split_enable) {
- + for(i=0; i < 128; i++) {
- + dwc_otg_hcd->hub_port[i] = 0;
- + }
- + haint_saved.d32 = 0;
- + for(i=0; i < MAX_EPS_CHANNELS; i++) {
- + hcint_saved[i].d32 = 0;
- + hcintmsk_saved[i].d32 = 0;
- + }
- + }
- +
- }
-
- + if(fiq_fix_enable)
- + local_fiq_enable();
- +
- if (dwc_otg_hcd->fops->disconnect) {
- dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
- }
- @@ -407,6 +445,7 @@ static int dwc_otg_hcd_sleep_cb(void *p)
- }
- #endif
-
- +
- /**
- * HCD Callback function for Remote Wakeup.
- *
- @@ -457,10 +496,12 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
- dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
- int atomic_alloc)
- {
- - dwc_irqflags_t flags;
- int retval = 0;
- + uint8_t needs_scheduling = 0;
- + dwc_otg_transaction_type_e tr_type;
- dwc_otg_qtd_t *qtd;
- gintmsk_data_t intr_mask = {.d32 = 0 };
- + hprt0_data_t hprt0 = { .d32 = 0 };
-
- #ifdef DEBUG /* integrity checks (Broadcom) */
- if (NULL == hcd->core_if) {
- @@ -475,6 +516,16 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
- return -DWC_E_NO_DEVICE;
- }
-
- + /* Some core configurations cannot support LS traffic on a FS root port */
- + if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) &&
- + (hcd->core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) {
- + hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
- + if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) {
- + return -DWC_E_NO_DEVICE;
- + }
- + }
- +
- qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc);
- if (qtd == NULL) {
- DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
- @@ -490,32 +541,27 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
- return -DWC_E_NO_MEMORY;
- }
- #endif
- - retval =
- - dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
- + intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
- + if(!intr_mask.b.sofintr) needs_scheduling = 1;
- + if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP))
- + /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
- + needs_scheduling = 0;
- +
- + retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
- // creates a new queue in ep_handle if it doesn't exist already
- if (retval < 0) {
- DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
- "Error status %d\n", retval);
- dwc_otg_hcd_qtd_free(qtd);
- - } else {
- - qtd->qh = *ep_handle;
- + return retval;
- }
- - intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
- - if (!intr_mask.b.sofintr && retval == 0) {
- - dwc_otg_transaction_type_e tr_type;
- - if ((qtd->qh->ep_type == UE_BULK)
- - && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) {
- - /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
- - return 0;
- - }
- - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- +
- + if(needs_scheduling) {
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- dwc_otg_hcd_queue_transactions(hcd, tr_type);
- }
- - DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- }
- -
- return retval;
- }
-
- @@ -524,6 +570,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
- {
- dwc_otg_qh_t *qh;
- dwc_otg_qtd_t *urb_qtd;
- + BUG_ON(!hcd);
- + BUG_ON(!dwc_otg_urb);
-
- #ifdef DEBUG /* integrity checks (Broadcom) */
-
- @@ -540,14 +588,17 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
- return -DWC_E_INVALID;
- }
- urb_qtd = dwc_otg_urb->qtd;
- + BUG_ON(!urb_qtd);
- if (urb_qtd->qh == NULL) {
- DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n");
- return -DWC_E_INVALID;
- }
- #else
- urb_qtd = dwc_otg_urb->qtd;
- + BUG_ON(!urb_qtd);
- #endif
- qh = urb_qtd->qh;
- + BUG_ON(!qh);
- if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- if (urb_qtd->in_process) {
- dump_channel_info(hcd, qh);
- @@ -571,6 +622,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
- */
- dwc_otg_hc_halt(hcd->core_if, qh->channel,
- DWC_OTG_HC_XFER_URB_DEQUEUE);
- +
- + dwc_otg_hcd_release_port(hcd, qh);
- }
- }
-
- @@ -687,6 +740,33 @@ static void reset_tasklet_func(void *dat
- dwc_otg_hcd->flags.b.port_reset_change = 1;
- }
-
- +static void completion_tasklet_func(void *ptr)
- +{
- + dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
- + struct urb *urb;
- + urb_tq_entry_t *item;
- + dwc_irqflags_t flags;
- +
- + /* This could just be spin_lock_irq */
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
- + item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
- + urb = item->urb;
- + DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
- + urb_tq_entries);
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + DWC_FREE(item);
- +
- + usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
- +
- + fiq_print(FIQDBG_PORTHUB, "COMPLETE");
- +
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + }
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + return;
- +}
- +
- static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
- {
- dwc_list_link_t *item;
- @@ -819,12 +899,14 @@ static void dwc_otg_hcd_free(dwc_otg_hcd
- } else if (dwc_otg_hcd->status_buf != NULL) {
- DWC_FREE(dwc_otg_hcd->status_buf);
- }
- + DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock);
- DWC_SPINLOCK_FREE(dwc_otg_hcd->lock);
- /* Set core_if's lock pointer to NULL */
- dwc_otg_hcd->core_if->lock = NULL;
-
- DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
- DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
- + DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
-
- #ifdef DWC_DEV_SRPCAP
- if (dwc_otg_hcd->core_if->power_down == 2 &&
- @@ -874,7 +956,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd
- DWC_LIST_INIT(&hcd->periodic_sched_ready);
- DWC_LIST_INIT(&hcd->periodic_sched_assigned);
- DWC_LIST_INIT(&hcd->periodic_sched_queued);
- -
- + DWC_TAILQ_INIT(&hcd->completed_urb_list);
- /*
- * Create a host channel descriptor for each host channel implemented
- * in the controller. Initialize the channel descriptor array.
- @@ -912,6 +994,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd
-
- /* Initialize reset tasklet. */
- hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
- +
- + hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
- + completion_tasklet_func, hcd);
- #ifdef DWC_DEV_SRPCAP
- if (hcd->core_if->power_down == 2) {
- /* Initialize Power on timer for Host power up in case hibernation */
- @@ -944,6 +1029,12 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd
- hcd->frame_list = NULL;
- hcd->frame_list_dma = 0;
- hcd->periodic_qh_count = 0;
- +
- + DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port));
- +#ifdef FIQ_DEBUG
- + DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc));
- +#endif
- +
- out:
- return retval;
- }
- @@ -1089,7 +1180,12 @@ static void assign_and_init_hc(dwc_otg_h
- uint32_t hub_addr, port_addr;
- hc->do_split = 1;
- hc->xact_pos = qtd->isoc_split_pos;
- - hc->complete_split = qtd->complete_split;
- + /* We don't need to do complete splits anymore */
- + if(fiq_split_enable)
- + hc->complete_split = qtd->complete_split = 0;
- + else
- + hc->complete_split = qtd->complete_split;
- +
- hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr);
- hc->hub_addr = (uint8_t) hub_addr;
- hc->port_addr = (uint8_t) port_addr;
- @@ -1236,6 +1332,65 @@ static void assign_and_init_hc(dwc_otg_h
- hc->qh = qh;
- }
-
- +/*
- +** Check the transaction to see if the port / hub has already been assigned for
- +** a split transaction
- +**
- +** Return 0 - Port is already in use
- +*/
- +int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
- +{
- + uint32_t hub_addr, port_addr;
- +
- + if(!fiq_split_enable)
- + return 0;
- +
- + hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
- +
- + if(hcd->hub_port[hub_addr] & (1 << port_addr))
- + {
- + fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count);
- +
- + qh->skip_count++;
- +
- + if(qh->skip_count > 40000)
- + {
- + printk_once(KERN_ERR "Error: Having to skip port allocation");
- + local_fiq_disable();
- + BUG();
- + return 0;
- + }
- + return 1;
- + }
- + else
- + {
- + qh->skip_count = 0;
- + hcd->hub_port[hub_addr] |= 1 << port_addr;
- + fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
- +#ifdef FIQ_DEBUG
- + hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd);
- +#endif
- + return 0;
- + }
- +}
- +void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
- +{
- + uint32_t hub_addr, port_addr;
- +
- + if(!fiq_split_enable)
- + return;
- +
- + hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
- +
- + hcd->hub_port[hub_addr] &= ~(1 << port_addr);
- +#ifdef FIQ_DEBUG
- + hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1;
- +#endif
- + fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
- +
- +}
- +
- +
- /**
- * This function selects transactions from the HCD transfer schedule and
- * assigns them to available host channels. It is called from HCD interrupt
- @@ -1249,9 +1404,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- {
- dwc_list_link_t *qh_ptr;
- dwc_otg_qh_t *qh;
- + dwc_otg_qtd_t *qtd;
- int num_channels;
- dwc_irqflags_t flags;
- - dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
- + dwc_spinlock_t *channel_lock = hcd->channel_lock;
- dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
-
- #ifdef DEBUG_SOF
- @@ -1269,11 +1425,29 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
-
- while (qh_ptr != &hcd->periodic_sched_ready &&
- !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
- +
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- +
- + if(qh->do_split) {
- + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- + if(!(qh->ep_type == UE_ISOCHRONOUS &&
- + (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
- + qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) {
- + if(dwc_otg_hcd_allocate_port(hcd, qh))
- + {
- + qh_ptr = DWC_LIST_NEXT(qh_ptr);
- + g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1);
- + continue;
- + }
- + }
- + }
- +
- if (microframe_schedule) {
- // Make sure we leave one channel for non periodic transactions.
- DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
- if (hcd->available_host_channels <= 1) {
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
- + if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh);
- break;
- }
- hcd->available_host_channels--;
- @@ -1294,8 +1468,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
- &qh->qh_list_entry);
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
- -
- - ret_val = DWC_OTG_TRANSACTION_PERIODIC;
- }
-
- /*
- @@ -1310,6 +1482,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- num_channels - hcd->periodic_channels) &&
- !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
-
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- +
- + /*
- + * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission
- + * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed
- + * cheeky devices that just hold off using NAKs
- + */
- + if (nak_holdoff_enable && qh->do_split) {
- + if (qh->nak_frame != 0xffff &&
- + dwc_full_frame_num(qh->nak_frame) ==
- + dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) {
- + /*
- + * Revisit: Need to avoid trampling on periodic scheduling.
- + * Currently we are safe because g_np_count != g_np_sent whenever we hit this,
- + * but if this behaviour is changed then periodic endpoints will get a slower
- + * polling rate.
- + */
- + g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM;
- + qh_ptr = DWC_LIST_NEXT(qh_ptr);
- + continue;
- + } else {
- + qh->nak_frame = 0xffff;
- + }
- + }
- +
- if (microframe_schedule) {
- DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
- if (hcd->available_host_channels < 1) {
- @@ -1322,7 +1519,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- last_sel_trans_num_nonper_scheduled++;
- #endif /* DEBUG_HOST_CHANNELS */
- }
- - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
-
- assign_and_init_hc(hcd, qh);
-
- @@ -1336,21 +1532,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- &qh->qh_list_entry);
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
-
- - if (ret_val == DWC_OTG_TRANSACTION_NONE) {
- - ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
- - } else {
- - ret_val = DWC_OTG_TRANSACTION_ALL;
- - }
- + g_np_sent++;
-
- if (!microframe_schedule)
- hcd->non_periodic_channels++;
- }
-
- + if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned))
- + ret_val |= DWC_OTG_TRANSACTION_PERIODIC;
- +
- + if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active))
- + ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC;
- +
- +
- #ifdef DEBUG_HOST_CHANNELS
- last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels;
- #endif /* DEBUG_HOST_CHANNELS */
- -
- - DWC_SPINLOCK_FREE(channel_lock);
- return ret_val;
- }
-
- @@ -1464,6 +1661,15 @@ static void process_periodic_channels(dw
-
- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
-
- + // Do not send a split start transaction any later than frame .6
- + // Note, we have to schedule a periodic in .5 to make it go in .6
- + if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- + {
- + qh_ptr = qh_ptr->next;
- + g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- + continue;
- + }
- +
- /*
- * Set a flag if we're queuing high-bandwidth in slave mode.
- * The flag prevents any halts to get into the request queue in
- @@ -1593,6 +1799,15 @@ static void process_non_periodic_channel
-
- qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
- qh_list_entry);
- +
- + // Do not send a split start transaction any later than frame .5
- + // non periodic transactions will start immediately in this uframe
- + if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- + {
- + g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- + break;
- + }
- +
- status =
- queue_transaction(hcd, qh->channel,
- tx_status.b.nptxfspcavail);
- @@ -3118,17 +3333,13 @@ dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc
- else
- dwc_otg_urb = DWC_ALLOC(size);
-
- - if (NULL != dwc_otg_urb)
- - dwc_otg_urb->packet_count = iso_desc_count;
- + if (dwc_otg_urb)
- + dwc_otg_urb->packet_count = iso_desc_count;
- else {
- - dwc_otg_urb->packet_count = 0;
- - if (size != 0) {
- - DWC_ERROR("**** DWC OTG HCD URB alloc - "
- - "%salloc of %db failed\n",
- - atomic_alloc?"atomic ":"", size);
- - }
- - }
- -
- + DWC_ERROR("**** DWC OTG HCD URB alloc - "
- + "%salloc of %db failed\n",
- + atomic_alloc?"atomic ":"", size);
- + }
- return dwc_otg_urb;
- }
-
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- @@ -168,10 +168,10 @@ typedef enum dwc_otg_control_phase {
-
- /** Transaction types. */
- typedef enum dwc_otg_transaction_type {
- - DWC_OTG_TRANSACTION_NONE,
- - DWC_OTG_TRANSACTION_PERIODIC,
- - DWC_OTG_TRANSACTION_NON_PERIODIC,
- - DWC_OTG_TRANSACTION_ALL
- + DWC_OTG_TRANSACTION_NONE = 0,
- + DWC_OTG_TRANSACTION_PERIODIC = 1,
- + DWC_OTG_TRANSACTION_NON_PERIODIC = 2,
- + DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC
- } dwc_otg_transaction_type_e;
-
- struct dwc_otg_qh;
- @@ -321,6 +321,11 @@ typedef struct dwc_otg_qh {
- */
- uint16_t sched_frame;
-
- + /*
- + ** Frame a NAK was received on this queue head, used to minimise NAK retransmission
- + */
- + uint16_t nak_frame;
- +
- /** (micro)frame at which last start split was initialized. */
- uint16_t start_split_frame;
-
- @@ -365,10 +370,19 @@ typedef struct dwc_otg_qh {
-
- uint16_t speed;
- uint16_t frame_usecs[8];
- +
- + uint32_t skip_count;
- } dwc_otg_qh_t;
-
- DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
-
- +typedef struct urb_tq_entry {
- + struct urb *urb;
- + DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
- +} urb_tq_entry_t;
- +
- +DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
- +
- /**
- * This structure holds the state of the HCD, including the non-periodic and
- * periodic schedules.
- @@ -546,9 +560,12 @@ struct dwc_otg_hcd {
- /* Tasket to do a reset */
- dwc_tasklet_t *reset_tasklet;
-
- + dwc_tasklet_t *completion_tasklet;
- + struct urb_list completed_urb_list;
- +
- /* */
- dwc_spinlock_t *lock;
- -
- + dwc_spinlock_t *channel_lock;
- /**
- * Private data that could be used by OS wrapper.
- */
- @@ -559,6 +576,12 @@ struct dwc_otg_hcd {
- /** Frame List */
- uint32_t *frame_list;
-
- + /** Hub - Port assignment */
- + int hub_port[128];
- +#ifdef FIQ_DEBUG
- + int hub_port_alloc[2048];
- +#endif
- +
- /** Frame List DMA address */
- dma_addr_t frame_list_dma;
-
- @@ -589,6 +612,10 @@ extern dwc_otg_transaction_type_e dwc_ot
- extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
- dwc_otg_transaction_type_e tr_type);
-
- +int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh);
- +void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh);
- +
- +
- /** @} */
-
- /** @name Interrupt Handler Functions */
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
- @@ -276,7 +276,7 @@ void dump_frame_list(dwc_otg_hcd_t * hcd
- static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- dwc_irqflags_t flags;
- - dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
- + dwc_spinlock_t *channel_lock = hcd->channel_lock;
-
- dwc_hc_t *hc = qh->channel;
- if (dwc_qh_is_non_per(qh)) {
- @@ -306,7 +306,6 @@ static void release_channel_ddma(dwc_otg
- dwc_memset(qh->desc_list, 0x00,
- sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
- }
- - DWC_SPINLOCK_FREE(channel_lock);
- }
-
- /**
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
- @@ -113,6 +113,11 @@ extern void dwc_otg_hcd_remove(dwc_otg_h
- */
- extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
-
- +/** This function is used to handle the fast interrupt
- + *
- + */
- +extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void);
- +
- /**
- * Returns private data set by
- * dwc_otg_hcd_set_priv_data function.
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- @@ -34,6 +34,12 @@
-
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- +#include "dwc_otg_mphi_fix.h"
- +
- +#include <linux/jiffies.h>
- +#include <mach/hardware.h>
- +#include <asm/fiq.h>
- +
-
- extern bool microframe_schedule;
-
- @@ -41,38 +47,487 @@ extern bool microframe_schedule;
- * This file contains the implementation of the HCD Interrupt handlers.
- */
-
- +/*
- + * Some globals to communicate between the FIQ and INTERRUPT
- + */
- +
- +void * dummy_send;
- +mphi_regs_t c_mphi_regs;
- +volatile void *dwc_regs_base;
- +int fiq_done, int_done;
- +
- +gintsts_data_t gintsts_saved = {.d32 = 0};
- +hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
- +hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
- +int split_out_xfersize[MAX_EPS_CHANNELS];
- +haint_data_t haint_saved;
- +
- +int g_next_sched_frame, g_np_count, g_np_sent;
- +static int mphi_int_count = 0 ;
- +
- +hcchar_data_t nak_hcchar;
- +hctsiz_data_t nak_hctsiz;
- +hcsplt_data_t nak_hcsplt;
- +int nak_count;
- +
- +int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
- +int split_start_frame[MAX_EPS_CHANNELS];
- +int queued_port[MAX_EPS_CHANNELS];
- +
- +#ifdef FIQ_DEBUG
- +char buffer[1000*16];
- +int wptr;
- +void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...)
- +{
- + FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB;
- + va_list args;
- + char text[17];
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
- + unsigned long flags;
- +
- + local_irq_save(flags);
- + local_fiq_disable();
- + if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR)
- + {
- + snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937);
- + va_start(args, fmt);
- + vsnprintf(text+8, 9, fmt, args);
- + va_end(args);
- +
- + memcpy(buffer + wptr, text, 16);
- + wptr = (wptr + 16) % sizeof(buffer);
- + }
- + local_irq_restore(flags);
- +}
- +#endif
- +
- +void notrace fiq_queue_request(int channel, int odd_frame)
- +{
- + hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
- + hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
- + hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) };
- +
- + if(hcsplt.b.spltena == 0)
- + {
- + fiq_print(FIQDBG_ERR, "SPLTENA ");
- + BUG();
- + }
- +
- + if(hcchar.b.epdir == 1)
- + {
- + fiq_print(FIQDBG_SCHED, "IN Ch %d", channel);
- + }
- + else
- + {
- + hctsiz.b.xfersize = 0;
- + fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel);
- + }
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32);
- +
- + hcsplt.b.compsplt = 1;
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32);
- +
- + // Send the Split complete
- + hcchar.b.chen = 1;
- + hcchar.b.oddfrm = odd_frame ? 1 : 0;
- +
- + // Post this for transmit on the next frame for periodic or this frame for non-periodic
- + fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN");
- +
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32);
- +}
- +
- +static int last_sof = -1;
- +
- +/*
- +** Function to handle the start of frame interrupt, choose whether we need to do anything and
- +** therefore trigger the main interrupt
- +**
- +** returns int != 0 - interrupt has been handled
- +*/
- +int diff;
- +
- +int notrace fiq_sof_handle(hfnum_data_t hfnum)
- +{
- + int handled = 0;
- + int i;
- +
- + // Just check that once we're running we don't miss a SOF
- + /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff)))
- + {
- + fiq_print(FIQDBG_ERR, "LASTSOF ");
- + fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7);
- + fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7);
- + BUG();
- + }*/
- +
- + // Only start remembering the last sof when the interrupt has been
- + // enabled (we don't check the mask to come in here...)
- + if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3))
- + last_sof = hfnum.b.frnum;
- +
- + for(i = 0; i < MAX_EPS_CHANNELS; i++)
- + {
- + if(complete_sched[i] != -1)
- + {
- + if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0))
- + {
- + fiq_queue_request(i, hfnum.b.frnum & 1);
- + complete_sched[i] = -1;
- + }
- + }
- +
- + if(complete_sched[i] != -1)
- + {
- + // This is because we've seen a split complete occur with no start...
- + // most likely because missed the complete 0x3fff frames ago!
- +
- + diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ;
- + if(diff > 32 && diff < 0x3f00)
- + {
- + fiq_print(FIQDBG_ERR, "SPLTMISS");
- + BUG();
- + }
- + }
- + }
- +
- + if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
- + {
- + /*
- + * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet
- + * g_next_sched_frame is the next frame we have periodic packets for
- + *
- + * if neither of these are required for this frame then just clear the interrupt
- + */
- + handled = 1;
- +
- + }
- +
- + return handled;
- +}
- +
- +int notrace port_id(hcsplt_data_t hcsplt)
- +{
- + return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8);
- +}
- +
- +int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum)
- +{
- + hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
- + hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
- + hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) };
- + hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) };
- + hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)};
- +
- + hcint_saved[channel].d32 |= hcint.d32;
- + hcintmsk_saved[channel].d32 = hcintmsk.d32;
- +
- + if(hcsplt.b.spltena)
- + {
- + fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt));
- + if(hcint.b.chhltd)
- + {
- + fiq_print(FIQDBG_SCHED, "CH HLT %d", channel);
- + fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]);
- + }
- + if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr)
- + {
- + queued_port[channel] = 0;
- + fiq_print(FIQDBG_ERR, "CHAN ERR");
- + }
- + if(hcint.b.xfercomp)
- + {
- + // Clear the port allocation and transmit anything also on this port
- + queued_port[channel] = 0;
- + fiq_print(FIQDBG_SCHED, "XFERCOMP");
- + }
- + if(hcint.b.nak)
- + {
- + queued_port[channel] = 0;
- + fiq_print(FIQDBG_SCHED, "NAK");
- + }
- + if(hcint.b.ack && !hcsplt.b.compsplt)
- + {
- + int i;
- +
- + // Do not complete isochronous out transactions
- + if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0)
- + {
- + queued_port[channel] = 0;
- + fiq_print(FIQDBG_SCHED, "ISOC_OUT");
- + }
- + else
- + {
- + // Make sure we check the port / hub combination that we sent this split on.
- + // Do not queue a second request to the same port
- + for(i = 0; i < MAX_EPS_CHANNELS; i++)
- + {
- + if(port_id(hcsplt) == queued_port[i])
- + {
- + fiq_print(FIQDBG_ERR, "PORTERR ");
- + //BUG();
- + }
- + }
- +
- + split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7;
- +
- + // Note, the size of an OUT is in the start split phase, not
- + // the complete split
- + split_out_xfersize[channel] = hctsiz.b.xfersize;
- +
- + hcint_saved[channel].b.chhltd = 0;
- + hcint_saved[channel].b.ack = 0;
- +
- + queued_port[channel] = port_id(hcsplt);
- +
- + if(hcchar.b.eptype & 1)
- + {
- + // Send the periodic complete in the same oddness frame as the ACK went...
- + fiq_queue_request(channel, !(hfnum.b.frnum & 1));
- + // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
- + }
- + else
- + {
- + // Schedule the split complete to occur later
- + complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2);
- + fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
- + }
- + }
- + }
- + if(hcint.b.nyet)
- + {
- + fiq_print(FIQDBG_ERR, "NYETERR1");
- + //BUG();
- + // Can transmit a split complete up to uframe .0 of the next frame
- + if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8))
- + {
- + // Send it next frame
- + if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc
- + {
- + fiq_print(FIQDBG_SCHED, "NYT:SEND");
- + fiq_queue_request(channel, !(hfnum.b.frnum & 1));
- + }
- + else
- + {
- + // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP)
- + complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
- + fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
- + }
- + hcint_saved[channel].b.chhltd = 0;
- + hcint_saved[channel].b.nyet = 0;
- + }
- + else
- + {
- + queued_port[channel] = 0;
- + fiq_print(FIQDBG_ERR, "NYETERR2");
- + //BUG();
- + }
- + }
- + }
- + else
- + {
- + /*
- + * If we have any of NAK, ACK, Datatlgerr active on a
- + * non-split channel, the sole reason is to reset error
- + * counts for a previously broken transaction. The FIQ
- + * will thrash on NAK IN and ACK OUT in particular so
- + * handle it "once" and allow the IRQ to do the rest.
- + */
- + hcint.d32 &= hcintmsk.d32;
- + if(hcint.b.nak)
- + {
- + hcintmsk.b.nak = 0;
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
- + }
- + if (hcint.b.ack)
- + {
- + hcintmsk.b.ack = 0;
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
- + }
- + }
- +
- + // Clear the interrupt, this will also clear the HAINT bit
- + FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32);
- + return hcint_saved[channel].d32 == 0;
- +}
- +
- +gintsts_data_t gintsts;
- +gintmsk_data_t gintmsk;
- +// triggered: The set of interrupts that were triggered
- +// handled: The set of interrupts that have been handled (no IRQ is
- +// required)
- +// keep: The set of interrupts we want to keep unmasked even though we
- +// want to trigger an IRQ to handle it (SOF and HCINTR)
- +gintsts_data_t triggered, handled, keep;
- +hfnum_data_t hfnum;
- +
- +void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void)
- +{
- +
- + /* entry takes care to store registers we will be treading on here */
- + asm __volatile__ (
- + "mov ip, sp ;"
- + /* stash FIQ and normal regs */
- + "stmdb sp!, {r0-r12, lr};"
- + /* !! THIS SETS THE FRAME, adjust to > sizeof locals */
- + "sub fp, ip, #512 ;"
- + );
- +
- + // Cannot put local variables at the beginning of the function
- + // because otherwise 'C' will play with the stack pointer. any locals
- + // need to be inside the following block
- + do
- + {
- + fiq_done++;
- + gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14);
- + gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18);
- + hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408);
- + triggered.d32 = gintsts.d32 & gintmsk.d32;
- + handled.d32 = 0;
- + keep.d32 = 0;
- + fiq_print(FIQDBG_INT, "FIQ ");
- + fiq_print(FIQDBG_INT, "%08x", gintsts.d32);
- + fiq_print(FIQDBG_INT, "%08x", gintmsk.d32);
- + if(gintsts.d32)
- + {
- + // If port enabled
- + if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5)
- + {
- + if(gintsts.b.sofintr)
- + {
- + if(fiq_sof_handle(hfnum))
- + {
- + handled.b.sofintr = 1; /* Handled in FIQ */
- + }
- + else
- + {
- + /* Keer interrupt unmasked */
- + keep.b.sofintr = 1;
- + }
- + {
- + // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing
- + // a start of frame interrupt
- + gintsts_data_t gintsts = { .b.sofintr = 1 };
- + FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
- + }
- + }
- +
- + if(fiq_split_enable && gintsts.b.hcintr)
- + {
- + int i;
- + haint_data_t haint;
- + haintmsk_data_t haintmsk;
- +
- + haint.d32 = FIQ_READ(dwc_regs_base + 0x414);
- + haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418);
- + haint.d32 &= haintmsk.d32;
- + haint_saved.d32 |= haint.d32;
- +
- + fiq_print(FIQDBG_INT, "hcintr");
- + fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414));
- +
- + // Go through each channel that has an enabled interrupt
- + for(i = 0; i < 16; i++)
- + if((haint.d32 >> i) & 1)
- + if(fiq_hcintr_handle(i, hfnum))
- + haint_saved.d32 &= ~(1 << i); /* this was handled */
- +
- + /* If we've handled all host channel interrupts then don't trigger the interrupt */
- + if(haint_saved.d32 == 0)
- + {
- + handled.b.hcintr = 1;
- + }
- + else
- + {
- + /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */
- + keep.b.hcintr = 1;
- + }
- +
- + {
- + gintsts_data_t gintsts = { .b.hcintr = 1 };
- +
- + // Always clear the channel interrupt
- + FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
- + }
- + }
- + }
- + else
- + {
- + last_sof = -1;
- + }
- + }
- +
- + // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep
- + gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32));
- + // Save those that were triggered but not handled
- + gintsts_saved.d32 |= triggered.d32 & ~handled.d32;
- + FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
- +
- + // Clear and save any unhandled interrupts and trigger the interrupt
- + if(gintsts_saved.d32)
- + {
- + /* To enable the MPHI interrupt (INT 32)
- + */
- + FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send);
- + FIQ_WRITE( c_mphi_regs.outddb, (1 << 29));
- +
- + mphi_int_count++;
- + }
- + }
- + while(0);
- +
- + mb();
- +
- + /* exit back to normal mode restoring everything */
- + asm __volatile__ (
- + /* return FIQ regs back to pristine state
- + * and get normal regs back
- + */
- + "ldmia sp!, {r0-r12, lr};"
- +
- + /* return */
- + "subs pc, lr, #4;"
- + );
- +}
- +
- /** This function handles interrupts for the HCD. */
- int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- {
- int retval = 0;
- + static int last_time;
-
- dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
- gintsts_data_t gintsts;
- + gintmsk_data_t gintmsk;
- + hfnum_data_t hfnum;
- +
- #ifdef DEBUG
- dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
-
- - //GRAYG: debugging
- - if (NULL == global_regs) {
- - DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p "
- - "core_if=%p\n",
- - dwc_otg_hcd, global_regs);
- - return retval;
- - }
- #endif
-
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- +
- /* Exit from ISR if core is hibernated */
- if (core_if->hibernation_suspend == 1) {
- - return retval;
- + goto exit_handler_routine;
- }
- DWC_SPINLOCK(dwc_otg_hcd->lock);
- /* Check if HOST Mode */
- if (dwc_otg_is_host_mode(core_if)) {
- - gintsts.d32 = dwc_otg_read_core_intr(core_if);
- + local_fiq_disable();
- + gintmsk.d32 |= gintsts_saved.d32;
- + gintsts.d32 |= gintsts_saved.d32;
- + gintsts_saved.d32 = 0;
- + local_fiq_enable();
- if (!gintsts.d32) {
- - DWC_SPINUNLOCK(dwc_otg_hcd->lock);
- - return 0;
- + goto exit_handler_routine;
- }
- + gintsts.d32 &= gintmsk.d32;
- +
- #ifdef DEBUG
- + // We should be OK doing this because the common interrupts should already have been serviced
- /* Don't print debug message in the interrupt handler on SOF */
- #ifndef DEBUG_SOF
- if (gintsts.d32 != DWC_SOF_INTR_MASK)
- @@ -88,10 +543,16 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n",
- gintsts.d32, core_if);
- #endif
- -
- - if (gintsts.b.sofintr) {
- + hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum);
- + if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
- + {
- + /* Note, we should never get here if the FIQ is doing it's job properly*/
- retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
- }
- + else if (gintsts.b.sofintr) {
- + retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
- + }
- +
- if (gintsts.b.rxstsqlvl) {
- retval |=
- dwc_otg_hcd_handle_rx_status_q_level_intr
- @@ -106,7 +567,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- /** @todo Implement i2cintr handler. */
- }
- if (gintsts.b.portintr) {
- +
- + gintmsk_data_t gintmsk = { .b.portintr = 1};
- retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
- }
- if (gintsts.b.hcintr) {
- retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
- @@ -138,11 +602,48 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- #endif
-
- }
- +
- +exit_handler_routine:
- +
- + if (fiq_fix_enable)
- + {
- + local_fiq_disable();
- + // Make sure that we don't clear the interrupt if we've still got pending work to do
- + if(gintsts_saved.d32 == 0)
- + {
- + /* Clear the MPHI interrupt */
- + DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16));
- + if (mphi_int_count >= 60)
- + {
- + DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16)));
- + while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17)))
- + ;
- + DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31));
- + mphi_int_count = 0;
- + }
- + int_done++;
- + }
- +
- + // Unmask handled interrupts
- + FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
- + //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1);
- +
- + local_fiq_enable();
- +
- + if((jiffies / HZ) > last_time)
- + {
- + /* Once a second output the fiq and irq numbers, useful for debug */
- + last_time = jiffies / HZ;
- + DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done);
- + }
- + }
- +
- DWC_SPINUNLOCK(dwc_otg_hcd->lock);
- return retval;
- }
-
- #ifdef DWC_TRACK_MISSED_SOFS
- +
- #warning Compiling code to track missed SOFs
- #define FRAME_NUM_ARRAY_SIZE 1000
- /**
- @@ -188,7 +689,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_
- dwc_list_link_t *qh_entry;
- dwc_otg_qh_t *qh;
- dwc_otg_transaction_type_e tr_type;
- - gintsts_data_t gintsts = {.d32 = 0 };
- + int did_something = 0;
- + int32_t next_sched_frame = -1;
-
- hfnum.d32 =
- DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
- @@ -212,17 +714,31 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_
- qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry);
- qh_entry = qh_entry->next;
- if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) {
- +
- /*
- * Move QH to the ready list to be executed next
- * (micro)frame.
- */
- DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
- &qh->qh_list_entry);
- +
- + did_something = 1;
- + }
- + else
- + {
- + if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame))
- + {
- + next_sched_frame = qh->sched_frame;
- + }
- }
- }
- +
- + g_next_sched_frame = next_sched_frame;
- +
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- dwc_otg_hcd_queue_transactions(hcd, tr_type);
- + did_something = 1;
- }
-
- /* Clear interrupt */
- @@ -511,6 +1027,15 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_o
-
- haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
-
- + // Overwrite with saved interrupts from fiq handler
- + if(fiq_split_enable)
- + {
- + local_fiq_disable();
- + haint.d32 = haint_saved.d32;
- + haint_saved.d32 = 0;
- + local_fiq_enable();
- + }
- +
- for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
- if (haint.b2.chint & (1 << i)) {
- retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
- @@ -551,7 +1076,10 @@ static uint32_t get_actual_xfer_length(d
- *short_read = (hctsiz.b.xfersize != 0);
- }
- } else if (hc->qh->do_split) {
- - length = qtd->ssplit_out_xfer_count;
- + if(fiq_split_enable)
- + length = split_out_xfersize[hc->hc_num];
- + else
- + length = qtd->ssplit_out_xfer_count;
- } else {
- length = hc->xfer_len;
- }
- @@ -595,7 +1123,6 @@ static int update_urb_state_xfer_comp(dw
- DWC_OTG_HC_XFER_COMPLETE,
- &short_read);
-
- -
- /* non DWORD-aligned buffer case handling. */
- if (hc->align_buff && xfer_length && hc->ep_is_in) {
- dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
- @@ -797,11 +1324,24 @@ static void release_channel(dwc_otg_hcd_
- dwc_otg_transaction_type_e tr_type;
- int free_qtd;
- dwc_irqflags_t flags;
- - dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC();
- + dwc_spinlock_t *channel_lock = hcd->channel_lock;
- +#ifdef FIQ_DEBUG
- + int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0;
- +#endif
- + int hog_port = 0;
-
- DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n",
- __func__, hc->hc_num, halt_status, hc->xfer_len);
-
- + if(fiq_split_enable && hc->do_split) {
- + if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) {
- + if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID ||
- + hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) {
- + hog_port = 1;
- + }
- + }
- + }
- +
- switch (halt_status) {
- case DWC_OTG_HC_XFER_URB_COMPLETE:
- free_qtd = 1;
- @@ -876,15 +1416,32 @@ cleanup:
-
- DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
- hcd->available_host_channels++;
- + fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels);
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
- }
-
- + if(fiq_split_enable && hc->do_split)
- + {
- + if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr)))
- + {
- + fiq_print(FIQDBG_ERR, "PRTNOTAL");
- + //BUG();
- + }
- + if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC ||
- + hc->ep_type == DWC_OTG_EP_TYPE_INTR)) {
- + hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr);
- +#ifdef FIQ_DEBUG
- + hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1;
- +#endif
- + fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp);
- + }
- + }
- +
- /* Try to queue more transfers now that there's a free channel. */
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- dwc_otg_hcd_queue_transactions(hcd, tr_type);
- }
- - DWC_SPINLOCK_FREE(channel_lock);
- }
-
- /**
- @@ -1295,6 +1852,17 @@ static int32_t handle_hc_nak_intr(dwc_ot
- "NAK Received--\n", hc->hc_num);
-
- /*
- + * When we get bulk NAKs then remember this so we holdoff on this qh until
- + * the beginning of the next frame
- + */
- + switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- + case UE_BULK:
- + case UE_CONTROL:
- + if (nak_holdoff_enable)
- + hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
- + }
- +
- + /*
- * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
- * interrupt. Re-start the SSPLIT transfer.
- */
- @@ -1316,7 +1884,11 @@ static int32_t handle_hc_nak_intr(dwc_ot
- * transfers in DMA mode for the sole purpose of
- * resetting the error count after a transaction error
- * occurs. The core will continue transferring data.
- + * Disable other interrupts unmasked for the same
- + * reason.
- */
- + disable_hc_int(hc_regs, datatglerr);
- + disable_hc_int(hc_regs, ack);
- qtd->error_count = 0;
- goto handle_nak_done;
- }
- @@ -1428,6 +2000,15 @@ static int32_t handle_hc_ack_intr(dwc_ot
- halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
- }
- } else {
- + /*
- + * An unmasked ACK on a non-split DMA transaction is
- + * for the sole purpose of resetting error counts. Disable other
- + * interrupts unmasked for the same reason.
- + */
- + if(hcd->core_if->dma_enable) {
- + disable_hc_int(hc_regs, datatglerr);
- + disable_hc_int(hc_regs, nak);
- + }
- qtd->error_count = 0;
-
- if (hc->qh->ping_state) {
- @@ -1490,8 +2071,10 @@ static int32_t handle_hc_nyet_intr(dwc_o
- hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- int frnum = dwc_otg_hcd_get_frame_number(hcd);
-
- + // With the FIQ running we only ever see the failed NYET
- if (dwc_full_frame_num(frnum) !=
- - dwc_full_frame_num(hc->qh->sched_frame)) {
- + dwc_full_frame_num(hc->qh->sched_frame) ||
- + fiq_split_enable) {
- /*
- * No longer in the same full speed frame.
- * Treat this as a transaction error.
- @@ -1778,13 +2361,28 @@ static int32_t handle_hc_datatglerr_intr
- dwc_otg_qtd_t * qtd)
- {
- DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- - "Data Toggle Error--\n", hc->hc_num);
- + "Data Toggle Error on %s transfer--\n",
- + hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
-
- - if (hc->ep_is_in) {
- + /* Data toggles on split transactions cause the hc to halt.
- + * restart transfer */
- + if(hc->qh->do_split)
- + {
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + } else if (hc->ep_is_in) {
- + /* An unmasked data toggle error on a non-split DMA transaction is
- + * for the sole purpose of resetting error counts. Disable other
- + * interrupts unmasked for the same reason.
- + */
- + if(hcd->core_if->dma_enable) {
- + disable_hc_int(hc_regs, ack);
- + disable_hc_int(hc_regs, nak);
- + }
- qtd->error_count = 0;
- - } else {
- - DWC_ERROR("Data Toggle Error on OUT transfer,"
- - "channel %d\n", hc->hc_num);
- }
-
- disable_hc_int(hc_regs, datatglerr);
- @@ -1862,10 +2460,10 @@ static inline int halt_status_ok(dwc_otg
- static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- - dwc_otg_qtd_t * qtd)
- + dwc_otg_qtd_t * qtd,
- + hcint_data_t hcint,
- + hcintmsk_data_t hcintmsk)
- {
- - hcint_data_t hcint;
- - hcintmsk_data_t hcintmsk;
- int out_nak_enh = 0;
-
- /* For core with OUT NAK enhancement, the flow for high-
- @@ -1897,8 +2495,11 @@ static void handle_hc_chhltd_intr_dma(dw
- }
-
- /* Read the HCINTn register to determine the cause for the halt. */
- - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- + if(!fiq_split_enable)
- + {
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- + }
-
- if (hcint.b.xfercomp) {
- /** @todo This is here because of a possible hardware bug. Spec
- @@ -1937,6 +2538,8 @@ static void handle_hc_chhltd_intr_dma(dw
- handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
- } else if (hcint.b.frmovrun) {
- handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.datatglerr) {
- + handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
- } else if (!out_nak_enh) {
- if (hcint.b.nyet) {
- /*
- @@ -1986,12 +2589,24 @@ static void handle_hc_chhltd_intr_dma(dw
- DWC_READ_REG32(&hcd->
- core_if->core_global_regs->
- gintsts));
- + /* Failthrough: use 3-strikes rule */
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- }
-
- }
- } else {
- DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
- hcint.d32);
- + /* Failthrough: use 3-strikes rule */
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- }
- }
-
- @@ -2009,13 +2624,15 @@ static void handle_hc_chhltd_intr_dma(dw
- static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- - dwc_otg_qtd_t * qtd)
- + dwc_otg_qtd_t * qtd,
- + hcint_data_t hcint,
- + hcintmsk_data_t hcintmsk)
- {
- DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- "Channel Halted--\n", hc->hc_num);
-
- if (hcd->core_if->dma_enable) {
- - handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd);
- + handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk);
- } else {
- #ifdef DEBUG
- if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
- @@ -2032,7 +2649,7 @@ static int32_t handle_hc_chhltd_intr(dwc
- int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
- {
- int retval = 0;
- - hcint_data_t hcint;
- + hcint_data_t hcint, hcint_orig;
- hcintmsk_data_t hcintmsk;
- dwc_hc_t *hc;
- dwc_otg_hc_regs_t *hc_regs;
- @@ -2042,15 +2659,33 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
-
- hc = dwc_otg_hcd->hc_ptr_array[num];
- hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
- + if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- + /* We are responding to a channel disable. Driver
- + * state is cleared - our qtd has gone away.
- + */
- + release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
- + return 1;
- + }
- qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
-
- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + hcint_orig = hcint;
- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- DWC_DEBUGPL(DBG_HCDV,
- " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
- hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
- hcint.d32 = hcint.d32 & hcintmsk.d32;
-
- + if(fiq_split_enable)
- + {
- + // replace with the saved interrupts from the fiq handler
- + local_fiq_disable();
- + hcint_orig.d32 = hcint_saved[num].d32;
- + hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32;
- + hcint_saved[num].d32 = 0;
- + local_fiq_enable();
- + }
- +
- if (!dwc_otg_hcd->core_if->dma_enable) {
- if (hcint.b.chhltd && hcint.d32 != 0x2) {
- hcint.b.chhltd = 0;
- @@ -2068,7 +2703,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
- hcint.b.nyet = 0;
- }
- if (hcint.b.chhltd) {
- - retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]);
- }
- if (hcint.b.ahberr) {
- retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- @@ -2080,7 +2715,8 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
- retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- }
- if (hcint.b.ack) {
- - retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + if(!hcint.b.chhltd)
- + retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- }
- if (hcint.b.nyet) {
- retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- @@ -2102,5 +2738,4 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
-
- return retval;
- }
- -
- #endif /* DWC_DEVICE_ONLY */
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- @@ -1,3 +1,4 @@
- +
- /* ==========================================================================
- * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $
- * $Revision: #20 $
- @@ -50,6 +51,7 @@
- #include <linux/dma-mapping.h>
- #include <linux/version.h>
- #include <asm/io.h>
- +#include <asm/fiq.h>
- #include <linux/usb.h>
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
- #include <../drivers/usb/core/hcd.h>
- @@ -67,6 +69,8 @@
- #include "dwc_otg_dbg.h"
- #include "dwc_otg_driver.h"
- #include "dwc_otg_hcd.h"
- +#include "dwc_otg_mphi_fix.h"
- +
- /**
- * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
- * qualified with its direction (possible 32 endpoints per device).
- @@ -76,6 +80,8 @@
-
- static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
-
- +extern bool fiq_fix_enable;
- +
- /** @name Linux HC Driver API Functions */
- /** @{ */
- /* manage i/o requests, device state */
- @@ -259,13 +265,15 @@ static void free_bus_bandwidth(struct us
-
- /**
- * Sets the final status of an URB and returns it to the device driver. Any
- - * required cleanup of the URB is performed.
- + * required cleanup of the URB is performed. The HCD lock should be held on
- + * entry.
- */
- static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
- dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
- {
- struct urb *urb = (struct urb *)urb_handle;
- -
- + urb_tq_entry_t *new_entry;
- + int rc = 0;
- if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
- __func__, urb, usb_pipedevice(urb->pipe),
- @@ -279,7 +287,7 @@ static int _complete(dwc_otg_hcd_t * hcd
- }
- }
- }
- -
- + new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
- urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
- /* Convert status value. */
- switch (status) {
- @@ -301,6 +309,9 @@ static int _complete(dwc_otg_hcd_t * hcd
- case -DWC_E_OVERFLOW:
- status = -EOVERFLOW;
- break;
- + case -DWC_E_SHUTDOWN:
- + status = -ESHUTDOWN;
- + break;
- default:
- if (status) {
- DWC_PRINTF("Uknown urb status %d\n", status);
- @@ -342,18 +353,33 @@ static int _complete(dwc_otg_hcd_t * hcd
- }
-
- DWC_FREE(dwc_otg_urb);
- -
- + if (!new_entry) {
- + DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
- + urb->status = -EPROTO;
- + /* don't schedule the tasklet -
- + * directly return the packet here with error. */
- #if USB_URB_EP_LINKING
- - usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
- + usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
- #endif
- - DWC_SPINUNLOCK(hcd->lock);
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- - usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
- + usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
- #else
- - usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
- + usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
- #endif
- - DWC_SPINLOCK(hcd->lock);
- -
- + } else {
- + new_entry->urb = urb;
- +#if USB_URB_EP_LINKING
- + rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
- + if(0 == rc) {
- + usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
- + }
- +#endif
- + if(0 == rc) {
- + DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
- + urb_tq_entries);
- + DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
- + }
- + }
- return 0;
- }
-
- @@ -366,6 +392,16 @@ static struct dwc_otg_hcd_function_ops h
- .get_b_hnp_enable = _get_b_hnp_enable,
- };
-
- +static struct fiq_handler fh = {
- + .name = "usb_fiq",
- +};
- +struct fiq_stack_s {
- + int magic1;
- + uint8_t stack[2048];
- + int magic2;
- +} fiq_stack;
- +
- +extern mphi_regs_t c_mphi_regs;
- /**
- * Initializes the HCD. This function allocates memory for and initializes the
- * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
- @@ -379,6 +415,7 @@ int hcd_init(dwc_bus_dev_t *_dev)
- dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- int retval = 0;
- u64 dmamask;
- + struct pt_regs regs;
-
- DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev);
-
- @@ -396,6 +433,20 @@ int hcd_init(dwc_bus_dev_t *_dev)
- pci_set_consistent_dma_mask(_dev, dmamask);
- #endif
-
- + if (fiq_fix_enable)
- + {
- + // Set up fiq
- + claim_fiq(&fh);
- + set_fiq_handler(__FIQ_Branch, 4);
- + memset(®s,0,sizeof(regs));
- + regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq;
- + regs.ARM_r9 = (long)0;
- + regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4;
- + set_fiq_regs(®s);
- + fiq_stack.magic1 = 0xdeadbeef;
- + fiq_stack.magic2 = 0xaa995566;
- + }
- +
- /*
- * Allocate memory for the base HCD plus the DWC OTG HCD.
- * Initialize the base HCD.
- @@ -415,6 +466,30 @@ int hcd_init(dwc_bus_dev_t *_dev)
-
- hcd->regs = otg_dev->os_dep.base;
-
- + if (fiq_fix_enable)
- + {
- + volatile extern void *dwc_regs_base;
- +
- + //Set the mphi periph to the required registers
- + c_mphi_regs.base = otg_dev->os_dep.mphi_base;
- + c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c;
- + c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28;
- + c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c;
- + c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50;
- +
- + dwc_regs_base = otg_dev->os_dep.base;
- +
- + //Enable mphi peripheral
- + writel((1<<31),c_mphi_regs.ctrl);
- +#ifdef DEBUG
- + if (readl(c_mphi_regs.ctrl) & 0x80000000)
- + DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n");
- + else
- + DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n");
- +#endif
- + // Enable FIQ interrupt from USB peripheral
- + enable_fiq(INTERRUPT_VC_USB);
- + }
- /* Initialize the DWC OTG HCD. */
- dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
- if (!dwc_otg_hcd) {
- @@ -607,9 +682,7 @@ static int dwc_otg_urb_enqueue(struct us
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
- struct usb_host_endpoint *ep = urb->ep;
- #endif
- -#if USB_URB_EP_LINKING
- dwc_irqflags_t irqflags;
- -#endif
- void **ref_ep_hcpriv = &ep->hcpriv;
- dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- dwc_otg_hcd_urb_t *dwc_otg_urb;
- @@ -661,9 +734,8 @@ static int dwc_otg_urb_enqueue(struct us
- if(dwc_otg_urb == NULL)
- return -ENOMEM;
-
- - urb->hcpriv = dwc_otg_urb;
- - if (!dwc_otg_urb && urb->number_of_packets)
- - return -ENOMEM;
- + if (!dwc_otg_urb && urb->number_of_packets)
- + return -ENOMEM;
-
- dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe),
- usb_pipeendpoint(urb->pipe), ep_type,
- @@ -703,37 +775,42 @@ static int dwc_otg_urb_enqueue(struct us
- iso_frame_desc[i].length);
- }
-
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
- + urb->hcpriv = dwc_otg_urb;
- #if USB_URB_EP_LINKING
- - DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
- retval = usb_hcd_link_urb_to_ep(hcd, urb);
- - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
- if (0 == retval)
- #endif
- - {
- - retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
- - /*(dwc_otg_qh_t **)*/
- - ref_ep_hcpriv,
- - mem_flags == GFP_ATOMIC ? 1 : 0);
- - if (0 == retval) {
- - if (alloc_bandwidth) {
- - allocate_bus_bandwidth(hcd,
- - dwc_otg_hcd_get_ep_bandwidth(
- - dwc_otg_hcd, *ref_ep_hcpriv),
- - urb);
- - }
- - } else {
- + {
- + retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
- + /*(dwc_otg_qh_t **)*/
- + ref_ep_hcpriv, 1);
- + if (0 == retval) {
- + if (alloc_bandwidth) {
- + allocate_bus_bandwidth(hcd,
- + dwc_otg_hcd_get_ep_bandwidth(
- + dwc_otg_hcd, *ref_ep_hcpriv),
- + urb);
- + }
- + } else {
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
- #if USB_URB_EP_LINKING
- - dwc_irqflags_t irqflags;
- - DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
- - DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
- - usb_hcd_unlink_urb_from_ep(hcd, urb);
- - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
- -#endif
- - if (retval == -DWC_E_NO_DEVICE) {
- - retval = -ENODEV;
- - }
- - }
- - }
- + usb_hcd_unlink_urb_from_ep(hcd, urb);
- +#endif
- + DWC_FREE(dwc_otg_urb);
- + urb->hcpriv = NULL;
- + if (retval == -DWC_E_NO_DEVICE)
- + retval = -ENODEV;
- + }
- + }
- +#if USB_URB_EP_LINKING
- + else
- + {
- + DWC_FREE(dwc_otg_urb);
- + urb->hcpriv = NULL;
- + }
- +#endif
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
- return retval;
- }
-
- @@ -777,6 +854,8 @@ static int dwc_otg_urb_dequeue(struct us
- usb_hcd_unlink_urb_from_ep(hcd, urb);
- #endif
- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
- +
- +
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- usb_hcd_giveback_urb(hcd, urb);
- #else
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- @@ -41,6 +41,7 @@
-
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- +#include "dwc_otg_mphi_fix.h"
-
- extern bool microframe_schedule;
-
- @@ -182,6 +183,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_ot
- if (microframe_schedule)
- qh->speed = dev_speed;
-
- + qh->nak_frame = 0xffff;
-
- if (((dev_speed == USB_SPEED_LOW) ||
- (dev_speed == USB_SPEED_FULL)) &&
- @@ -191,6 +193,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_ot
- dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
- hub_port);
- qh->do_split = 1;
- + qh->skip_count = 0;
- }
-
- if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
- @@ -573,6 +576,9 @@ static int check_max_xfer_size(dwc_otg_h
- return status;
- }
-
- +
- +extern int g_next_sched_frame, g_np_count, g_np_sent;
- +
- /**
- * Schedules an interrupt or isochronous transfer in the periodic schedule.
- *
- @@ -631,8 +637,13 @@ static int schedule_periodic(dwc_otg_hcd
- DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
- }
- else {
- - /* Always start in the inactive schedule. */
- - DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
- + if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame))
- + {
- + g_next_sched_frame = qh->sched_frame;
- +
- + }
- + /* Always start in the inactive schedule. */
- + DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
- }
-
- if (!microframe_schedule) {
- @@ -646,6 +657,7 @@ static int schedule_periodic(dwc_otg_hcd
- return status;
- }
-
- +
- /**
- * This function adds a QH to either the non periodic or periodic schedule if
- * it is not already in the schedule. If the QH is already in the schedule, no
- @@ -668,6 +680,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * h
- /* Always start in the inactive schedule. */
- DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
- &qh->qh_list_entry);
- + g_np_count++;
- } else {
- status = schedule_periodic(hcd, qh);
- if ( !hcd->periodic_qh_count ) {
- @@ -727,6 +740,9 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t
- hcd->non_periodic_qh_ptr->next;
- }
- DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
- +
- + // If we've removed the last non-periodic entry then there are none left!
- + g_np_count = g_np_sent;
- } else {
- deschedule_periodic(hcd, qh);
- hcd->periodic_qh_count--;
- @@ -755,6 +771,24 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
- int sched_next_periodic_split)
- {
- if (dwc_qh_is_non_per(qh)) {
- +
- + dwc_otg_qh_t *qh_tmp;
- + dwc_list_link_t *qh_list;
- + DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive)
- + {
- + qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry);
- + if(qh_tmp == qh)
- + {
- + /*
- + * FIQ is being disabled because this one nevers gets a np_count increment
- + * This is still not absolutely correct, but it should fix itself with
- + * just an unnecessary extra interrupt
- + */
- + g_np_sent = g_np_count;
- + }
- + }
- +
- +
- dwc_otg_hcd_qh_remove(hcd, qh);
- if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- /* Add back to inactive non-periodic schedule. */
- @@ -768,6 +802,7 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
- if (sched_next_periodic_split) {
-
- qh->sched_frame = frame_number;
- +
- if (dwc_frame_num_le(frame_number,
- dwc_frame_num_inc
- (qh->start_split_frame,
- @@ -816,6 +851,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
- DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
- &qh->qh_list_entry);
- } else {
- + if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame))
- + {
- + g_next_sched_frame = qh->sched_frame;
- + }
- +
- DWC_LIST_MOVE_HEAD
- (&hcd->periodic_sched_inactive,
- &qh->qh_list_entry);
- @@ -880,6 +920,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t
- * QH to place the QTD into. If it does not find a QH, then it will create a
- * new QH. If the QH to which the QTD is added is not currently scheduled, it
- * is placed into the proper schedule based on its EP type.
- + * HCD lock must be held and interrupts must be disabled on entry
- *
- * @param[in] qtd The QTD to add
- * @param[in] hcd The DWC HCD structure
- @@ -892,8 +933,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
- dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
- {
- int retval = 0;
- - dwc_irqflags_t flags;
- -
- dwc_otg_hcd_urb_t *urb = qtd->urb;
-
- /*
- @@ -903,18 +942,16 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
- if (*qh == NULL) {
- *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
- if (*qh == NULL) {
- - retval = -1;
- + retval = -DWC_E_NO_MEMORY;
- goto done;
- }
- }
- - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- retval = dwc_otg_hcd_qh_add(hcd, *qh);
- if (retval == 0) {
- DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
- qtd_list_entry);
- + qtd->qh = *qh;
- }
- - DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- -
- done:
-
- return retval;
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
- @@ -0,0 +1,113 @@
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_dbg.h"
- +
- +void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name)
- +{
- + DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n"
- + "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n"
- + "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n"
- + "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n"
- + "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n"
- + "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n"
- + "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n"
- + "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n"
- + "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
- + function_name,
- + gintsts.b.curmode,
- + gintsts.b.modemismatch,
- + gintsts.b.otgintr,
- + gintsts.b.sofintr,
- + gintsts.b.rxstsqlvl,
- + gintsts.b.nptxfempty,
- + gintsts.b.ginnakeff,
- + gintsts.b.goutnakeff,
- + gintsts.b.ulpickint,
- + gintsts.b.i2cintr,
- + gintsts.b.erlysuspend,
- + gintsts.b.usbsuspend,
- + gintsts.b.usbreset,
- + gintsts.b.enumdone,
- + gintsts.b.isooutdrop,
- + gintsts.b.eopframe,
- + gintsts.b.restoredone,
- + gintsts.b.epmismatch,
- + gintsts.b.inepint,
- + gintsts.b.outepintr,
- + gintsts.b.incomplisoin,
- + gintsts.b.incomplisoout,
- + gintsts.b.fetsusp,
- + gintsts.b.resetdet,
- + gintsts.b.portintr,
- + gintsts.b.hcintr,
- + gintsts.b.ptxfempty,
- + gintsts.b.lpmtranrcvd,
- + gintsts.b.conidstschng,
- + gintsts.b.disconnect,
- + gintsts.b.sessreqintr,
- + gintsts.b.wkupintr);
- + return;
- +}
- +
- +void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name)
- +{
- + DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n"
- + "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n"
- + "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n"
- + "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n"
- + "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n"
- + "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n"
- + "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n"
- + "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n"
- + "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
- + function_name,
- + gintmsk.b.modemismatch,
- + gintmsk.b.otgintr,
- + gintmsk.b.sofintr,
- + gintmsk.b.rxstsqlvl,
- + gintmsk.b.nptxfempty,
- + gintmsk.b.ginnakeff,
- + gintmsk.b.goutnakeff,
- + gintmsk.b.ulpickint,
- + gintmsk.b.i2cintr,
- + gintmsk.b.erlysuspend,
- + gintmsk.b.usbsuspend,
- + gintmsk.b.usbreset,
- + gintmsk.b.enumdone,
- + gintmsk.b.isooutdrop,
- + gintmsk.b.eopframe,
- + gintmsk.b.restoredone,
- + gintmsk.b.epmismatch,
- + gintmsk.b.inepintr,
- + gintmsk.b.outepintr,
- + gintmsk.b.incomplisoin,
- + gintmsk.b.incomplisoout,
- + gintmsk.b.fetsusp,
- + gintmsk.b.resetdet,
- + gintmsk.b.portintr,
- + gintmsk.b.hcintr,
- + gintmsk.b.ptxfempty,
- + gintmsk.b.lpmtranrcvd,
- + gintmsk.b.conidstschng,
- + gintmsk.b.disconnect,
- + gintmsk.b.sessreqintr,
- + gintmsk.b.wkupintr);
- + return;
- +}
- +
- +void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name)
- +{
- + DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n"
- + "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n"
- + "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n"
- + "mvic: %1i\n",
- + function_name,
- + gotgint.b.sesenddet,
- + gotgint.b.sesreqsucstschng,
- + gotgint.b.hstnegsucstschng,
- + gotgint.b.hstnegdet,
- + gotgint.b.adevtoutchng,
- + gotgint.b.debdone,
- + gotgint.b.mvic);
- +
- + return;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
- @@ -0,0 +1,48 @@
- +#ifndef __DWC_OTG_MPHI_FIX_H__
- +#define __DWC_OTG_MPHI_FIX_H__
- +#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_))
- +#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_))
- +
- +typedef struct {
- + volatile void* base;
- + volatile void* ctrl;
- + volatile void* outdda;
- + volatile void* outddb;
- + volatile void* intstat;
- +} mphi_regs_t;
- +
- +void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name);
- +void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name);
- +void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name);
- +
- +extern gintsts_data_t gintsts_saved;
- +
- +#ifdef DEBUG
- +#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__)
- +#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__)
- +#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__)
- +
- +#else
- +#define DWC_DBG_PRINT_CORE_INT(_arg_)
- +#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_)
- +#define DWC_DBG_PRINT_OTG_INT(_arg_)
- +
- +#endif
- +
- +typedef enum {
- + FIQDBG_SCHED = (1 << 0),
- + FIQDBG_INT = (1 << 1),
- + FIQDBG_ERR = (1 << 2),
- + FIQDBG_PORTHUB = (1 << 3),
- +} FIQDBG_T;
- +
- +void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...);
- +#ifdef FIQ_DEBUG
- +#define fiq_print _fiq_print
- +#else
- +#define fiq_print(x, y, ...)
- +#endif
- +
- +extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable;
- +
- +#endif
- --- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
- @@ -97,6 +97,9 @@ typedef struct os_dependent {
- /** Register offset for Diagnostic API */
- uint32_t reg_offset;
-
- + /** Base address for MPHI peripheral */
- + void *mphi_base;
- +
- #ifdef LM_INTERFACE
- struct lm_device *lmdev;
- #elif defined(PCI_INTERFACE)
- --- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
- @@ -4276,7 +4276,7 @@ do { \
- && (pcd->ep0state == EP0_OUT_DATA_PHASE))
- status.d32 = core_if->dev_if->out_desc_addr->status.d32;
- if (pcd->ep0state == EP0_OUT_STATUS_PHASE)
- - status.d32 = status.d32 = core_if->dev_if->
- + status.d32 = core_if->dev_if->
- out_desc_addr->status.d32;
-
- if (status.b.sr) {
|