| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-12-06 GuEe-GUI first version
- * 2023-02-25 GuEe-GUI add EDMA support
- */
- #include <rtthread.h>
- #define DBG_TAG "sdio.dw"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <mmu.h>
- #include <cpuport.h>
- #include "sdio-dw.h"
- /* Common flag combinations */
- #define PINT(x) SDIO_DW_INT_##x
- #define SDIO_DW_DATA_ERROR_FLAGS (PINT(DRTO) | PINT(DCRC) | PINT(HTO) | PINT(SBE) | PINT(EBE) | PINT(HLE))
- #define SDIO_DW_CMD_ERROR_FLAGS (PINT(RTO) | PINT(RCRC) | PINT(RESP_ERR) | PINT(HLE))
- #define SDIO_DW_ERROR_FLAGS (SDIO_DW_DATA_ERROR_FLAGS | SDIO_DW_CMD_ERROR_FLAGS)
- #define SDIO_DW_SEND_STATUS 1
- #define SDIO_DW_RECV_STATUS 2
- #define SDIO_DW_DMA_THRESHOLD 16
- #define SDIO_DW_FREQ_HZ_MAX 200000000
- #define SDIO_DW_FREQ_HZ_MIN 100000
- #define PINTC(x) SDIO_DW_IDMAC_INT_##x
- #define SDIO_DW_IDMAC_INT_CLR (PINTC(AI) | PINTC(NI) | PINTC(CES) | PINTC(DU) | PINTC(FBE) | PINTC(RI) | PINTC(TI))
- #define DESC_RING_BUF_SZ ARCH_PAGE_SIZE
- #define NSEC_PER_SEC 1000000000L
- #define USEC_PER_MSEC 1000L
- #define MSEC_PER_SEC 1000L
- struct idmac_desc64
- {
- rt_uint32_t des0; /* Control descriptor */
- #define IDMAC_OWN_CLR64(x) !((x) & rt_cpu_to_le32(IDMAC_DES0_OWN))
- rt_uint32_t des1; /* Reserved */
- rt_uint32_t des2; /* Buffer sizes */
- #define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
- ((d)->des2 = ((d)->des2 & rt_cpu_to_le32(0x03ffe000)) | ((rt_cpu_to_le32(s)) & rt_cpu_to_le32(0x1fff)))
- rt_uint32_t des3; /* Reserved */
- rt_uint32_t des4; /* Lower 32-bits of buffer address pointer 1 */
- rt_uint32_t des5; /* Upper 32-bits of buffer address pointer 1 */
- rt_uint32_t des6; /* Lower 32-bits of next descriptor address */
- rt_uint32_t des7; /* Upper 32-bits of next descriptor address */
- };
- struct idmac_desc32
- {
- rt_le32_t des0; /* Control Descriptor */
- #define IDMAC_DES0_DIC RT_BIT(1)
- #define IDMAC_DES0_LD RT_BIT(2)
- #define IDMAC_DES0_FD RT_BIT(3)
- #define IDMAC_DES0_CH RT_BIT(4)
- #define IDMAC_DES0_ER RT_BIT(5)
- #define IDMAC_DES0_CES RT_BIT(30)
- #define IDMAC_DES0_OWN RT_BIT(31)
- rt_le32_t des1; /* Buffer sizes */
- #define IDMAC_32ADDR_SET_BUFFER1_SIZE(d, s) \
- ((d)->des1 = ((d)->des1 & rt_cpu_to_le32(0x03ffe000)) | (rt_cpu_to_le32((s) & 0x1fff)))
- rt_le32_t des2; /* Buffer 1 physical address */
- rt_le32_t des3; /* Buffer 2 physical address */
- };
- /* Each descriptor can transfer up to 4KB of data in chained mode */
- #define DW_MCI_DESC_DATA_LENGTH 0x1000
- static rt_bool_t sdio_dw_ctrl_reset(struct sdio_dw *sd, rt_uint32_t reset)
- {
- rt_uint32_t ctrl;
- rt_tick_t start;
- int timeout = rt_tick_from_millisecond(500);
- ctrl = sdio_dw_readl(sd, CTRL);
- ctrl |= reset;
- sdio_dw_writel(sd, CTRL, ctrl);
- start = rt_tick_get();
- while ((sdio_dw_readl(sd, CTRL) & reset))
- {
- if ((rt_tick_get() - start) > timeout)
- {
- LOG_E("Timeout resetting block (ctrl reset 0x%x)", ctrl & reset);
- return RT_FALSE;
- }
- rt_hw_cpu_relax();
- }
- return RT_TRUE;
- }
- static void sdio_dw_wait_while_busy(struct sdio_dw *sd, rt_uint32_t cmd_flags)
- {
- if ((cmd_flags & SDIO_DW_CMD_PRV_DAT_WAIT) && !(cmd_flags & SDIO_DW_CMD_VOLT_SWITCH))
- {
- rt_tick_t start = rt_tick_get();
- int timeout = rt_tick_from_millisecond(500);
- while ((sdio_dw_readl(sd, STATUS) & SDIO_DW_STATUS_BUSY))
- {
- if ((rt_tick_get() - start) > timeout)
- {
- LOG_E("Wait busy fail");
- break;
- }
- rt_hw_cpu_relax();
- }
- }
- }
- static void sdio_dw_send_cmd(struct sdio_dw_slot *slot, rt_uint32_t cmd, rt_uint32_t arg)
- {
- rt_tick_t start;
- struct sdio_dw *sd = slot->sd;
- int timeout = rt_tick_from_millisecond(500);
- sdio_dw_writel(sd, CMDARG, arg);
- rt_hw_wmb();
- sdio_dw_wait_while_busy(sd, cmd);
- sdio_dw_writel(sd, CMD, SDIO_DW_CMD_START | cmd);
- start = rt_tick_get();
- while ((sdio_dw_readl(sd, CMD) & SDIO_DW_CMD_START))
- {
- if ((rt_tick_get() - start) > timeout)
- {
- LOG_E("Wait command start fail");
- break;
- }
- rt_hw_cpu_relax();
- }
- }
- rt_inline void sdio_dw_set_cto(struct sdio_dw *sd)
- {
- rt_ubase_t level;
- rt_uint32_t cto_clks, cto_div, cto_ms;
- cto_clks = sdio_dw_readl(sd, TMOUT) & 0xff;
- cto_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2;
- if (cto_div == 0)
- {
- cto_div = 1;
- }
- cto_ms = RT_DIV_ROUND_UP_ULL((rt_uint64_t)MSEC_PER_SEC * cto_clks * cto_div,
- sd->bus_hz);
- /* Add a bit spare time */
- cto_ms += 10;
- /*
- * The durations we're working with are fairly short so we have to be extra
- * careful about synchronization here. Specifically in hardware a command
- * timeout is _at most_ 5.1 ms, so that means we expect an interrupt
- * (either command done or timeout) to come rather quickly after the
- * sdio_dw_writel. ...but just in case we have a long interrupt latency
- * let's add a bit of paranoia.
- *
- * In general we'll assume that at least an interrupt will be asserted in
- * hardware by the time the cto_timer runs. ...and if it hasn't been
- * asserted in hardware by that time then we'll assume it'll never come.
- */
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE))
- {
- rt_tick_t tick = rt_tick_from_millisecond(cto_ms) + 1;
- rt_timer_control(&sd->cto_timer, RT_TIMER_CTRL_SET_TIME, &tick);
- rt_timer_start(&sd->cto_timer);
- }
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- static void sdio_dw_start_cmd(struct sdio_dw_slot *slot, rt_uint32_t cmd,
- rt_uint32_t arg)
- {
- struct sdio_dw *sd = slot->sd;
- sdio_dw_writel(sd, CMDARG, arg);
- rt_hw_wmb();
- sdio_dw_wait_while_busy(sd, cmd);
- sdio_dw_writel(sd, CMD, SDIO_DW_CMD_START | cmd);
- /* Response expected command only */
- if ((cmd & SDIO_DW_CMD_RESP_EXP))
- {
- sdio_dw_set_cto(sd);
- }
- }
- rt_inline void send_stop_abort(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- struct rt_mmcsd_cmd *stop = &sd->stop_abort;
- sdio_dw_start_cmd(sd->slot, sd->stop_cmdr, stop->arg);
- }
- /* DMA interface functions */
- static void sdio_dw_stop_dma(struct sdio_dw *sd)
- {
- if (sd->using_dma)
- {
- sd->dma_ops->stop(sd);
- sd->dma_ops->cleanup(sd);
- }
- /* Data transfer was stopped by the interrupt handler */
- rt_bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- }
- static rt_uint32_t sdio_dw_prep_stop_abort(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd)
- {
- rt_uint32_t cmdr;
- struct rt_mmcsd_cmd *stop;
- if (!cmd->data)
- {
- return 0;
- }
- stop = &sd->stop_abort;
- cmdr = cmd->cmd_code;
- rt_memset(stop, 0, sizeof(*stop));
- if (cmdr == READ_SINGLE_BLOCK ||
- cmdr == READ_MULTIPLE_BLOCK ||
- cmdr == WRITE_BLOCK ||
- cmdr == WRITE_MULTIPLE_BLOCK ||
- cmdr == SEND_TUNING_BLOCK ||
- cmdr == SEND_TUNING_BLOCK_HS200 ||
- cmdr == GEN_CMD)
- {
- stop->cmd_code = STOP_TRANSMISSION;
- stop->arg = 0;
- stop->flags = CMD_AC;
- }
- else if (cmdr == SD_IO_RW_EXTENDED)
- {
- stop->cmd_code = SD_IO_RW_DIRECT;
- stop->arg |= (1 << 31) | (0 << 28) | ((cmd->arg >> 28) & 0x7);
- stop->flags = RESP_SPI_R5 | CMD_AC;
- }
- else
- {
- return 0;
- }
- cmdr = stop->cmd_code | SDIO_DW_CMD_STOP | SDIO_DW_CMD_RESP_CRC | SDIO_DW_CMD_RESP_EXP;
- if (!rt_bitmap_test_bit(&sd->slot->flags, DW_MMC_CARD_NO_USE_HOLD))
- {
- cmdr |= SDIO_DW_CMD_USE_HOLD_REG;
- }
- return cmdr;
- }
- static void sdio_dw_idmac_reset(struct sdio_dw *sd)
- {
- /* Software reset of DMA */
- sdio_dw_writel(sd, BMOD, sdio_dw_readl(sd, BMOD) | SDIO_DW_IDMAC_SWRESET);
- }
- static rt_err_t sdio_dw_idmac_init(struct sdio_dw *sd)
- {
- int i;
- rt_err_t err = RT_EOK;
- if (sd->dma_64bit_address)
- {
- struct idmac_desc64 *p;
- /* Number of descriptors in the ring buffer */
- sd->ring_size = DESC_RING_BUF_SZ / sizeof(struct idmac_desc64);
- /* Forward link the descriptor list */
- for (i = 0, p = sd->dma_buf; i < sd->ring_size - 1; ++i, ++p)
- {
- p->des6 = (sd->dma_buf_phy + (sizeof(struct idmac_desc64) * (i + 1))) & 0xffffffff;
- p->des7 = (rt_uint64_t)(sd->dma_buf_phy + (sizeof(struct idmac_desc64) * (i + 1))) >> 32;
- /* Initialize reserved and buffer size fields to "0" */
- p->des0 = 0;
- p->des1 = 0;
- p->des2 = 0;
- p->des3 = 0;
- }
- /* Set the last descriptor as the end-of-ring descriptor */
- p->des6 = sd->dma_buf_phy & 0xffffffff;
- p->des7 = (rt_uint64_t)sd->dma_buf_phy >> 32;
- p->des0 = IDMAC_DES0_ER;
- }
- else
- {
- struct idmac_desc32 *p;
- /* Number of descriptors in the ring buffer */
- sd->ring_size = DESC_RING_BUF_SZ / sizeof(struct idmac_desc32);
- /* Forward link the descriptor list */
- for (i = 0, p = sd->dma_buf; i < sd->ring_size - 1; ++i, ++p)
- {
- p->des3 = rt_cpu_to_le32(sd->dma_buf_phy + (sizeof(struct idmac_desc32) * (i + 1)));
- p->des0 = 0;
- p->des1 = 0;
- }
- /* Set the last descriptor as the end-of-ring descriptor */
- p->des3 = rt_cpu_to_le32(sd->dma_buf_phy);
- p->des0 = rt_cpu_to_le32(IDMAC_DES0_ER);
- }
- sdio_dw_idmac_reset(sd);
- if (sd->dma_64bit_address)
- {
- /* Mask out interrupts - get Tx & Rx complete only */
- sdio_dw_writel(sd, IDSTS64, SDIO_DW_IDMAC_INT_CLR);
- sdio_dw_writel(sd, IDINTEN64, PINTC(NI) | PINTC(RI) | PINTC(TI));
- /* Set the descriptor base address */
- sdio_dw_writel(sd, DBADDRL, sd->dma_buf_phy & 0xffffffff);
- sdio_dw_writel(sd, DBADDRU, (rt_uint64_t)sd->dma_buf_phy >> 32);
- }
- else
- {
- /* Mask out interrupts - get Tx & Rx complete only */
- sdio_dw_writel(sd, IDSTS, SDIO_DW_IDMAC_INT_CLR);
- sdio_dw_writel(sd, IDINTEN, PINTC(NI) | PINTC(RI) | PINTC(TI));
- /* Set the descriptor base address */
- sdio_dw_writel(sd, DBADDR, sd->dma_buf_phy);
- }
- return err;
- }
- rt_inline rt_err_t sdio_dw_prepare_desc64(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_uint32_t desc_len;
- rt_uint64_t mem_addr;
- int timeout = rt_tick_from_millisecond(100);
- struct idmac_desc64 *desc_first, *desc_last, *desc;
- desc_first = desc_last = desc = sd->dma_buf;
- mem_addr = (rt_uint64_t)rt_kmem_v2p(sd->last_buf);
- for (rt_uint32_t length = sd->last_remain; length; ++desc)
- {
- rt_tick_t start = rt_tick_get();
- desc_len = rt_min_t(rt_uint32_t, length, DW_MCI_DESC_DATA_LENGTH);
- length -= desc_len;
- /*
- * Wait for the former clear OWN bit operation of IDMAC to make sure
- * that this descriptor isn't still owned by IDMAC as IDMAC's write ops
- * and CPU's read ops are asynchronous.
- */
- while ((HWREG32(&desc->des0) & IDMAC_DES0_OWN))
- {
- if ((rt_tick_get() - start) > timeout)
- {
- goto _err_own_bit;
- }
- rt_hw_cpu_relax();
- }
- /* Set the OWN bit and disable interrupts for this descriptor */
- desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
- /* Buffer length */
- IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len);
- /* Physical address to DMA to/from */
- desc->des4 = mem_addr & 0xffffffff;
- desc->des5 = mem_addr >> 32;
- /* Update physical address for the next desc */
- mem_addr += desc_len;
- /* Save pointer to the last descriptor */
- desc_last = desc;
- }
- /* Set first descriptor */
- desc_first->des0 |= IDMAC_DES0_FD;
- /* Set last descriptor */
- desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
- desc_last->des0 |= IDMAC_DES0_LD;
- return RT_EOK;
- _err_own_bit:
- /* restore the descriptor chain as it's polluted */
- LOG_D("Descriptor is still owned by IDMAC");
- rt_memset(sd->dma_buf, 0, DESC_RING_BUF_SZ);
- sdio_dw_idmac_init(sd);
- return -RT_EINVAL;
- }
- rt_inline rt_err_t sdio_dw_prepare_desc32(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_uint32_t desc_len, mem_addr;
- int timeout = rt_tick_from_millisecond(100);
- struct idmac_desc32 *desc_first, *desc_last, *desc;
- desc_first = desc_last = desc = sd->dma_buf;
- mem_addr = (rt_ubase_t)rt_kmem_v2p(sd->last_buf);
- for (rt_uint32_t length = sd->last_remain; length; ++desc)
- {
- rt_tick_t start = rt_tick_get();
- desc_len = rt_min_t(rt_uint32_t, length, DW_MCI_DESC_DATA_LENGTH);
- length -= desc_len;
- /*
- * Wait for the former clear OWN bit operation of IDMAC to make sure
- * that this descriptor isn't still owned by IDMAC as IDMAC's write ops
- * and CPU's read ops are asynchronous.
- */
- while (!IDMAC_OWN_CLR64(HWREG32(&desc->des0)))
- {
- if ((rt_tick_get() - start) > timeout)
- {
- goto _err_own_bit;
- }
- rt_hw_cpu_relax();
- }
- /* Set the OWN bit and disable interrupts for this descriptor */
- desc->des0 = rt_cpu_to_le32(IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH);
- /* Buffer length */
- IDMAC_32ADDR_SET_BUFFER1_SIZE(desc, desc_len);
- /* Physical address to DMA to/from */
- desc->des2 = rt_cpu_to_le32(mem_addr);
- /* Update physical address for the next desc */
- mem_addr += desc_len;
- /* Save pointer to the last descriptor */
- desc_last = desc;
- }
- /* Set first descriptor */
- desc_first->des0 |= rt_cpu_to_le32(IDMAC_DES0_FD);
- /* Set last descriptor */
- desc_last->des0 &= rt_cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC));
- desc_last->des0 |= rt_cpu_to_le32(IDMAC_DES0_LD);
- return RT_EOK;
- _err_own_bit:
- /* restore the descriptor chain as it's polluted */
- LOG_D("Descriptor is still owned by IDMAC");
- rt_memset(sd->dma_buf, 0, DESC_RING_BUF_SZ);
- sdio_dw_idmac_init(sd);
- return -RT_EINVAL;
- }
- static rt_err_t sdio_dw_idmac_start(struct sdio_dw *sd)
- {
- rt_err_t err = RT_EOK;
- if (sd->dma_64bit_address)
- {
- err = sdio_dw_prepare_desc64(sd, sd->data);
- }
- else
- {
- err = sdio_dw_prepare_desc32(sd, sd->data);
- }
- if (err)
- {
- goto _out;
- }
- /* Drain writebuffer */
- rt_hw_wmb();
- /* Make sure to reset DMA in case we did PIO before this */
- sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_DMA_RESET);
- sdio_dw_idmac_reset(sd);
- /* Select IDMAC interface */
- sdio_dw_writel(sd, CTRL, sdio_dw_readl(sd, CTRL) | SDIO_DW_CTRL_USE_IDMAC);
- /* Drain writebuffer */
- rt_hw_wmb();
- /* Enable the IDMAC */
- sdio_dw_writel(sd, BMOD, sdio_dw_readl(sd, BMOD) | SDIO_DW_IDMAC_ENABLE | SDIO_DW_IDMAC_FB);
- /* Start it running */
- sdio_dw_writel(sd, PLDMND, 1);
- _out:
- return err;
- }
- static rt_err_t sdio_dw_idmac_complete(struct sdio_dw *sd)
- {
- rt_err_t err = RT_EOK;
- struct rt_mmcsd_data *data = sd->data;
- sd->dma_ops->cleanup(sd);
- if (data)
- {
- rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- }
- return err;
- }
- static rt_err_t sdio_dw_idmac_stop(struct sdio_dw *sd)
- {
- rt_uint32_t reg;
- /* Disable and reset the IDMAC interface */
- reg = sdio_dw_readl(sd, CTRL);
- reg &= ~SDIO_DW_CTRL_USE_IDMAC;
- reg |= SDIO_DW_CTRL_DMA_RESET;
- sdio_dw_writel(sd, CTRL, reg);
- /* Stop the IDMAC running */
- reg = sdio_dw_readl(sd, BMOD);
- reg &= ~(SDIO_DW_IDMAC_ENABLE | SDIO_DW_IDMAC_FB);
- reg |= SDIO_DW_IDMAC_SWRESET;
- sdio_dw_writel(sd, BMOD, reg);
- return RT_EOK;
- }
- static rt_err_t sdio_dw_idmac_cleanup(struct sdio_dw *sd)
- {
- return RT_EOK;
- }
- static const struct sdio_dw_dma_ops sdio_dw_idmac_ops =
- {
- .init = sdio_dw_idmac_init,
- .start = sdio_dw_idmac_start,
- .complete = sdio_dw_idmac_complete,
- .stop = sdio_dw_idmac_stop,
- .cleanup = sdio_dw_idmac_cleanup,
- };
- static void edma_callback(struct rt_dma_chan *chan, rt_size_t size)
- {
- struct sdio_dw *sd = chan->priv;
- sd->dma_ops->complete(sd);
- }
- static rt_err_t sdio_dw_edmac_init(struct sdio_dw *sd)
- {
- rt_err_t err = RT_EOK;
- sd->edma_chan = rt_dma_chan_request(sd->bus_dev, "rx-tx");
- if (rt_is_err(sd->edma_chan))
- {
- err = rt_ptr_err(sd->edma_chan);
- LOG_E("Get external DMA channel error = %s", rt_strerror(err));
- sd->edma_chan = RT_NULL;
- }
- else if (!sd->edma_chan)
- {
- err = -RT_ERROR;
- }
- else
- {
- sd->edma_chan->callback = edma_callback;
- sd->edma_chan->priv = sd;
- }
- return err;
- }
- static rt_err_t sdio_dw_edmac_start(struct sdio_dw *sd)
- {
- rt_err_t err;
- struct rt_dma_slave_config config;
- struct rt_dma_slave_transfer transfer;
- rt_memset(&config, 0, sizeof(config));
- config.src_addr_width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_addr_width = RT_DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.src_addr = (rt_ubase_t)rt_kmem_v2p(sd->last_buf);
- config.dst_addr = (rt_ubase_t)rt_kmem_v2p(sd->fifo_base);
- config.dst_maxburst = 1 << (((sdio_dw_readl(sd, FIFOTH) >> 28) & 0x7) + 1);
- config.dst_maxburst = config.dst_maxburst == 2 ? 1 : config.dst_maxburst;
- config.src_maxburst = config.dst_maxburst;
- if (sd->data->flags & DATA_DIR_READ)
- {
- config.direction = RT_DMA_DEV_TO_MEM;
- }
- else
- {
- config.direction = RT_DMA_MEM_TO_DEV;
- }
- if ((err = rt_dma_chan_config(sd->edma_chan, &config)))
- {
- LOG_E("Config EDMAC error = %s", rt_strerror(err));
- return err;
- }
- rt_memset(&transfer, 0, sizeof(transfer));
- transfer.src_addr = config.src_addr;
- transfer.dst_addr = config.dst_addr;
- transfer.buffer_len = sd->last_remain;
- if ((err = rt_dma_prep_single(sd->edma_chan, &transfer)))
- {
- LOG_E("Prepare EDMAC error = %s", rt_strerror(err));
- return err;
- }
- return rt_dma_chan_start(sd->edma_chan);
- }
- static rt_err_t sdio_dw_edmac_complete(struct sdio_dw *sd)
- {
- rt_err_t err = RT_EOK;
- struct rt_mmcsd_data *data = sd->data;
- sd->dma_ops->cleanup(sd);
- if (data)
- {
- rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- }
- return err;
- }
- static rt_err_t sdio_dw_edmac_stop(struct sdio_dw *sd)
- {
- return rt_dma_chan_stop(sd->edma_chan);
- }
- static rt_err_t sdio_dw_edmac_cleanup(struct sdio_dw *sd)
- {
- return RT_EOK;
- }
- static rt_err_t sdio_dw_edmac_exit(struct sdio_dw *sd)
- {
- if (sd->edma_chan)
- {
- rt_dma_chan_release(sd->edma_chan);
- sd->edma_chan = RT_NULL;
- }
- return RT_EOK;
- }
- static const struct sdio_dw_dma_ops sdio_dw_edmac_ops =
- {
- .init = sdio_dw_edmac_init,
- .start = sdio_dw_edmac_start,
- .complete = sdio_dw_edmac_complete,
- .stop = sdio_dw_edmac_stop,
- .cleanup = sdio_dw_edmac_cleanup,
- .exit = sdio_dw_edmac_exit,
- };
- static rt_bool_t sdio_dw_get_cd(struct sdio_dw_slot *slot)
- {
- rt_bool_t present;
- struct sdio_dw *sd = slot->sd;
- if (!controller_is_removable(slot->host))
- {
- present = RT_TRUE;
- }
- else
- {
- present = (sdio_dw_readl(sd, CDETECT) & (1 << slot->id)) == 0;
- }
- return present;
- }
- static void sdio_dw_adjust_fifoth(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- static const rt_uint32_t mszs[] = { 1, 4, 8, 16, 32, 64, 128, 256 };
- rt_uint32_t blksz = data->blksize;
- rt_uint32_t fifo_width = 1 << sd->data_shift;
- rt_uint32_t blksz_depth = blksz / fifo_width, fifoth_val;
- rt_uint32_t msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
- int idx = RT_ARRAY_SIZE(mszs) - 1;
- /* PIO should ship this scenario */
- if (!sd->use_dma)
- {
- return;
- }
- tx_wmark = (sd->fifo_depth) / 2;
- tx_wmark_invers = sd->fifo_depth - tx_wmark;
- /* MSIZE is '1', if blksz is not a multiple of the FIFO width */
- if (blksz % fifo_width)
- {
- goto _done;
- }
- do {
- if (!((blksz_depth % mszs[idx]) || (tx_wmark_invers % mszs[idx])))
- {
- msize = idx;
- rx_wmark = mszs[idx] - 1;
- break;
- }
- } while (--idx > 0);
- /* If idx is '0', it won't be tried Thus, initial values are uesed */
- _done:
- fifoth_val = SDIO_DW_SET_FIFOTH(msize, rx_wmark, tx_wmark);
- sdio_dw_writel(sd, FIFOTH, fifoth_val);
- }
- static void sdio_dw_ctrl_thld(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_uint8_t enable;
- rt_uint16_t thld_size;
- rt_uint32_t blksz_depth, fifo_depth;
- rt_uint32_t blksz = data->blksize;
- /*
- * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
- * in the FIFO region, so we really shouldn't access it).
- */
- if (sd->verid < SDIO_DW_240A ||
- (sd->verid < SDIO_DW_280A && (data->flags & DATA_DIR_WRITE)))
- {
- return;
- }
- /*
- * Card write Threshold is introduced since 2.80a
- * It's used when HS400 mode is enabled.
- */
- if ((data->flags & DATA_DIR_WRITE) && sd->timing != MMCSD_TIMING_MMC_HS400)
- {
- goto _disable;
- }
- if ((data->flags & DATA_DIR_WRITE))
- {
- enable = SDIO_DW_CARD_WR_THR_EN;
- }
- else
- {
- enable = SDIO_DW_CARD_RD_THR_EN;
- }
- if (sd->timing != MMCSD_TIMING_MMC_HS200 &&
- sd->timing != MMCSD_TIMING_UHS_SDR104 &&
- sd->timing != MMCSD_TIMING_MMC_HS400)
- {
- goto _disable;
- }
- blksz_depth = blksz / (1 << sd->data_shift);
- fifo_depth = sd->fifo_depth;
- if (blksz_depth > fifo_depth)
- {
- goto _disable;
- }
- /*
- * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz'
- * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz
- * Currently just choose blksz.
- */
- thld_size = blksz;
- sdio_dw_writel(sd, CDTHRCTL, SDIO_DW_SET_THLD(thld_size, enable));
- return;
- _disable:
- sdio_dw_writel(sd, CDTHRCTL, 0);
- }
- static rt_err_t sdio_dw_submit_data_dma(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_uint32_t temp;
- rt_ubase_t level;
- sd->using_dma = RT_FALSE;
- /* If we don't have a channel, we can't do DMA */
- if (!sd->use_dma)
- {
- return -RT_ENOSYS;
- }
- sd->using_dma = RT_TRUE;
- /*
- * Decide the MSIZE and RX/TX Watermark. If current block size is same with
- * previous size, no need to update fifoth.
- */
- if (sd->prev_blksz != data->blksize)
- {
- sdio_dw_adjust_fifoth(sd, data);
- }
- /* Enable the DMA interface */
- temp = sdio_dw_readl(sd, CTRL);
- temp |= SDIO_DW_CTRL_DMA_ENABLE;
- sdio_dw_writel(sd, CTRL, temp);
- /* Disable RX/TX IRQs, let DMA handle it */
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- temp = sdio_dw_readl(sd, INTMASK);
- temp &= ~(PINT(RXDR) | PINT(TXDR));
- sdio_dw_writel(sd, INTMASK, temp);
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- /* Flush data to memory */
- rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, sd->last_buf, sd->last_remain);
- if (sd->dma_ops->start(sd))
- {
- /* We can't do DMA, try PIO for this one */
- sd->dma_ops->stop(sd);
- return -RT_ENOSYS;
- }
- return RT_EOK;
- }
- static void sdio_dw_submit_data(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_ubase_t level;
- rt_uint32_t temp;
- data->err = -RT_ERROR;
- sd->data = data;
- sd->last_buf = data->buf;
- sd->last_remain = data->blks * data->blksize;
- if ((data->flags & DATA_DIR_READ))
- {
- sd->dir_status = SDIO_DW_RECV_STATUS;
- }
- else
- {
- sd->dir_status = SDIO_DW_SEND_STATUS;
- }
- sdio_dw_ctrl_thld(sd, data);
- if (sdio_dw_submit_data_dma(sd, data))
- {
- sd->part_buf_start = 0;
- sd->part_buf_count = 0;
- sdio_dw_writel(sd, RINTSTS, PINT(TXDR) | PINT(RXDR));
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- temp = sdio_dw_readl(sd, INTMASK);
- temp |= PINT(TXDR) | PINT(RXDR);
- sdio_dw_writel(sd, INTMASK, temp);
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- temp = sdio_dw_readl(sd, CTRL);
- temp &= ~SDIO_DW_CTRL_DMA_ENABLE;
- sdio_dw_writel(sd, CTRL, temp);
- /*
- * Use the initial fifoth_val for PIO mode. If wm_algined is set, we set
- * watermark same as data size. If next issued data may be transfered by
- * DMA mode, prev_blksz should be invalidated.
- */
- if (sd->wm_aligned)
- {
- sdio_dw_adjust_fifoth(sd, data);
- }
- else
- {
- sdio_dw_writel(sd, FIFOTH, sd->fifoth_val);
- }
- sd->prev_blksz = 0;
- }
- else
- {
- /*
- * Keep the current block size.
- * It will be used to decide whether to update fifoth register next time.
- */
- sd->prev_blksz = data->blksize;
- }
- }
- static void sdio_dw_setup_bus(struct sdio_dw_slot *slot, rt_bool_t force_clkinit)
- {
- struct sdio_dw *sd = slot->sd;
- rt_uint32_t clock = slot->clock;
- rt_uint32_t cmd_bits = SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT;
- /* We must continue to set bit 28 in CMD until the change is complete */
- if (sd->state == STATE_WAITING_CMD11_DONE)
- {
- cmd_bits |= SDIO_DW_CMD_VOLT_SWITCH;
- }
- if (!clock)
- {
- sdio_dw_writel(sd, CLKENA, 0);
- sdio_dw_send_cmd(slot, cmd_bits, 0);
- }
- else if (clock != sd->current_speed || force_clkinit)
- {
- rt_uint32_t clk_en_a, div = sd->bus_hz / clock;
- if (sd->bus_hz % clock && sd->bus_hz > clock)
- {
- /* Move the + 1 after the divide to prevent over-clocking the card */
- div += 1;
- }
- div = (sd->bus_hz != clock) ? RT_DIV_ROUND_UP(div, 2) : 0;
- if (clock != slot->clk_old &&
- !rt_bitmap_test_bit(&slot->flags, DW_MMC_CARD_NEEDS_POLL) &&
- !force_clkinit)
- {
- LOG_D("Bus speed (slot %d) = %uHz (slot req %uHz, actual %uHZ div = %d)",
- slot->id, sd->bus_hz, clock,
- div ? ((sd->bus_hz / div) >> 1) : sd->bus_hz, div);
- }
- /* Disable clock */
- sdio_dw_writel(sd, CLKENA, 0);
- sdio_dw_writel(sd, CLKSRC, 0);
- /* Inform CIU */
- sdio_dw_send_cmd(slot, cmd_bits, 0);
- /* Set clock to desired speed */
- sdio_dw_writel(sd, CLKDIV, div);
- /* Inform CIU */
- sdio_dw_send_cmd(slot, cmd_bits, 0);
- /* Enable clock; only low power if no SDIO */
- clk_en_a = SDIO_DW_CLKEN_ENABLE << slot->id;
- if (!rt_bitmap_test_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR))
- {
- clk_en_a |= SDIO_DW_CLKEN_LOW_PWR << slot->id;
- }
- sdio_dw_writel(sd, CLKENA, clk_en_a);
- /* Inform CIU */
- sdio_dw_send_cmd(slot, cmd_bits, 0);
- /* Keep the last clock value that was requested from core */
- slot->clk_old = clock;
- }
- sd->current_speed = clock;
- /* Set the current slot bus width */
- sdio_dw_writel(sd, CTYPE, (slot->ctype << slot->id));
- }
- static void sdio_dw_set_data_timeout(struct sdio_dw *sd, rt_uint32_t timeout_ns)
- {
- rt_uint64_t tmp;
- rt_uint32_t clk_div, tmout;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- if (drv_data && drv_data->set_data_timeout)
- {
- drv_data->set_data_timeout(sd, timeout_ns);
- return;
- }
- clk_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2;
- if (clk_div == 0)
- {
- clk_div = 1;
- }
- tmp = RT_DIV_ROUND_UP_ULL((rt_uint64_t)timeout_ns * sd->bus_hz, NSEC_PER_SEC);
- tmp = RT_DIV_ROUND_UP_ULL(tmp, clk_div);
- /* TMOUT[7:0] (RESPONSE_TIMEOUT): Set maximum */
- tmout = 0xff;
- /* TMOUT[31:8] (DATA_TIMEOUT) */
- if (!tmp || tmp > 0xffffff)
- {
- tmout |= (0xffffff << 8);
- }
- else
- {
- tmout |= (tmp & 0xffffff) << 8;
- }
- sdio_dw_writel(sd, TMOUT, tmout);
- }
- /* Push final bytes to part_buf, only use during push */
- static void sdio_dw_set_part_bytes(struct sdio_dw *sd, void *buf, int cnt)
- {
- rt_memcpy((void *)&sd->part_buf, buf, cnt);
- sd->part_buf_count = cnt;
- }
- /* Append bytes to part_buf, only use during push */
- static int sdio_dw_push_part_bytes(struct sdio_dw *sd, void *buf, int cnt)
- {
- cnt = rt_min(cnt, (1 << sd->data_shift) - sd->part_buf_count);
- rt_memcpy((void *)&sd->part_buf + sd->part_buf_count, buf, cnt);
- sd->part_buf_count += cnt;
- return cnt;
- }
- /* Pull first bytes from part_buf, only use during pull */
- static int sdio_dw_pull_part_bytes(struct sdio_dw *sd, void *buf, int cnt)
- {
- cnt = rt_min_t(int, cnt, sd->part_buf_count);
- if (cnt)
- {
- rt_memcpy(buf, (void *)&sd->part_buf + sd->part_buf_start, cnt);
- sd->part_buf_count -= cnt;
- sd->part_buf_start += cnt;
- }
- return cnt;
- }
- /* Pull final bytes from the part_buf, assuming it's just been filled */
- static void sdio_dw_pull_final_bytes(struct sdio_dw *sd, void *buf, int cnt)
- {
- rt_memcpy(buf, &sd->part_buf, cnt);
- sd->part_buf_start = cnt;
- sd->part_buf_count = (1 << sd->data_shift) - cnt;
- }
- static void sdio_dw_push_data16(struct sdio_dw *sd, void *buf, int cnt)
- {
- struct rt_mmcsd_data *data = sd->data;
- int init_cnt = cnt;
- /* Try and push anything in the part_buf */
- if ((sd->part_buf_count))
- {
- int len = sdio_dw_push_part_bytes(sd, buf, cnt);
- buf += len;
- cnt -= len;
- if (sd->part_buf_count == 2)
- {
- sdio_dw_fifo_writew(sd, sd->part_buf16);
- sd->part_buf_count = 0;
- }
- }
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x1))
- {
- while (cnt >= 2)
- {
- rt_uint16_t aligned_buf[64];
- int len = rt_min(cnt & -2, (int)sizeof(aligned_buf));
- int items = len >> 1;
- /* rt_memcpy from input buffer into aligned buffer */
- rt_memcpy(aligned_buf, buf, len);
- buf += len;
- cnt -= len;
- /* Push data from aligned buffer into fifo */
- for (int i = 0; i < items; ++i)
- {
- sdio_dw_fifo_writew(sd, aligned_buf[i]);
- }
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint16_t *pdata = buf;
- for (; cnt >= 2; cnt -= 2)
- {
- sdio_dw_fifo_writew(sd, *pdata++);
- }
- buf = pdata;
- }
- /* Put anything remaining in the part_buf */
- if (cnt)
- {
- sdio_dw_set_part_bytes(sd, buf, cnt);
- /* Push data if we have reached the expected data length */
- if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks))
- {
- sdio_dw_fifo_writew(sd, sd->part_buf16);
- }
- }
- }
- static void sdio_dw_pull_data16(struct sdio_dw *sd, void *buf, int cnt)
- {
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x1))
- {
- while (cnt >= 2)
- {
- /* Pull data from fifo into aligned buffer */
- rt_uint16_t aligned_buf[64];
- int len = rt_min(cnt & -2, (int)sizeof(aligned_buf));
- int items = len >> 1;
- for (int i = 0; i < items; ++i)
- {
- aligned_buf[i] = sdio_dw_fifo_readw(sd);
- }
- /* rt_memcpy from aligned buffer into output buffer */
- rt_memcpy(buf, aligned_buf, len);
- buf += len;
- cnt -= len;
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint16_t *pdata = buf;
- for (; cnt >= 2; cnt -= 2)
- {
- *pdata++ = sdio_dw_fifo_readw(sd);
- }
- buf = pdata;
- }
- if (cnt)
- {
- sd->part_buf16 = sdio_dw_fifo_readw(sd);
- sdio_dw_pull_final_bytes(sd, buf, cnt);
- }
- }
- static void sdio_dw_push_data32(struct sdio_dw *sd, void *buf, int cnt)
- {
- struct rt_mmcsd_data *data = sd->data;
- int init_cnt = cnt;
- /* Try and push anything in the part_buf */
- if ((sd->part_buf_count))
- {
- int len = sdio_dw_push_part_bytes(sd, buf, cnt);
- buf += len;
- cnt -= len;
- if (sd->part_buf_count == 4)
- {
- sdio_dw_fifo_writel(sd, sd->part_buf32);
- sd->part_buf_count = 0;
- }
- }
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x3))
- {
- while (cnt >= 4)
- {
- rt_uint32_t aligned_buf[32];
- int len = rt_min(cnt & -4, (int)sizeof(aligned_buf));
- int items = len >> 2;
- /* rt_memcpy from input buffer into aligned buffer */
- rt_memcpy(aligned_buf, buf, len);
- buf += len;
- cnt -= len;
- /* Push data from aligned buffer into fifo */
- for (int i = 0; i < items; ++i)
- {
- sdio_dw_fifo_writel(sd, aligned_buf[i]);
- }
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint32_t *pdata = buf;
- for (; cnt >= 4; cnt -= 4)
- {
- sdio_dw_fifo_writel(sd, *pdata++);
- }
- buf = pdata;
- }
- /* Put anything remaining in the part_buf */
- if (cnt)
- {
- sdio_dw_set_part_bytes(sd, buf, cnt);
- /* Push data if we have reached the expected data length */
- if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks))
- {
- sdio_dw_fifo_writel(sd, sd->part_buf32);
- }
- }
- }
- static void sdio_dw_pull_data32(struct sdio_dw *sd, void *buf, int cnt)
- {
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x3))
- {
- while (cnt >= 4)
- {
- /* Pull data from fifo into aligned buffer */
- rt_uint32_t aligned_buf[32];
- int len = rt_min(cnt & -4, (int)sizeof(aligned_buf));
- int items = len >> 2;
- for (int i = 0; i < items; ++i)
- {
- aligned_buf[i] = sdio_dw_fifo_readl(sd);
- }
- /* rt_memcpy from aligned buffer into output buffer */
- rt_memcpy(buf, aligned_buf, len);
- buf += len;
- cnt -= len;
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint32_t *pdata = buf;
- for (; cnt >= 4; cnt -= 4)
- {
- *pdata++ = sdio_dw_fifo_readl(sd);
- }
- buf = pdata;
- }
- if (cnt)
- {
- sd->part_buf32 = sdio_dw_fifo_readl(sd);
- sdio_dw_pull_final_bytes(sd, buf, cnt);
- }
- }
- static void sdio_dw_push_data64(struct sdio_dw *sd, void *buf, int cnt)
- {
- struct rt_mmcsd_data *data = sd->data;
- int init_cnt = cnt;
- /* Try and push anything in the part_buf */
- if ((sd->part_buf_count))
- {
- int len = sdio_dw_push_part_bytes(sd, buf, cnt);
- buf += len;
- cnt -= len;
- if (sd->part_buf_count == 8)
- {
- sdio_dw_fifo_writeq(sd, sd->part_buf64);
- sd->part_buf_count = 0;
- }
- }
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x7))
- {
- while (cnt >= 8)
- {
- rt_uint64_t aligned_buf[16];
- int len = rt_min(cnt & -8, (int)sizeof(aligned_buf));
- int items = len >> 3;
- /* rt_memcpy from input buffer into aligned buffer */
- rt_memcpy(aligned_buf, buf, len);
- buf += len;
- cnt -= len;
- /* Push data from aligned buffer into fifo */
- for (int i = 0; i < items; ++i)
- {
- sdio_dw_fifo_writeq(sd, aligned_buf[i]);
- }
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint64_t *pdata = buf;
- for (; cnt >= 8; cnt -= 8)
- {
- sdio_dw_fifo_writeq(sd, *pdata++);
- }
- buf = pdata;
- }
- /* Put anything remaining in the part_buf */
- if (cnt)
- {
- sdio_dw_set_part_bytes(sd, buf, cnt);
- /* Push data if we have reached the expected data length */
- if ((data->bytes_xfered + init_cnt) == (data->blksize * data->blks))
- {
- sdio_dw_fifo_writeq(sd, sd->part_buf64);
- }
- }
- }
- static void sdio_dw_pull_data64(struct sdio_dw *sd, void *buf, int cnt)
- {
- #ifndef ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS
- if (((rt_ubase_t)buf & 0x7))
- {
- while (cnt >= 8)
- {
- /* Pull data from fifo into aligned buffer */
- rt_uint64_t aligned_buf[16];
- int len = rt_min(cnt & -8, (int)sizeof(aligned_buf));
- int items = len >> 3;
- for (int i = 0; i < items; ++i)
- {
- aligned_buf[i] = sdio_dw_fifo_readq(sd);
- }
- /* rt_memcpy from aligned buffer into output buffer */
- rt_memcpy(buf, aligned_buf, len);
- buf += len;
- cnt -= len;
- }
- }
- else
- #endif /* !ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS */
- {
- rt_uint64_t *pdata = buf;
- for (; cnt >= 8; cnt -= 8)
- {
- *pdata++ = sdio_dw_fifo_readq(sd);
- }
- buf = pdata;
- }
- if (cnt)
- {
- sd->part_buf64 = sdio_dw_fifo_readq(sd);
- sdio_dw_pull_final_bytes(sd, buf, cnt);
- }
- }
- static void sdio_dw_pull_data(struct sdio_dw *sd, void *buf, int cnt)
- {
- /* Get remaining partial bytes */
- int len = sdio_dw_pull_part_bytes(sd, buf, cnt);
- if (len != cnt)
- {
- buf += len;
- cnt -= len;
- /* Get the rest of the data */
- sd->pull_data(sd, buf, cnt);
- }
- }
- static void sdio_dw_read_data_pio(struct sdio_dw *sd, rt_bool_t dto)
- {
- void *buf;
- int shift = sd->data_shift;
- struct rt_mmcsd_data *data = sd->data;
- rt_uint32_t status, remain, fcnt, len;
- buf = sd->last_buf;
- remain = sd->last_remain;
- do {
- if (!remain)
- {
- break;
- }
- do {
- fcnt = (SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS)) << shift) + sd->part_buf_count;
- len = rt_min(remain, fcnt);
- if (!len)
- {
- break;
- }
- sdio_dw_pull_data(sd, buf, len);
- data->bytes_xfered += len;
- buf += len;
- remain -= len;
- } while (remain);
- status = sdio_dw_readl(sd, MINTSTS);
- sdio_dw_writel(sd, RINTSTS, PINT(RXDR));
- /* If the RXDR is ready read again */
- } while ((status & PINT(RXDR)) || (dto && SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS))));
- sd->last_buf = remain ? buf : RT_NULL;
- sd->last_remain = remain;
- rt_hw_wmb();
- rt_bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- }
- static void sdio_dw_write_data_pio(struct sdio_dw *sd)
- {
- void *buf;
- int shift = sd->data_shift;
- struct rt_mmcsd_data *data = sd->data;
- rt_uint32_t status, remain, fcnt, len, fifo_depth;
- buf = sd->last_buf;
- remain = sd->last_remain;
- fifo_depth = sd->fifo_depth;
- do {
- if (!remain)
- {
- break;
- }
- do {
- fcnt = ((fifo_depth - SDIO_DW_GET_FCNT(sdio_dw_readl(sd, STATUS))) << shift) - sd->part_buf_count;
- len = rt_min(remain, fcnt);
- if (!len)
- {
- break;
- }
- sd->push_data(sd, buf, len);
- data->bytes_xfered += len;
- buf += len;
- remain -= len;
- } while (remain);
- status = sdio_dw_readl(sd, MINTSTS);
- sdio_dw_writel(sd, RINTSTS, PINT(TXDR));
- /* If TXDR write again */
- } while ((status & PINT(TXDR)));
- sd->last_buf = remain ? buf : RT_NULL;
- sd->last_remain = remain;
- rt_hw_wmb();
- rt_bitmap_set_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- }
- static void sdio_dw_init_dma(struct sdio_dw *sd)
- {
- int addr_config;
- /*
- * Check tansfer mode from HCON[17:16]
- * Clear the ambiguous description of dw_mmc databook:
- * 2b'00: No DMA Interface -> Actually means using Internal DMA block
- * 2b'01: DesignWare DMA Interface -> Synopsys DW-DMA block
- * 2b'10: Generic DMA Interface -> non-Synopsys generic DMA block
- * 2b'11: Non DW DMA Interface -> pio only
- * Compared to DesignWare DMA Interface, Generic DMA Interface has a simpler
- * request/acknowledge handshake mechanism and both of them are regarded as
- * external dma master for dw_mmc.
- */
- sd->use_dma = SDIO_DW_GET_TRANS_MODE(sdio_dw_readl(sd, HCON));
- if (sd->use_dma == DMA_INTERFACE_IDMA)
- {
- sd->use_dma = TRANS_MODE_IDMAC;
- }
- else if (sd->use_dma == DMA_INTERFACE_DWDMA || sd->use_dma == DMA_INTERFACE_GDMA)
- {
- sd->use_dma = TRANS_MODE_EDMAC;
- }
- else
- {
- goto _no_dma;
- }
- /* Determine which DMA interface to use */
- if (sd->use_dma == TRANS_MODE_IDMAC)
- {
- /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */
- addr_config = SDIO_DW_GET_ADDR_CONFIG(sdio_dw_readl(sd, HCON));
- /* Supports IDMAC in 64/32-bit address mode */
- sd->dma_64bit_address = (addr_config == 1);
- LOG_D("IDMAC supports %s-bit address mode", sd->dma_64bit_address ? "64" : "32");
- /* Alloc memory for translation */
- sd->dma_buf = rt_dma_alloc_coherent(sd->bus_dev, DESC_RING_BUF_SZ, &sd->dma_buf_phy);
- if (!sd->dma_buf)
- {
- LOG_E("Could not alloc DMA memory witch cache");
- goto _no_dma;
- }
- sd->dma_ops = &sdio_dw_idmac_ops;
- LOG_D("Using internal DMA controller");
- }
- else
- {
- if (!rt_dm_dev_prop_read_bool(&sd->parent, "dma-names") ||
- !rt_dm_dev_prop_read_bool(&sd->parent, "dmas"))
- {
- goto _no_dma;
- }
- sd->dma_ops = &sdio_dw_edmac_ops;
- LOG_D("Using external DMA controller");
- }
- if (sd->dma_ops->init && sd->dma_ops->start && sd->dma_ops->stop && sd->dma_ops->cleanup)
- {
- if (sd->dma_ops->init(sd))
- {
- LOG_E("Unable to initialize DMA Controller");
- goto _no_dma;
- }
- }
- else
- {
- LOG_E("DMA initialization not found");
- goto _no_dma;
- }
- return;
- _no_dma:
- LOG_D("Using PIO mode");
- sd->use_dma = TRANS_MODE_PIO;
- }
- static rt_bool_t sdio_dw_reset(struct sdio_dw *sd)
- {
- rt_err_t res = RT_FALSE;
- rt_uint32_t flags = SDIO_DW_CTRL_RESET | SDIO_DW_CTRL_FIFO_RESET;
- if (sd->use_dma)
- {
- flags |= SDIO_DW_CTRL_DMA_RESET;
- }
- if (sdio_dw_ctrl_reset(sd, flags))
- {
- int timeout = 500 * USEC_PER_MSEC;
- /* In all cases we clear the RAWINTS register to clear any interrupts */
- sdio_dw_writel(sd, RINTSTS, 0xffffffff);
- if (!sd->use_dma)
- {
- res = RT_TRUE;
- goto _ciu_out;
- }
- /* Wait for dma_req to be cleared */
- while ((sdio_dw_readl(sd, STATUS) & SDIO_DW_STATUS_DMA_REQ) && timeout--)
- {
- rt_hw_cpu_relax();
- }
- if (time <= 0)
- {
- LOG_E("Timeout waiting for dma_req to be cleared in reset");
- goto _ciu_out;
- }
- /* when using DMA next we reset the fifo again */
- if (!sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_FIFO_RESET))
- {
- goto _ciu_out;
- }
- }
- else
- {
- /* If the controller reset bit did clear, then set clock regs */
- if (!(sdio_dw_readl(sd, CTRL) & SDIO_DW_CTRL_RESET))
- {
- LOG_E("FIFO/DMA reset bits didn't clear but ciu was reset, doing clock update");
- goto _ciu_out;
- }
- }
- if (sd->use_dma == TRANS_MODE_IDMAC)
- {
- /* It is also required that we reinit idmac */
- sdio_dw_idmac_init(sd);
- }
- res = RT_TRUE;
- _ciu_out:
- /* After a CTRL reset we need to have CIU set clock registers */
- sdio_dw_send_cmd(sd->slot, SDIO_DW_CMD_UPD_CLK, 0);
- return res;
- }
- static void sdio_dw_start_request(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd)
- {
- rt_uint32_t cmd_flags;
- struct sdio_dw_slot *slot = sd->slot;
- struct rt_mmcsd_data *data = cmd->data;
- if (sd->state == STATE_WAITING_CMD11_DONE)
- {
- sd->state = STATE_IDLE;
- }
- if (sd->state == STATE_IDLE)
- {
- sd->state = STATE_SENDING_CMD;
- }
- sd->req = sd->slot->req;
- sd->cmd = cmd;
- sd->pending_events = 0;
- sd->cmd_status = 0;
- sd->data_status = 0;
- sd->dir_status = 0;
- if (data)
- {
- sdio_dw_set_data_timeout(sd, data->timeout_ns);
- sdio_dw_writel(sd, BYTCNT, data->blksize * data->blks);
- sdio_dw_writel(sd, BLKSIZ, data->blksize);
- }
- cmd_flags = cmd->cmd_code;
- if (cmd->cmd_code == STOP_TRANSMISSION ||
- cmd->cmd_code == GO_IDLE_STATE ||
- cmd->cmd_code == GO_INACTIVE_STATE ||
- (cmd->cmd_code == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1ffff) == SDIO_REG_CCCR_IO_ABORT))
- {
- cmd_flags |= SDIO_DW_CMD_STOP;
- }
- else if (cmd->cmd_code != SEND_STATUS && data)
- {
- cmd_flags |= SDIO_DW_CMD_PRV_DAT_WAIT;
- }
- if (cmd->cmd_code == VOLTAGE_SWITCH)
- {
- rt_uint32_t clk_en_a;
- /* Special bit makes CMD11 not die */
- cmd_flags |= SDIO_DW_CMD_VOLT_SWITCH;
- /* Change state to continue to handle CMD11 weirdness */
- sd->state = STATE_SENDING_CMD11;
- /*
- * We need to disable low power mode (automatic clock stop) while
- * doing voltage switch so we don't confuse the card, since stopping
- * the clock is a specific part of the UHS voltage change dance.
- *
- * Note that low power mode (SDIO_DW_CLKEN_LOW_PWR) will be
- * unconditionally turned back on in dw_mci_setup_bus() if it's ever
- * called with a non-zero clock. That shouldn't happen until the
- * voltage change is all done.
- */
- clk_en_a = sdio_dw_readl(sd, CLKENA);
- clk_en_a &= ~(SDIO_DW_CLKEN_LOW_PWR << slot->id);
- sdio_dw_writel(sd, CLKENA, clk_en_a);
- sdio_dw_send_cmd(sd->slot, SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT, 0);
- }
- switch (resp_type(cmd))
- {
- case RESP_NONE:
- break;
- case RESP_R1:
- case RESP_R5:
- case RESP_R6:
- case RESP_R7:
- case RESP_R1B:
- cmd_flags |= SDIO_DW_CMD_RESP_EXP;
- cmd_flags |= SDIO_DW_CMD_RESP_CRC;
- break;
- case RESP_R2:
- cmd_flags |= SDIO_DW_CMD_RESP_EXP;
- cmd_flags |= SDIO_DW_CMD_RESP_CRC;
- cmd_flags |= SDIO_DW_CMD_RESP_LONG;
- break;
- case RESP_R3:
- case RESP_R4:
- cmd_flags |= SDIO_DW_CMD_RESP_EXP;
- break;
- default:
- LOG_D("Unsupported cmd type = %x", resp_type(cmd));
- break;
- }
- if (data)
- {
- cmd_flags |= SDIO_DW_CMD_DAT_EXP;
- if ((data->flags & DATA_DIR_WRITE))
- {
- cmd_flags |= SDIO_DW_CMD_DAT_WR;
- }
- }
- if (!rt_bitmap_test_bit(&slot->flags, DW_MMC_CARD_NO_USE_HOLD))
- {
- cmd_flags |= SDIO_DW_CMD_USE_HOLD_REG;
- }
- if (rt_bitmap_test_bit(&slot->flags, DW_MMC_CARD_NEED_INIT))
- {
- cmd_flags |= SDIO_DW_CMD_INIT;
- rt_bitmap_clear_bit(&slot->flags, DW_MMC_CARD_NEED_INIT);
- }
- if (data)
- {
- sdio_dw_submit_data(sd, data);
- /* Drain writebuffer */
- rt_hw_wmb();
- }
- sdio_dw_start_cmd(slot, cmd_flags, cmd->arg);
- if (cmd->cmd_code == VOLTAGE_SWITCH)
- {
- rt_ubase_t level = rt_spin_lock_irqsave(&sd->irq_lock);
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE))
- {
- rt_tick_t tick = rt_tick_from_millisecond(500) + 1;
- rt_timer_control(&sd->cmd11_timer, RT_TIMER_CTRL_SET_TIME, &tick);
- rt_timer_start(&sd->cmd11_timer);
- }
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- sd->stop_cmdr = sdio_dw_prep_stop_abort(sd, cmd);
- }
- static void sdio_dw_end_request(struct sdio_dw *sd)
- {
- sd->slot->req = RT_NULL;
- sd->req = RT_NULL;
- if (sd->state == STATE_SENDING_CMD11)
- {
- sd->state = STATE_WAITING_CMD11_DONE;
- }
- else
- {
- sd->state = STATE_IDLE;
- }
- rt_hw_spin_unlock(&sd->lock.lock);
- mmcsd_req_complete(sd->slot->host);
- rt_hw_spin_lock(&sd->lock.lock);
- }
- static rt_err_t sdio_dw_cmd_complete(struct sdio_dw *sd, struct rt_mmcsd_cmd *cmd)
- {
- rt_uint32_t status = sd->cmd_status;
- sd->cmd_status = 0;
- /* Read the response from the card (up to 16 bytes) */
- if (resp_type(cmd) == RESP_R2)
- {
- cmd->resp[0] = sdio_dw_readl(sd, RESP3);
- cmd->resp[1] = sdio_dw_readl(sd, RESP2);
- cmd->resp[2] = sdio_dw_readl(sd, RESP1);
- cmd->resp[3] = sdio_dw_readl(sd, RESP0);
- }
- else
- {
- cmd->resp[0] = sdio_dw_readl(sd, RESP0);
- }
- if ((status & PINT(RTO)))
- {
- cmd->err = -RT_ETIMEOUT;
- }
- else if ((resp_type(cmd) & (RESP_R1 | RESP_R5 | RESP_R6 | RESP_R7 | RESP_R1B)) &&
- (status & PINT(RCRC)))
- {
- cmd->err = -RT_EIO;
- }
- else if ((status & PINT(RESP_ERR)))
- {
- cmd->err = -RT_EIO;
- }
- else
- {
- cmd->err = RT_EOK;
- }
- return cmd->err;
- }
- static int sdio_dw_data_complete(struct sdio_dw *sd, struct rt_mmcsd_data *data)
- {
- rt_uint32_t status = sd->data_status;
- if (status & SDIO_DW_DATA_ERROR_FLAGS)
- {
- if (status & PINT(DRTO))
- {
- data->err = -RT_ETIMEOUT;
- }
- else if (status & PINT(DCRC))
- {
- data->err = -RT_EIO;
- }
- else if (status & PINT(EBE))
- {
- if (sd->dir_status == SDIO_DW_SEND_STATUS)
- {
- /*
- * No data CRC status was returned. The number of bytes
- * transferred will be exaggerated in PIO mode.
- */
- data->bytes_xfered = 0;
- data->err = -RT_ETIMEOUT;
- }
- else if (sd->dir_status == SDIO_DW_RECV_STATUS)
- {
- data->err = -RT_EIO;
- }
- }
- else
- {
- /* PINT(SBE) is included */
- data->err = -RT_EIO;
- }
- LOG_D("Data error, status 0x%x", status);
- /* After an error, there may be data lingering in the FIFO */
- sdio_dw_reset(sd);
- }
- else
- {
- data->bytes_xfered = data->blks * data->blksize;
- data->err = RT_EOK;
- }
- return data->err;
- }
- static void sdio_dw_mmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
- {
- struct sdio_dw_slot *slot = host->private_data;
- struct sdio_dw *sd = slot->sd;
- /*
- * The check for card presence and queueing of the request must be atomic,
- * otherwise the card could be removed in between and the request wouldn't
- * fail until another card was inserted.
- */
- if (!sdio_dw_get_cd(slot))
- {
- req->cmd->err = -RT_EIO;
- mmcsd_req_complete(host);
- return;
- }
- rt_hw_spin_lock(&sd->lock.lock);
- sd->slot->req = req;
- sdio_dw_start_request(sd, req->sbc ? : req->cmd);
- rt_hw_spin_unlock(&sd->lock.lock);
- }
- static void sdio_dw_mmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *ios)
- {
- rt_err_t err;
- rt_uint32_t regs;
- struct sdio_dw_slot *slot = host->private_data;
- struct sdio_dw *sd = slot->sd;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- /* Bus */
- switch (ios->bus_width)
- {
- case MMCSD_BUS_WIDTH_4:
- slot->ctype = SDIO_DW_CTYPE_4BIT;
- break;
- case MMCSD_BUS_WIDTH_8:
- slot->ctype = SDIO_DW_CTYPE_8BIT;
- break;
- default:
- slot->ctype = SDIO_DW_CTYPE_1BIT;
- break;
- }
- regs = sdio_dw_readl(sd, UHS_REG);
- /* DDR mode set */
- if (ios->timing == MMCSD_TIMING_MMC_DDR52 ||
- ios->timing == MMCSD_TIMING_UHS_DDR50 ||
- ios->timing == MMCSD_TIMING_MMC_HS400)
- {
- regs |= ((0x1 << slot->id) << 16);
- }
- else
- {
- regs &= ~((0x1 << slot->id) << 16);
- }
- sdio_dw_writel(sd, UHS_REG, regs);
- sd->timing = ios->timing;
- /*
- * Use mirror of ios->clock to prevent race with mmc core ios update when
- * finding the minimum.
- */
- slot->clock = ios->clock;
- if (drv_data && drv_data->set_iocfg)
- {
- drv_data->set_iocfg(sd, ios);
- }
- /* Power */
- switch (ios->power_mode)
- {
- case MMCSD_POWER_UP:
- if (host->supply.vmmc)
- {
- err = sdio_regulator_set_ocr(host, host->supply.vmmc, ios->vdd);
- if (err)
- {
- LOG_E("Failed to enable vmmc regulator error = %s", rt_strerror(err));
- return;
- }
- }
- rt_bitmap_set_bit(&slot->flags, DW_MMC_CARD_NEED_INIT);
- regs = sdio_dw_readl(sd, PWREN);
- regs |= (1 << slot->id);
- sdio_dw_writel(sd, PWREN, regs);
- break;
- case MMCSD_POWER_ON:
- if (!sd->vqmmc_enabled)
- {
- if (host->supply.vqmmc)
- {
- err = rt_regulator_enable(host->supply.vqmmc);
- if (err)
- {
- LOG_E("Failed to enable vqmmc error = %s", rt_strerror(err));
- }
- else
- {
- sd->vqmmc_enabled = RT_TRUE;
- }
- }
- else
- {
- sd->vqmmc_enabled = RT_TRUE;
- }
- sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_ALL_RESET_FLAGS);
- }
- /* Adjust clock / bus width after power is up */
- sdio_dw_setup_bus(slot, RT_FALSE);
- break;
- case MMCSD_POWER_OFF:
- /* Turn clock off before power goes down */
- sdio_dw_setup_bus(slot, RT_FALSE);
- if (host->supply.vmmc)
- {
- sdio_regulator_set_ocr(host, host->supply.vmmc, 0);
- }
- if (host->supply.vqmmc && sd->vqmmc_enabled)
- {
- rt_regulator_disable(host->supply.vqmmc);
- }
- sd->vqmmc_enabled = RT_FALSE;
- regs = sdio_dw_readl(sd, PWREN);
- regs &= ~(1 << slot->id);
- sdio_dw_writel(sd, PWREN, regs);
- break;
- default:
- LOG_E("Invalid power_mode value %x", ios->power_mode);
- break;
- }
- if (sd->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
- {
- sd->state = STATE_IDLE;
- }
- }
- static rt_int32_t sdio_dw_mmc_get_card_status(struct rt_mmcsd_host *host)
- {
- return 0;
- }
- static void sdio_dw_mmc_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable)
- {
- rt_ubase_t level;
- rt_uint32_t int_mask, clk_en_a_old, clk_en_a;
- struct sdio_dw_slot *slot = host->private_data;
- struct sdio_dw *sd = slot->sd;
- const rt_uint32_t clken_low_pwr = SDIO_DW_CLKEN_LOW_PWR << slot->id;
- /*
- * Low power mode will stop the card clock when idle. According to the
- * description of the CLKENA register we should disable low power mode for
- * SDIO cards if we need SDIO interrupts to work.
- */
- clk_en_a_old = sdio_dw_readl(sd, CLKENA);
- if (enable)
- {
- rt_bitmap_set_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR);
- clk_en_a = clk_en_a_old & ~clken_low_pwr;
- }
- else
- {
- rt_bitmap_clear_bit(&slot->flags, DW_MMC_CARD_NO_LOW_PWR);
- clk_en_a = clk_en_a_old | clken_low_pwr;
- }
- if (clk_en_a != clk_en_a_old)
- {
- sdio_dw_writel(sd, CLKENA, clk_en_a);
- sdio_dw_send_cmd(slot, SDIO_DW_CMD_UPD_CLK | SDIO_DW_CMD_PRV_DAT_WAIT, 0);
- }
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- /* Enable/disable Slot Specific SDIO interrupt */
- int_mask = sdio_dw_readl(sd, INTMASK);
- if (enable)
- {
- int_mask |= SDIO_DW_INT_SDIO(slot->sdio_id);
- }
- else
- {
- int_mask &= ~SDIO_DW_INT_SDIO(slot->sdio_id);
- }
- sdio_dw_writel(sd, INTMASK, int_mask);
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- static rt_int32_t sdio_dw_mmc_execute_tuning(struct rt_mmcsd_host *host, rt_int32_t opcode)
- {
- struct sdio_dw_slot *slot = host->private_data;
- struct sdio_dw *sd = slot->sd;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- if (drv_data && drv_data->execute_tuning)
- {
- return drv_data->execute_tuning(slot, opcode);
- }
- return -RT_EINVAL;
- }
- static rt_bool_t sdio_dw_mmc_card_busy(struct rt_mmcsd_host *host)
- {
- rt_uint32_t status;
- struct sdio_dw_slot *slot = host->private_data;
- /* Check the busy bit which is low when DAT[3:0] (the data lines) are 0000 */
- status = sdio_dw_readl(slot->sd, STATUS);
- return !!(status & SDIO_DW_STATUS_BUSY);
- }
- static rt_err_t sdio_dw_mmc_signal_voltage_switch(struct rt_mmcsd_host *host,
- struct rt_mmcsd_io_cfg *ios)
- {
- rt_uint32_t uhs, v18;
- struct sdio_dw_slot *slot = host->private_data;
- struct sdio_dw *sd = slot->sd;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- v18 = SDIO_DW_UHS_18V << slot->id;
- if (drv_data && drv_data->switch_voltage)
- {
- return drv_data->switch_voltage(host, ios);
- }
- /*
- * Program the voltage. Note that some instances of dw_mmc may use
- * the UHS_REG for this. For other instances (like exynos) the UHS_REG
- * does no harm but you need to set the regulator directly. Try both.
- */
- uhs = sdio_dw_readl(sd, UHS_REG);
- if (ios->signal_voltage == MMCSD_SIGNAL_VOLTAGE_330)
- {
- uhs &= ~v18;
- }
- else
- {
- uhs |= v18;
- }
- if (host->supply.vqmmc)
- {
- rt_err_t err = sdio_regulator_set_vqmmc(host, ios);
- if (err < 0)
- {
- LOG_D("Regulator set error %s to %s V", rt_strerror(err),
- uhs & v18 ? "1.8" : "3.3");
- return err;
- }
- }
- sdio_dw_writel(sd, UHS_REG, uhs);
- return RT_EOK;
- }
- static const struct rt_mmcsd_host_ops sdio_dw_mmc_ops =
- {
- .request = sdio_dw_mmc_request,
- .set_iocfg = sdio_dw_mmc_set_iocfg,
- .get_card_status = sdio_dw_mmc_get_card_status,
- .enable_sdio_irq = sdio_dw_mmc_enable_sdio_irq,
- .execute_tuning = sdio_dw_mmc_execute_tuning,
- .card_busy = sdio_dw_mmc_card_busy,
- .signal_voltage_switch = sdio_dw_mmc_signal_voltage_switch,
- };
- static void sdio_dw_set_drto(struct sdio_dw *sd)
- {
- rt_ubase_t level;
- rt_uint32_t drto_clks, drto_div, drto_ms;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- if (drv_data && drv_data->get_drto_clks)
- {
- drto_clks = drv_data->get_drto_clks(sd);
- }
- else
- {
- drto_clks = sdio_dw_readl(sd, TMOUT) >> 8;
- }
- drto_div = (sdio_dw_readl(sd, CLKDIV) & 0xff) * 2;
- if (drto_div == 0)
- {
- drto_div = 1;
- }
- drto_ms = RT_DIV_ROUND_UP_ULL((rt_uint64_t)MSEC_PER_SEC * drto_clks * drto_div,
- sd->bus_hz);
- /* Add a bit spare time */
- drto_ms += 10;
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE))
- {
- rt_tick_t tick = rt_tick_from_millisecond(drto_ms);
- rt_timer_control(&sd->dto_timer, RT_TIMER_CTRL_SET_TIME, &tick);
- rt_timer_start(&sd->dto_timer);
- }
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- static rt_bool_t sdio_dw_clear_pending_cmd_complete(struct sdio_dw *sd)
- {
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE))
- {
- return RT_FALSE;
- }
- rt_timer_stop(&sd->cto_timer);
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_CMD_COMPLETE);
- return RT_TRUE;
- }
- static rt_bool_t sdio_dw_clear_pending_data_complete(struct sdio_dw *sd)
- {
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE))
- {
- return RT_FALSE;
- }
- rt_timer_stop(&sd->dto_timer);
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_DATA_COMPLETE);
- return RT_TRUE;
- }
- static void sdio_dw_state_change(struct rt_work *work, void *work_data)
- {
- rt_err_t err;
- rt_uint32_t state, prev_state;
- struct rt_mmcsd_cmd *cmd;
- struct rt_mmcsd_req *req;
- struct rt_mmcsd_data *data;
- struct sdio_dw *sd = rt_container_of(work, struct sdio_dw, state_work);
- rt_hw_spin_lock(&sd->lock.lock);
- state = sd->state;
- req = sd->req;
- data = sd->data;
- _next_status:
- prev_state = state;
- switch (state)
- {
- case STATE_IDLE:
- case STATE_WAITING_CMD11_DONE:
- break;
- case STATE_SENDING_CMD11:
- case STATE_SENDING_CMD:
- if (!sdio_dw_clear_pending_cmd_complete(sd))
- {
- break;
- }
- cmd = sd->cmd;
- sd->cmd = RT_NULL;
- err = sdio_dw_cmd_complete(sd, cmd);
- if (cmd == req->sbc && !err)
- {
- sdio_dw_start_request(sd, req->cmd);
- goto _unlock;
- }
- if (cmd->data && err)
- {
- if (err != -RT_ETIMEOUT && sd->dir_status == SDIO_DW_RECV_STATUS)
- {
- state = STATE_SENDING_DATA;
- goto _check_status;
- }
- send_stop_abort(sd, data);
- sdio_dw_stop_dma(sd);
- state = STATE_SENDING_STOP;
- break;
- }
- if (!cmd->data || err)
- {
- sdio_dw_end_request(sd);
- goto _unlock;
- }
- prev_state = state = STATE_SENDING_DATA;
- /* Fall through */
- case STATE_SENDING_DATA:
- if (rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR))
- {
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_DATA_ERROR);
- if (!(sd->data_status & (PINT(DRTO) | PINT(EBE))))
- {
- send_stop_abort(sd, data);
- }
- sdio_dw_stop_dma(sd);
- state = STATE_DATA_ERROR;
- break;
- }
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_XFER_COMPLETE))
- {
- /*
- * If all data-related interrupts don't come within the given time
- * in reading data state.
- */
- if (sd->dir_status == SDIO_DW_RECV_STATUS)
- {
- sdio_dw_set_drto(sd);
- }
- break;
- }
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- /*
- * Handle an EVENT_DATA_ERROR that might have shown up before the
- * transfer completed. This might not have been caught by the check
- * above because the interrupt could have gone off between the previous
- * check and the check for transfer complete.
- *
- * Technically this ought not be needed assuming we get a DATA_COMPLETE
- * eventually (we'll notice the error and end the request), but it
- * shouldn't hurt.
- *
- * This has the advantage of sending the stop command.
- */
- if (rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR))
- {
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_DATA_ERROR);
- if (!(sd->data_status & (PINT(DRTO) | PINT(EBE))))
- {
- send_stop_abort(sd, data);
- }
- sdio_dw_stop_dma(sd);
- state = STATE_DATA_ERROR;
- break;
- }
- prev_state = state = STATE_DATA_BUSY;
- /* Fall through */
- case STATE_DATA_BUSY:
- if (!sdio_dw_clear_pending_data_complete(sd))
- {
- /*
- * If data error interrupt comes but data over interrupt doesn't
- * come within the given time. In reading data state.
- */
- if (sd->dir_status == SDIO_DW_RECV_STATUS)
- {
- sdio_dw_set_drto(sd);
- }
- break;
- }
- sd->data = RT_NULL;
- err = sdio_dw_data_complete(sd, data);
- if (!err)
- {
- if (!data->stop || req->sbc)
- {
- if (req->sbc && data->stop)
- {
- data->stop->err = RT_EOK;
- }
- sdio_dw_end_request(sd);
- goto _unlock;
- }
- /* Stop command for open-ended transfer */
- if (data->stop)
- {
- send_stop_abort(sd, data);
- }
- }
- else
- {
- /*
- * If we don't have a command complete now we'll never get one since
- * we just reset everything; better end the request.
- *
- * If we do have a command complete we'll fall through to the
- * STATE_SENDING_STOP command and everything will be peachy keen.
- */
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE))
- {
- sd->cmd = RT_NULL;
- sdio_dw_end_request(sd);
- goto _unlock;
- }
- }
- /* If err has non-zero, stop-abort command has been already issued. */
- prev_state = state = STATE_SENDING_STOP;
- /* Fall through */
- case STATE_SENDING_STOP:
- if (!sdio_dw_clear_pending_cmd_complete(sd))
- {
- break;
- }
- /* CMD error in data command */
- if (req->cmd->err && req->data)
- {
- sdio_dw_reset(sd);
- }
- sd->cmd = RT_NULL;
- sd->data = RT_NULL;
- if (!req->sbc && req->stop)
- {
- sdio_dw_cmd_complete(sd, req->stop);
- }
- else
- {
- sd->cmd_status = 0;
- }
- sdio_dw_end_request(sd);
- goto _unlock;
- case STATE_DATA_ERROR:
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_XFER_COMPLETE))
- {
- break;
- }
- rt_bitmap_clear_bit(&sd->pending_events, EVENT_XFER_COMPLETE);
- state = STATE_DATA_BUSY;
- break;
- default:
- break;
- }
- _check_status:
- if (state != prev_state)
- {
- goto _next_status;
- }
- sd->state = state;
- _unlock:
- rt_hw_spin_unlock(&sd->lock.lock);
- }
- static void sdio_dw_cmd11_timer(void *param)
- {
- struct sdio_dw *sd = param;
- if (sd->state != STATE_SENDING_CMD11)
- {
- LOG_W("Unexpected CMD11 timeout");
- return;
- }
- sd->cmd_status = PINT(RTO);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- }
- static void sdio_dw_cto_timer(void *param)
- {
- rt_ubase_t level;
- rt_uint32_t pending;
- struct sdio_dw *sd = param;
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- /*
- * If somehow we have very bad interrupt latency it's remotely possible that
- * the timer could fire while the interrupt is still pending or while the
- * interrupt is midway through running. Let's be paranoid and detect those
- * two cases. Note that this is paranoia is somewhat justified because in
- * this function we don't actually cancel the pending command in the
- * controller, we just assume it will never come.
- */
- /* Read-only mask reg */
- pending = sdio_dw_readl(sd, MINTSTS);
- if ((pending & (SDIO_DW_CMD_ERROR_FLAGS | PINT(CMD_DONE))))
- {
- /* The interrupt should fire; no need to act but we can warn */
- LOG_W("Unexpected interrupt latency");
- goto _unlock;
- }
- if (rt_bitmap_test_bit(&sd->pending_events, EVENT_CMD_COMPLETE))
- {
- /* Presumably interrupt handler couldn't delete the timer */
- LOG_W("CTO timeout when already completed");
- goto _unlock;
- }
- /*
- * Continued paranoia to make sure we're in the state we expect.
- * This paranoia isn't really justified but it seems good to be safe.
- */
- switch (sd->state)
- {
- case STATE_SENDING_CMD11:
- case STATE_SENDING_CMD:
- case STATE_SENDING_STOP:
- /*
- * If CMD_DONE interrupt does NOT come in sending command state, we
- * should notify the driver to terminate current transfer and report a
- * command timeout to the core.
- */
- sd->cmd_status = PINT(RTO);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- break;
- default:
- LOG_W("Unexpected command timeout, state %d", sd->state);
- break;
- }
- _unlock:
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- static void sdio_dw_dto_timer(void *param)
- {
- rt_ubase_t level;
- rt_uint32_t pending;
- struct sdio_dw *sd = param;
- level = rt_spin_lock_irqsave(&sd->irq_lock);
- /*
- * The DTO timer is much longer than the CTO timer, so it's even less likely
- * that we'll these cases, but it pays to be paranoid.
- */
- /* Read-only mask reg */
- pending = sdio_dw_readl(sd, MINTSTS);
- if ((pending & PINT(DATA_OVER)))
- {
- /* The interrupt should fire; no need to act but we can warn */
- LOG_W("Unexpected data interrupt latency");
- goto _unlock;
- }
- if (rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_COMPLETE))
- {
- /* Presumably interrupt handler couldn't delete the timer */
- LOG_W("DTO timeout when already completed");
- goto _unlock;
- }
- /*
- * Continued paranoia to make sure we're in the state we expect.
- * This paranoia isn't really justified but it seems good to be safe.
- */
- switch (sd->state)
- {
- case STATE_SENDING_DATA:
- case STATE_DATA_BUSY:
- /*
- * If DTO interrupt does NOT come in sending data state, we should
- * notify the driver to terminate current transfer and report a data
- * timeout to the core.
- */
- sd->data_status = PINT(DRTO);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_DATA_ERROR);
- rt_bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- break;
- default:
- LOG_W("Unexpected data timeout, state %d", sd->state);
- break;
- }
- _unlock:
- rt_spin_unlock_irqrestore(&sd->irq_lock, level);
- }
- static void sdio_dw_cmd_interrupt(struct sdio_dw *sd, rt_uint32_t status)
- {
- rt_timer_stop(&sd->cto_timer);
- if (!sd->cmd_status)
- {
- sd->cmd_status = status;
- }
- /* Drain writebuffer */
- rt_hw_wmb();
- rt_bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- }
- static void sdio_dw_isr(int irqno, void *param)
- {
- rt_uint32_t pending;
- struct sdio_dw *sd = (struct sdio_dw *)param;
- struct sdio_dw_slot *slot = sd->slot;
- /* Read-only mask reg */
- pending = sdio_dw_readl(sd, MINTSTS);
- if (pending)
- {
- if (sd->state == STATE_SENDING_CMD11 && (pending & PINT(VOLT_SWITCH)))
- {
- sdio_dw_writel(sd, RINTSTS, PINT(VOLT_SWITCH));
- pending &= ~PINT(VOLT_SWITCH);
- rt_hw_spin_lock(&sd->irq_lock.lock);
- sdio_dw_cmd_interrupt(sd, pending);
- rt_hw_spin_unlock(&sd->irq_lock.lock);
- rt_timer_stop(&sd->cmd11_timer);
- }
- if ((pending & SDIO_DW_CMD_ERROR_FLAGS))
- {
- rt_hw_spin_lock(&sd->irq_lock.lock);
- rt_timer_stop(&sd->cto_timer);
- sdio_dw_writel(sd, RINTSTS, SDIO_DW_CMD_ERROR_FLAGS);
- sd->cmd_status = pending;
- rt_hw_wmb();
- rt_bitmap_set_bit(&sd->pending_events, EVENT_CMD_COMPLETE);
- rt_hw_spin_unlock(&sd->irq_lock.lock);
- }
- if ((pending & SDIO_DW_DATA_ERROR_FLAGS))
- {
- rt_hw_spin_lock(&sd->irq_lock.lock);
- if ((sd->quirks & SDIO_DW_QUIRK_EXTENDED_TMOUT))
- {
- rt_timer_stop(&sd->dto_timer);
- }
- sdio_dw_writel(sd, RINTSTS, SDIO_DW_DATA_ERROR_FLAGS);
- sd->data_status = pending;
- rt_hw_wmb();
- rt_bitmap_set_bit(&sd->pending_events, EVENT_DATA_ERROR);
- if ((sd->quirks & SDIO_DW_QUIRK_EXTENDED_TMOUT))
- {
- /* In case of error, we cannot expect a DTO */
- rt_bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE);
- }
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- rt_hw_spin_unlock(&sd->irq_lock.lock);
- }
- if ((pending & PINT(DATA_OVER)))
- {
- rt_hw_spin_lock(&sd->irq_lock.lock);
- rt_timer_stop(&sd->dto_timer);
- sdio_dw_writel(sd, RINTSTS, PINT(DATA_OVER));
- if (!sd->data_status)
- {
- sd->data_status = pending;
- }
- rt_hw_wmb();
- if (sd->dir_status == SDIO_DW_RECV_STATUS && sd->data && sd->data->buf)
- {
- sdio_dw_read_data_pio(sd, RT_TRUE);
- }
- rt_bitmap_set_bit(&sd->pending_events, EVENT_DATA_COMPLETE);
- rt_workqueue_urgent_work(sd->state_wq, &sd->state_work);
- rt_hw_spin_unlock(&sd->irq_lock.lock);
- }
- if ((pending & PINT(RXDR)))
- {
- sdio_dw_writel(sd, RINTSTS, PINT(RXDR));
- if (sd->dir_status == SDIO_DW_RECV_STATUS && sd->data && sd->data->buf)
- {
- sdio_dw_read_data_pio(sd, RT_FALSE);
- }
- }
- if ((pending & PINT(TXDR)))
- {
- sdio_dw_writel(sd, RINTSTS, PINT(TXDR));
- if (sd->dir_status == SDIO_DW_SEND_STATUS && sd->data && sd->data->buf)
- {
- sdio_dw_write_data_pio(sd);
- }
- }
- if ((pending & PINT(CMD_DONE)))
- {
- rt_hw_spin_lock(&sd->irq_lock.lock);
- sdio_dw_writel(sd, RINTSTS, PINT(CMD_DONE));
- sdio_dw_cmd_interrupt(sd, pending);
- rt_hw_spin_unlock(&sd->irq_lock.lock);
- }
- if ((pending & PINT(CD)))
- {
- sdio_dw_writel(sd, RINTSTS, PINT(CD));
- mmcsd_change(slot->host);
- }
- if ((pending & SDIO_DW_INT_SDIO(slot->sdio_id)))
- {
- sdio_dw_writel(sd, RINTSTS, SDIO_DW_INT_SDIO(slot->sdio_id));
- sdio_dw_mmc_enable_sdio_irq(slot->host, RT_FALSE);
- sdio_irq_wakeup(slot->host);
- }
- }
- if (sd->use_dma != TRANS_MODE_IDMAC)
- {
- return;
- }
- /* Handle IDMA interrupts */
- pending = sd->dma_64bit_address ? sdio_dw_readl(sd, IDSTS64) : sdio_dw_readl(sd, IDSTS);
- if ((pending & (PINTC(TI) | PINTC(RI))))
- {
- if (sd->dma_64bit_address)
- {
- sdio_dw_writel(sd, IDSTS64, PINTC(TI) | PINTC(RI));
- sdio_dw_writel(sd, IDSTS64, PINTC(NI));
- }
- else
- {
- sdio_dw_writel(sd, IDSTS, PINTC(TI) | PINTC(RI));
- sdio_dw_writel(sd, IDSTS, PINTC(NI));
- }
- if (!rt_bitmap_test_bit(&sd->pending_events, EVENT_DATA_ERROR))
- {
- sd->dma_ops->complete(sd);
- }
- }
- }
- #ifdef RT_USING_OFW
- static rt_err_t sdio_dw_parse_ofw(struct sdio_dw *sd)
- {
- struct rt_ofw_node *np = sd->parent.ofw_node;
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- rt_ofw_prop_read_u32(np, "fifo-depth", &sd->fifo_depth);
- rt_ofw_prop_read_u32(np, "card-detect-delay", &sd->detect_delay_ms);
- rt_ofw_prop_read_u32(np, "data-addr", &sd->data_addr_override);
- if (rt_ofw_prop_read_bool(np, "fifo-watermark-aligned"))
- {
- sd->wm_aligned = RT_TRUE;
- }
- rt_ofw_prop_read_u32(np, "clock-frequency", &sd->bus_hz);
- if (drv_data && drv_data->parse_ofw)
- {
- return drv_data->parse_ofw(sd);
- }
- return RT_EOK;
- }
- #else
- rt_inline rt_err_t sdio_dw_parse_ofw(struct sdio_dw *sd)
- {
- return -RT_ENOSYS;
- }
- #endif /* RT_USING_OFW */
- static rt_err_t sdio_dw_init_slot(struct sdio_dw *sd)
- {
- rt_err_t err;
- struct sdio_dw_slot *slot;
- struct rt_mmcsd_host *host = mmcsd_alloc_host();
- if (!host)
- {
- return -RT_ENOMEM;
- }
- slot = rt_calloc(1, sizeof(*slot));
- if (!slot)
- {
- err = -RT_ENOMEM;
- goto _free;
- }
- err = sdio_regulator_get_supply(&sd->parent, host);
- if (err)
- {
- goto _free;
- }
- host->ops = &sdio_dw_mmc_ops;
- host->private_data = slot;
- slot->host = host;
- slot->sd = sd;
- sd->slot = slot;
- slot->id = 0;
- slot->sdio_id = sd->sdio_id0 + slot->id;
- err = sdio_ofw_parse(sd->parent.ofw_node, host);
- if (err)
- {
- goto _free;
- }
- if (!host->valid_ocr)
- {
- host->valid_ocr = VDD_32_33 | VDD_33_34;
- }
- if (sd->minimum_speed)
- {
- host->freq_min = sd->minimum_speed;
- }
- else
- {
- host->freq_min = SDIO_DW_FREQ_HZ_MIN;
- }
- if (!host->freq_max)
- {
- host->freq_max = SDIO_DW_FREQ_HZ_MAX;
- }
- /* Useful defaults if platform data is unset. */
- if (sd->use_dma == TRANS_MODE_IDMAC)
- {
- host->max_dma_segs = sd->ring_size;
- host->max_blk_size = 65535;
- host->max_seg_size = DESC_RING_BUF_SZ;
- host->max_blk_count = (host->max_seg_size * sd->ring_size) / 512;
- }
- else if (sd->use_dma == TRANS_MODE_EDMAC)
- {
- host->max_dma_segs = 64;
- host->max_blk_size = 65535;
- host->max_blk_count = 65535;
- host->max_seg_size = host->max_blk_size * host->max_blk_count;
- }
- else
- {
- /* TRANS_MODE_PIO */
- host->max_dma_segs = 64;
- host->max_blk_size = 65535;
- host->max_blk_count = 512;
- host->max_seg_size = host->max_blk_size * host->max_blk_count;
- }
- return RT_EOK;
- _free:
- if (host)
- {
- mmcsd_free_host(host);
- }
- if (slot)
- {
- rt_free(slot);
- }
- return err;
- }
- static void sdio_dw_free(struct sdio_dw *sd)
- {
- if (!rt_is_err_or_null(sd->rstc))
- {
- rt_reset_control_assert(sd->rstc);
- rt_reset_control_put(sd->rstc);
- }
- if (!rt_is_err_or_null(sd->ciu_clk))
- {
- rt_clk_disable_unprepare(sd->ciu_clk);
- rt_clk_put(sd->ciu_clk);
- }
- if (!rt_is_err_or_null(sd->biu_clk))
- {
- rt_clk_disable_unprepare(sd->biu_clk);
- rt_clk_put(sd->biu_clk);
- }
- if (sd->use_dma && sd->dma_ops->exit)
- {
- sd->dma_ops->exit(sd);
- }
- if (sd->dma_buf)
- {
- rt_dma_free_coherent(sd->bus_dev,
- DESC_RING_BUF_SZ, sd->dma_buf, sd->dma_buf_phy);
- }
- }
- rt_err_t sdio_dw_probe(struct sdio_dw *sd)
- {
- int i, len;
- rt_err_t err = RT_EOK;
- char dev_name[RT_NAME_MAX];
- const struct sdio_dw_drv_data *drv_data = sd->drv_data;
- err = sdio_dw_parse_ofw(sd);
- if (err && err != -RT_ENOSYS)
- {
- goto _free_res;
- }
- sd->rstc = rt_reset_control_get_by_name(&sd->parent, "reset");
- if (rt_is_err(sd->rstc))
- {
- LOG_E("Reset controller not found");
- err = rt_ptr_err(sd->rstc);
- goto _free_res;
- }
- if (sd->rstc)
- {
- rt_reset_control_assert(sd->rstc);
- rt_hw_us_delay(20);
- rt_reset_control_deassert(sd->rstc);
- }
- sd->biu_clk = rt_clk_get_by_name(&sd->parent, "biu");
- sd->ciu_clk = rt_clk_get_by_name(&sd->parent, "ciu");
- if (rt_is_err(sd->biu_clk) || rt_is_err(sd->ciu_clk))
- {
- /* board has init clock */
- if (sd->bus_hz)
- {
- goto _out_clk;
- }
- err = rt_is_err(sd->biu_clk) ? rt_ptr_err(sd->biu_clk) : rt_ptr_err(sd->ciu_clk);
- goto _free_res;
- }
- err = rt_clk_prepare_enable(sd->ciu_clk);
- if (err)
- {
- goto _free_res;
- }
- if (sd->bus_hz)
- {
- rt_clk_set_rate(sd->ciu_clk, sd->bus_hz);
- }
- sd->bus_hz = rt_clk_get_rate(sd->ciu_clk);
- if (!sd->bus_hz)
- {
- err = -RT_EIO;
- LOG_E("Bus speed not found");
- goto _free_res;
- }
- _out_clk:
- if (drv_data && drv_data->init)
- {
- err = drv_data->init(sd);
- if (err)
- {
- goto _free_res;
- }
- }
- rt_spin_lock_init(&sd->lock);
- rt_spin_lock_init(&sd->irq_lock);
- /*
- * Get the host data width - this assumes that HCON has been set with the
- * correct values.
- */
- i = SDIO_DW_GET_HDATA_WIDTH(sdio_dw_readl(sd, HCON));
- if (!i)
- {
- sd->push_data = sdio_dw_push_data16;
- sd->pull_data = sdio_dw_pull_data16;
- sd->data_shift = 1;
- }
- else if (i == 2)
- {
- sd->push_data = sdio_dw_push_data64;
- sd->pull_data = sdio_dw_pull_data64;
- sd->data_shift = 3;
- }
- else
- {
- /* Check for a reserved value, and warn if it is */
- if (i != 1)
- {
- LOG_W("HCON reports a reserved host data width, defaulting to 32-bit access");
- }
- sd->push_data = sdio_dw_push_data32;
- sd->pull_data = sdio_dw_pull_data32;
- sd->data_shift = 2;
- }
- /* Reset all blocks */
- if (!sdio_dw_ctrl_reset(sd, SDIO_DW_CTRL_ALL_RESET_FLAGS))
- {
- err = -RT_EIO;
- goto _free_res;
- }
- sdio_dw_init_dma(sd);
- /* Clear the interrupts for the host controller */
- sdio_dw_writel(sd, RINTSTS, 0xffffffff);
- /* Disable all mmc interrupt first */
- sdio_dw_writel(sd, INTMASK, 0);
- /* Put in max timeout */
- sdio_dw_writel(sd, TMOUT, 0xffffffff);
- /*
- * FIFO threshold settings:
- * Rx Mark = fifo_size / 2 - 1,
- * Tx Mark = fifo_size / 2
- * DMA Size = 8
- */
- if (sd->fifo_depth)
- {
- /*
- * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may have been
- * overwritten by the bootloader, just like we're about to do, so if you
- * know the value for your hardware, you should put it in the platform
- * data.
- */
- sd->fifo_depth = sdio_dw_readl(sd, FIFOTH);
- sd->fifo_depth = 1 + ((sd->fifo_depth >> 16) & 0xfff);
- }
- sd->fifoth_val = SDIO_DW_SET_FIFOTH(0x2, sd->fifo_depth / 2 - 1, sd->fifo_depth / 2);
- sdio_dw_writel(sd, FIFOTH, sd->fifoth_val);
- /* Disable clock to CIU */
- sdio_dw_writel(sd, CLKENA, 0);
- sdio_dw_writel(sd, CLKSRC, 0);
- /*
- * In 2.40a spec, Data offset is changed.
- * Need to check the version-id and set data-offset for DATA register.
- */
- sd->verid = SDIO_DW_GET_VERID(sdio_dw_readl(sd, VERID));
- LOG_D("Version ID is %04x", sd->verid);
- if (sd->data_addr_override)
- {
- sd->fifo_base = sd->base + sd->data_addr_override;
- }
- else if (sd->verid < SDIO_DW_240A)
- {
- sd->fifo_base = sd->base + DATA_OFFSET;
- }
- else
- {
- sd->fifo_base = sd->base + DATA_240A_OFFSET;
- }
- /*
- * Enable interrupts for command done, data over, data empty, receive ready
- * and error such as transmit, receive timeout, crc error
- */
- sdio_dw_writel(sd, INTMASK, PINT(CMD_DONE) | PINT(DATA_OVER) | PINT(TXDR) | PINT(RXDR) | SDIO_DW_ERROR_FLAGS);
- /* Enable mci interrupt */
- sdio_dw_writel(sd, CTRL, SDIO_DW_CTRL_INT_ENABLE);
- /* Enable GPIO interrupt */
- sdio_dw_writel(sd, INTMASK, sdio_dw_readl(sd, INTMASK) | PINT(CD));
- /* We need at least one slot to succeed */
- err = sdio_dw_init_slot(sd);
- if (err)
- {
- goto _free_res;
- }
- /* Now that slots are all setup, we can enable card detect */
- sdio_dw_writel(sd, INTMASK, sdio_dw_readl(sd, INTMASK) | PINT(CD));
- len = sdio_host_set_name(sd->slot->host, dev_name);
- sd->state_wq = rt_workqueue_create(dev_name, RT_SYSTEM_WORKQUEUE_STACKSIZE,
- RT_MMCSD_THREAD_PRIORITY);
- if (!sd->state_wq)
- {
- err = -RT_ENOMEM;
- goto _free_res;
- }
- rt_work_init(&sd->state_work, sdio_dw_state_change, sd);
- rt_hw_interrupt_install(sd->irq, sdio_dw_isr, sd, dev_name);
- rt_hw_interrupt_umask(sd->irq);
- rt_strncpy(&dev_name[len], "-cmd11", sizeof(dev_name) - len);
- rt_timer_init(&sd->cmd11_timer, dev_name, sdio_dw_cmd11_timer, sd,
- 0, RT_TIMER_FLAG_PERIODIC);
- rt_strncpy(&dev_name[len], "-cto", sizeof(dev_name) - len);
- rt_timer_init(&sd->cto_timer, dev_name, sdio_dw_cto_timer, sd,
- 0, RT_TIMER_FLAG_PERIODIC);
- rt_strncpy(&dev_name[len], "-dto", sizeof(dev_name) - len);
- rt_timer_init(&sd->dto_timer, dev_name, sdio_dw_dto_timer, sd,
- 0, RT_TIMER_FLAG_PERIODIC);
- mmcsd_change(sd->slot->host);
- return err;
- _free_res:
- sdio_dw_free(sd);
- return err;
- }
- rt_err_t sdio_dw_remove(struct sdio_dw *sd)
- {
- if (sd->slot)
- {
- mmcsd_free_host(sd->slot->host);
- }
- sdio_dw_writel(sd, RINTSTS, 0xffffffff);
- /* Disable all mmc interrupt first */
- sdio_dw_writel(sd, INTMASK, 0);
- /* Disable clock to CIU */
- sdio_dw_writel(sd, CLKENA, 0);
- sdio_dw_writel(sd, CLKSRC, 0);
- rt_hw_interrupt_mask(sd->irq);
- rt_pic_detach_irq(sd->irq, sd);
- rt_timer_detach(&sd->cmd11_timer);
- rt_timer_detach(&sd->cto_timer);
- rt_timer_detach(&sd->dto_timer);
- sdio_dw_free(sd);
- return RT_EOK;
- }
|