| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345 |
- From bc755a3b8859e7307a8b10f39ca4cb6401c51987 Mon Sep 17 00:00:00 2001
- From: Kurt Mahan <[email protected]>
- Date: Tue, 27 Nov 2007 14:39:37 -0700
- Subject: [PATCH] Add M5445x SPI support.
- LTIBName: m5445x-spi
- Signed-off-by: Kurt Mahan <[email protected]>
- ---
- arch/m68k/configs/m54455evb_defconfig | 24 +-
- drivers/spi/Kconfig | 36 +
- drivers/spi/Makefile | 4 +
- drivers/spi/coldfire_edma.c | 358 ++++++++
- drivers/spi/spi-m5445x.c | 156 ++++
- drivers/spi/spi_coldfire.c | 1552 +++++++++++++++++++++++++++++++++
- drivers/spi/ssi_audio.c | 906 +++++++++++++++++++
- include/asm-m68k/coldfire_edma.h | 101 ++-
- include/linux/spi/mcfqspi.h | 80 ++
- 9 files changed, 3196 insertions(+), 21 deletions(-)
- create mode 100644 drivers/spi/coldfire_edma.c
- create mode 100644 drivers/spi/spi-m5445x.c
- create mode 100644 drivers/spi/spi_coldfire.c
- create mode 100644 drivers/spi/ssi_audio.c
- create mode 100644 include/linux/spi/mcfqspi.h
- --- a/arch/m68k/configs/m54455evb_defconfig
- +++ b/arch/m68k/configs/m54455evb_defconfig
- @@ -321,6 +321,8 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=1
- #
- # Self-contained MTD device drivers
- #
- +# CONFIG_MTD_DATAFLASH is not set
- +# CONFIG_MTD_M25P80 is not set
- # CONFIG_MTD_SLRAM is not set
- # CONFIG_MTD_PHRAM is not set
- # CONFIG_MTD_MTDRAM is not set
- @@ -497,8 +499,26 @@ CONFIG_UNIX98_PTYS=y
- #
- # SPI support
- #
- -# CONFIG_SPI is not set
- -# CONFIG_SPI_MASTER is not set
- +CONFIG_SPI=y
- +# CONFIG_SPI_DEBUG is not set
- +CONFIG_COLDFIRE_EDMA=y
- +CONFIG_SPI_MASTER=y
- +
- +#
- +# SPI Master Controller Drivers
- +#
- +# CONFIG_SPI_BITBANG is not set
- +CONFIG_SPI_COLDFIRE=y
- +CONFIG_SPI_COLDFIRE_DSPI_EDMA=y
- +
- +#
- +# SPI Protocol Masters
- +#
- +# CONFIG_SPI_AT25 is not set
- +# CONFIG_SPI_SPIDEV is not set
- +# CONFIG_SPI_TLE62X0 is not set
- +CONFIG_SPI_COLDFIRE_SSI_AUDIO=y
- +# CONFIG_SSIAUDIO_USE_EDMA is not set
- # CONFIG_W1 is not set
- # CONFIG_POWER_SUPPLY is not set
- # CONFIG_HWMON is not set
- --- a/drivers/spi/Kconfig
- +++ b/drivers/spi/Kconfig
- @@ -35,6 +35,15 @@ config SPI_DEBUG
- Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
- sysfs, and debugfs support in SPI controller and protocol drivers.
-
- +config COLDFIRE_EDMA
- + tristate "Coldfire eDMA"
- + depends on COLDFIRE && EXPERIMENTAL
- + help
- + Support for Coldfire eDMA controller. Required for example
- + by SSI audio device driver.
- +
- +
- +
- #
- # MASTER side ... talking to discrete SPI slave chips including microcontrollers
- #
- @@ -113,6 +122,21 @@ config SPI_GPIO
-
- If unsure, say N.
-
- +config SPI_COLDFIRE
- + tristate "Coldfire QSPI/DSPI SPI Master"
- + depends on SPI_MASTER && COLDFIRE && EXPERIMENTAL
- + help
- + SPI driver for Freescale Coldfire QSPI module in master mode.
- + Tested with the 5282 processor, but should also work with other
- + Coldfire variants.
- +
- +config SPI_COLDFIRE_DSPI_EDMA
- + boolean "Coldfire DSPI master driver uses eDMA"
- + depends on SPI_MASTER && COLDFIRE && SPI_COLDFIRE && EXPERIMENTAL && COLDFIRE_EDMA
- + default n
- + help
- + Say "yes" if you want DSPI master driver to use eDMA for transfers.
- +
- config SPI_IMX
- tristate "Freescale iMX SPI controller"
- depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
- @@ -255,6 +279,18 @@ config SPI_TLE62X0
- #
- # Add new SPI protocol masters in alphabetical order above this line
- #
- +config SPI_COLDFIRE_SSI_AUDIO
- + tristate "Coldfire SSI AUDIO"
- + depends on SPI_MASTER && SPI_COLDFIRE && EXPERIMENTAL
- + help
- + SSI audio device driver
- +
- +config SSIAUDIO_USE_EDMA
- + boolean "Coldfire DSPI master driver uses eDMA"
- + default y
- + depends on EXPERIMENTAL && COLDFIRE_EDMA && SPI_COLDFIRE_SSI_AUDIO
- + help
- + Say "yes" if you want SSI audio driver to use eDMA for SSI transfers.
-
- # (slave support would go here)
-
- --- a/drivers/spi/Makefile
- +++ b/drivers/spi/Makefile
- @@ -6,6 +6,8 @@ ifeq ($(CONFIG_SPI_DEBUG),y)
- EXTRA_CFLAGS += -DDEBUG
- endif
-
- +obj-$(CONFIG_COLDFIRE_EDMA) += coldfire_edma.o
- +
- # small core, mostly translating board-specific
- # config declarations into driver model code
- obj-$(CONFIG_SPI_MASTER) += spi.o
- @@ -16,6 +18,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.
- obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
- obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
- obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
- +obj-$(CONFIG_SPI_COLDFIRE) += spi_coldfire.o spi-m5445x.o
- obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
- obj-$(CONFIG_SPI_IMX) += spi_imx.o
- obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
- @@ -35,6 +38,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.
- obj-$(CONFIG_SPI_AT25) += at25.o
- obj-$(CONFIG_SPI_SPIDEV) += spidev.o
- obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o
- +obj-$(CONFIG_SPI_COLDFIRE_SSI_AUDIO) += ssi_audio.o
- # ... add above this line ...
-
- # SPI slave controller drivers (upstream link)
- --- /dev/null
- +++ b/drivers/spi/coldfire_edma.c
- @@ -0,0 +1,358 @@
- +/*
- + *
- + * coldfire_edma.c - eDMA driver for Coldfire MCF5445x
- + *
- + * Yaroslav Vinogradov [email protected]
- + *
- + * Copyright Freescale Semiconductor, Inc. 2007
- + *
- + * This program is free software; you can redistribute it and/or modify it
- + * under the terms of the GNU General Public License as published by the
- + * Free Software Foundation; either version 2 of the License, or (at your
- + * option) any later version.
- + */
- +
- +#include <linux/init.h>
- +#include <linux/module.h>
- +#include <asm/virtconvert.h>
- +#include <asm/coldfire.h>
- +#include <linux/fs.h>
- +#include <linux/cdev.h>
- +#include <linux/seq_file.h>
- +#include <linux/proc_fs.h>
- +#include <asm/mcf5445x_edma.h>
- +#include <asm/mcf5445x_intc.h>
- +#include <asm/coldfire_edma.h>
- +
- +
- +/* callback handler data for each TCD */
- +struct edma_isr_record {
- + edma_irq_handler irq_handler; /* interrupt handler */
- + edma_error_handler error_handler; /* error interrupt handler */
- + void* dev; /* device used for the channel */
- + int allocated; /* busy flag */
- + spinlock_t *lock; /* spin lock (if needs to be locked in interrupt) */
- + const char* device_id; /* device id string, used in proc file system */
- +};
- +
- +/* device structure */
- +struct coldfire_edma_dev {
- + struct cdev cdev; /* character device */
- + struct edma_isr_record dma_interrupt_handlers[EDMA_CHANNELS]; /* channel handlers */
- +};
- +
- +/* allocated major device number */
- +static int coldfire_dma_major;
- +/* device driver structure */
- +static struct coldfire_edma_dev* devp = NULL;
- +
- +/* device driver file operations */
- +struct file_operations coldfire_edma_fops = {
- + .owner = THIS_MODULE,
- +};
- +
- +/* eDMA channel interrupt handler */
- +static int dmaisr(int irq, void *dev_id)
- +{
- + int channel = irq - EDMA_INT_CONTROLLER_BASE - EDMA_INT_CHANNEL_BASE;
- + int result = IRQ_HANDLED;
- +
- + if (devp!=NULL && devp->dma_interrupt_handlers[channel].lock) {
- + spin_lock(devp->dma_interrupt_handlers[channel].lock);
- + }
- +
- + if (devp!=NULL && devp->dma_interrupt_handlers[channel].irq_handler) {
- + result = devp->dma_interrupt_handlers[channel].irq_handler(channel,
- + devp->dma_interrupt_handlers[channel].dev);
- + } else {
- + confirm_edma_interrupt_handled(channel);
- + printk(EDMA_DRIVER_NAME ": No handler for DMA channel %d\n", channel);
- + }
- +
- + if (devp!=NULL && devp->dma_interrupt_handlers[channel].lock) {
- + spin_unlock(devp->dma_interrupt_handlers[channel].lock);
- + }
- +
- + return result;
- +}
- +
- +/* eDMA error interrupt handler */
- +static int dma_error_isr(int irq, void* dev_id)
- +{
- + u16 err;
- + int i;
- +
- + err = MCF_EDMA_ERR;
- + for (i=0;i<EDMA_CHANNELS;i++) {
- + if (err & (1<<i)) {
- + if (devp!=NULL && devp->dma_interrupt_handlers[i].error_handler) {
- + devp->dma_interrupt_handlers[i].error_handler(i, devp->dma_interrupt_handlers[i].dev);
- + } else {
- + printk(KERN_WARNING EDMA_DRIVER_NAME ": DMA error on channel %d\n", i);
- + }
- + }
- + }
- +
- + MCF_EDMA_CERR = MCF_EDMA_CERR_CAER;
- + return IRQ_HANDLED;
- +}
- +
- +/* sets channel parameters */
- +void set_edma_params(int channel, u32 source, u32 dest,
- + u32 attr, u32 soff, u32 nbytes, u32 slast,
- + u32 citer, u32 biter, u32 doff, u32 dlast_sga,
- + int major_int, int disable_req)
- +{
- +
- + if (channel<0 || channel>EDMA_CHANNELS)
- + return;
- +
- + MCF_EDMA_TCD_SADDR(channel) = source;
- + MCF_EDMA_TCD_DADDR(channel) = dest;
- + MCF_EDMA_TCD_ATTR(channel) = attr;
- + MCF_EDMA_TCD_SOFF(channel) = MCF_EDMA_TCD_SOFF_SOFF(soff);
- + MCF_EDMA_TCD_NBYTES(channel) = MCF_EDMA_TCD_NBYTES_NBYTES(nbytes);
- + MCF_EDMA_TCD_SLAST(channel) = MCF_EDMA_TCD_SLAST_SLAST(slast);
- + MCF_EDMA_TCD_CITER(channel) = MCF_EDMA_TCD_CITER_CITER(citer);
- + MCF_EDMA_TCD_BITER(channel)=MCF_EDMA_TCD_BITER_BITER(biter);
- + MCF_EDMA_TCD_DOFF(channel) = MCF_EDMA_TCD_DOFF_DOFF(doff);
- + MCF_EDMA_TCD_DLAST_SGA(channel) = MCF_EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga);
- + /* interrupt at the end of major loop */
- + if (major_int) {
- + MCF_EDMA_TCD_CSR(channel) |= MCF_EDMA_TCD_CSR_INT_MAJOR;
- + } else {
- + MCF_EDMA_TCD_CSR(channel) &= ~MCF_EDMA_TCD_CSR_INT_MAJOR;
- + }
- + /* disable request at the end of major loop of transfer or not*/
- + if (disable_req) {
- + MCF_EDMA_TCD_CSR(channel) |= MCF_EDMA_TCD_CSR_D_REQ;
- + } else {
- + MCF_EDMA_TCD_CSR(channel) &= ~MCF_EDMA_TCD_CSR_D_REQ;
- + }
- +
- +}
- +EXPORT_SYMBOL(set_edma_params);
- +
- +/* init eDMA controller */
- +void init_edma(void)
- +{
- + MCF_EDMA_CR = 0;
- +}
- +EXPORT_SYMBOL(init_edma);
- +
- +/* request eDMA channel */
- +int request_edma_channel(int channel,
- + edma_irq_handler handler,
- + edma_error_handler error_handler,
- + void* dev,
- + spinlock_t *lock,
- + const char* device_id )
- +{
- + if (devp!=NULL && channel>=0 && channel<=EDMA_CHANNELS) {
- + if (devp->dma_interrupt_handlers[channel].allocated) {
- + return -EBUSY;
- + }
- + devp->dma_interrupt_handlers[channel].allocated = 1;
- + devp->dma_interrupt_handlers[channel].irq_handler = handler;
- + devp->dma_interrupt_handlers[channel].error_handler = error_handler;
- + devp->dma_interrupt_handlers[channel].dev = dev;
- + devp->dma_interrupt_handlers[channel].lock = lock;
- + devp->dma_interrupt_handlers[channel].device_id = device_id;
- + return 0;
- + }
- + return -EINVAL;
- +}
- +EXPORT_SYMBOL(request_edma_channel);
- +
- +/* free eDMA channel */
- +int free_edma_channel(int channel, void* dev)
- +{
- + if (devp!=NULL && channel>=0 && channel<=EDMA_CHANNELS) {
- + if (devp->dma_interrupt_handlers[channel].allocated) {
- + if (devp->dma_interrupt_handlers[channel].dev != dev) {
- + return -EBUSY;
- + }
- + devp->dma_interrupt_handlers[channel].allocated = 0;
- + devp->dma_interrupt_handlers[channel].dev = NULL;
- + devp->dma_interrupt_handlers[channel].irq_handler = NULL;
- + devp->dma_interrupt_handlers[channel].error_handler = NULL;
- + devp->dma_interrupt_handlers[channel].lock = NULL;
- + }
- + return 0;
- + }
- + return -EINVAL;
- +}
- +EXPORT_SYMBOL(free_edma_channel);
- +
- +/* clean-up device driver allocated resources */
- +static void coldfire_edma_cleanup(void)
- +{
- + dev_t devno;
- + int i;
- +
- + /* free interrupts/memory */
- + if (devp) {
- + for (i=0;i<EDMA_CHANNELS;i++)
- + {
- + MCF_INTC0_SIMR = EDMA_INT_CHANNEL_BASE+i;
- + free_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i, devp);
- + }
- + MCF_INTC0_SIMR = EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS;
- + free_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+EDMA_CHANNELS, devp);
- + cdev_del(&devp->cdev);
- + kfree(devp);
- + }
- +
- + /* unregister character device */
- + devno = MKDEV(coldfire_dma_major, 0);
- + unregister_chrdev_region(devno, 1);
- +}
- +
- +#ifdef CONFIG_PROC_FS
- +/* proc file system support */
- +
- +#define FREE_CHANNEL "free"
- +#define DEVICE_UNKNOWN "device unknown"
- +
- +static int proc_edma_show(struct seq_file *m, void *v)
- +{
- + int i;
- +
- + if (devp==NULL) return 0;
- +
- + for (i = 0 ; i < EDMA_CHANNELS ; i++) {
- + if (devp->dma_interrupt_handlers[i].allocated) {
- + if (devp->dma_interrupt_handlers[i].device_id)
- + seq_printf(m, "%2d: %s\n", i, devp->dma_interrupt_handlers[i].device_id);
- + else
- + seq_printf(m, "%2d: %s\n", i, DEVICE_UNKNOWN);
- + } else {
- + seq_printf(m, "%2d: %s\n", i, FREE_CHANNEL);
- + }
- + }
- + return 0;
- +}
- +
- +static int proc_edma_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, proc_edma_show, NULL);
- +}
- +
- +static const struct file_operations proc_edma_operations = {
- + .open = proc_edma_open,
- + .read = seq_read,
- + .llseek = seq_lseek,
- + .release = single_release,
- +};
- +
- +static int __init proc_edma_init(void)
- +{
- + struct proc_dir_entry *e;
- +
- + e = create_proc_entry("edma", 0, NULL);
- + if (e)
- + e->proc_fops = &proc_edma_operations;
- +
- + return 0;
- +}
- +
- +#endif
- +
- +/* initializes device driver */
- +static int __init coldfire_edma_init(void)
- +{
- + dev_t dev;
- + int result;
- + int i;
- +
- + /* allocate free major number */
- + result = alloc_chrdev_region(&dev, DMA_DEV_MINOR, 1, EDMA_DRIVER_NAME);
- + if (result<0) {
- + printk(KERN_WARNING EDMA_DRIVER_NAME": can't get major %d\n", result);
- + return result;
- + }
- + coldfire_dma_major = MAJOR(dev);
- +
- + /* allocate device driver structure */
- + devp = kmalloc(sizeof(struct coldfire_edma_dev), GFP_KERNEL);
- + if (!devp) {
- + result = -ENOMEM;
- + goto fail;
- + }
- +
- + /* init handlers (no handlers for beggining) */
- + for (i=0;i<EDMA_CHANNELS;i++) {
- + devp->dma_interrupt_handlers[i].irq_handler = NULL;
- + devp->dma_interrupt_handlers[i].error_handler = NULL;
- + devp->dma_interrupt_handlers[i].dev = NULL;
- + devp->dma_interrupt_handlers[i].allocated = 0;
- + devp->dma_interrupt_handlers[i].lock = NULL;
- + devp->dma_interrupt_handlers[i].device_id = NULL;
- + }
- +
- + /* register char device */
- + cdev_init(&devp->cdev, &coldfire_edma_fops);
- + devp->cdev.owner = THIS_MODULE;
- + devp->cdev.ops = &coldfire_edma_fops;
- + result = cdev_add(&devp->cdev, dev, 1);
- + if (result) {
- + printk(KERN_NOTICE EDMA_DRIVER_NAME": Error %d adding coldfire-dma device\n", result);
- + result = -ENODEV;
- + goto fail;
- + }
- +
- + /* request/enable irq for each eDMA channel */
- + for (i=0;i<EDMA_CHANNELS;i++)
- + {
- + result = request_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i,
- + dmaisr, SA_INTERRUPT, EDMA_DRIVER_NAME, devp);
- + if (result) {
- + printk(KERN_WARNING EDMA_DRIVER_NAME": Cannot request irq %d\n",
- + EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i);
- + result = -EBUSY;
- + goto fail;
- + }
- +
- + MCF_INTC0_ICR(EDMA_INT_CHANNEL_BASE+i) = EDMA_IRQ_LEVEL;
- + MCF_INTC0_CIMR = EDMA_INT_CHANNEL_BASE+i;
- +
- + }
- +
- + /* request error interrupt */
- + result = request_irq(EDMA_INT_CHANNEL_BASE + EDMA_INT_CONTROLLER_BASE + EDMA_CHANNELS,
- + dma_error_isr, SA_INTERRUPT, EDMA_DRIVER_NAME, devp);
- + if (result) {
- + printk(KERN_WARNING EDMA_DRIVER_NAME": Cannot request irq %d\n",
- + EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+EDMA_CHANNELS);
- + result = -EBUSY;
- + goto fail;
- + }
- +
- + /* enable error interrupt in interrupt controller */
- + MCF_INTC0_ICR(EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS) = EDMA_IRQ_LEVEL;
- + MCF_INTC0_CIMR = EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS;
- +
- +#ifdef CONFIG_PROC_FS
- + proc_edma_init();
- +#endif
- +
- + printk(EDMA_DRIVER_NAME ": initialized successfully\n");
- +
- + return 0;
- +fail:
- + coldfire_edma_cleanup();
- + return result;
- +
- +}
- +
- +static void __exit coldfire_edma_exit(void)
- +{
- + coldfire_edma_cleanup();
- +}
- +
- +module_init(coldfire_edma_init);
- +module_exit(coldfire_edma_exit);
- +
- +MODULE_LICENSE("GPL");
- +MODULE_AUTHOR("Yaroslav Vinogradov, Freescale Inc.");
- +MODULE_DESCRIPTION("eDMA library for Coldfire 5445x");
- --- /dev/null
- +++ b/drivers/spi/spi-m5445x.c
- @@ -0,0 +1,156 @@
- +/***************************************************************************/
- +/*
- + * linux/arch/m68k/coldfire/spi-m5445x.c
- + *
- + * Sub-architcture dependant initialization code for the Freescale
- + * 5445x SPI module
- + *
- + * Yaroslav Vinogradov [email protected]
- + * Copyright Freescale Semiconductor, Inc 2007
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation; either version 2 of the License, or
- + * (at your option) any later version.
- + */
- +/***************************************************************************/
- +
- +
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/param.h>
- +#include <linux/init.h>
- +#include <linux/interrupt.h>
- +#include <linux/device.h>
- +#include <linux/platform_device.h>
- +#include <linux/spi/spi.h>
- +
- +#include <asm/dma.h>
- +#include <asm/traps.h>
- +#include <asm/machdep.h>
- +#include <asm/coldfire.h>
- +#include <asm/mcfsim.h>
- +#include <asm/mcfqspi.h>
- +#include <asm/mcf5445x_gpio.h>
- +
- +#define SPI_NUM_CHIPSELECTS 0x10
- +#define SPI_PAR_VAL (0 | MCF_GPIO_PAR_DSPI_PCS5_PCS5 | MCF_GPIO_PAR_DSPI_PCS2_PCS2 \
- + | MCF_GPIO_PAR_DSPI_PCS1_PCS1 | MCF_GPIO_PAR_DSPI_PCS0_PCS0 | MCF_GPIO_PAR_DSPI_SIN_SIN \
- + | MCF_GPIO_PAR_DSPI_SOUT_SOUT | MCF_GPIO_PAR_DSPI_SCK_SCK)
- +
- +#define MCF5445x_DSPI_IRQ_SOURCE (31)
- +#define MCF5445x_DSPI_IRQ_VECTOR (64 + MCF5445x_DSPI_IRQ_SOURCE)
- +
- +#define MCF5445x_DSPI_PAR (0xFC0A4063)
- +#define MCF5445x_DSPI_MCR (0xFC05C000)
- +#define MCF5445x_INTC0_ICR (0xFC048040)
- +#define MCF5445x_INTC0_IMRL (0xFC04800C)
- +
- +
- +#define M5445x_AUDIO_IRQ_SOURCE 49
- +#define M5445x_AUDIO_IRQ_VECTOR (128+M5445x_AUDIO_IRQ_SOURCE)
- +#define M5445x_AUDIO_IRQ_LEVEL 4
- +
- +void coldfire_qspi_cs_control(u8 cs, u8 command)
- +{
- +}
- +
- +#if defined(CONFIG_SPI_COLDFIRE_SSI_AUDIO)
- +static struct coldfire_spi_chip ssi_audio_chip_info = {
- + .mode = SPI_MODE_0,
- + .bits_per_word = 16,
- + .del_cs_to_clk = 16,
- + .del_after_trans = 16,
- + .void_write_data = 0
- +};
- +
- +#endif
- +
- +static struct spi_board_info spi_board_info[] = {
- +
- +#if defined(CONFIG_SPI_COLDFIRE_SSI_AUDIO)
- + {
- + .modalias = "ssi_audio",
- + .max_speed_hz = 300000,
- + .bus_num = 1,
- + .chip_select = 5,
- + .irq = M5445x_AUDIO_IRQ_VECTOR,
- + .platform_data = NULL,
- + .controller_data = &ssi_audio_chip_info
- + }
- +#endif
- +
- +};
- +
- +static struct coldfire_spi_master coldfire_master_info = {
- + .bus_num = 1,
- + .num_chipselect = SPI_NUM_CHIPSELECTS,
- + .irq_source = MCF5445x_DSPI_IRQ_SOURCE,
- + .irq_vector = MCF5445x_DSPI_IRQ_VECTOR,
- + .irq_mask = (0x01 << MCF5445x_DSPI_IRQ_SOURCE),
- + .irq_lp = 0x2, /* Level */
- + .par_val = SPI_PAR_VAL,
- +// .par_val16 = SPI_PAR_VAL,
- + .cs_control = coldfire_qspi_cs_control,
- +};
- +
- +static struct resource coldfire_spi_resources[] = {
- + [0] = {
- + .name = "qspi-par",
- + .start = MCF5445x_DSPI_PAR,
- + .end = MCF5445x_DSPI_PAR,
- + .flags = IORESOURCE_MEM
- + },
- +
- + [1] = {
- + .name = "qspi-module",
- + .start = MCF5445x_DSPI_MCR,
- + .end = MCF5445x_DSPI_MCR + 0xB8,
- + .flags = IORESOURCE_MEM
- + },
- +
- + [2] = {
- + .name = "qspi-int-level",
- + .start = MCF5445x_INTC0_ICR + MCF5445x_DSPI_IRQ_SOURCE,
- + .end = MCF5445x_INTC0_ICR + MCF5445x_DSPI_IRQ_SOURCE,
- + .flags = IORESOURCE_MEM
- + },
- +
- + [3] = {
- + .name = "qspi-int-mask",
- + .start = MCF5445x_INTC0_IMRL,
- + .end = MCF5445x_INTC0_IMRL,
- + .flags = IORESOURCE_MEM
- + }
- +};
- +
- +static struct platform_device coldfire_spi = {
- + .name = "spi_coldfire", //"coldfire-qspi",
- + .id = -1,
- + .resource = coldfire_spi_resources,
- + .num_resources = ARRAY_SIZE(coldfire_spi_resources),
- + .dev = {
- + .platform_data = &coldfire_master_info,
- + }
- +};
- +
- +static int __init spi_dev_init(void)
- +{
- + int retval = 0;
- +
- + retval = platform_device_register(&coldfire_spi);
- +
- + if (retval < 0) {
- + printk(KERN_ERR "SPI-m5445x: platform_device_register failed with code=%d\n", retval);
- + goto out;
- + }
- +
- + if (ARRAY_SIZE(spi_board_info))
- + retval = spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
- +
- +
- +out:
- + return retval;
- +}
- +
- +arch_initcall(spi_dev_init);
- --- /dev/null
- +++ b/drivers/spi/spi_coldfire.c
- @@ -0,0 +1,1552 @@
- +/****************************************************************************/
- +
- +/*
- + * spi_coldfire.c - Master QSPI/DSPI controller for the ColdFire processors
- + *
- + * (C) Copyright 2005, Intec Automation,
- + * Mike Lavender (mike@steroidmicros)
- + *
- + * (C) Copyright 2007, Freescale Inc,
- + * Yaroslav Vinogradov ([email protected])
- + *
- +
- + This program is free software; you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation; either version 2 of the License, or
- + (at your option) any later version.
- +
- + This program is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with this program; if not, write to the Free Software
- + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- +/* ------------------------------------------------------------------------- */
- +
- +
- +/****************************************************************************/
- +
- +/*
- + * Includes
- + */
- +
- +#include <linux/autoconf.h>
- +#include <linux/init.h>
- +#include <linux/module.h>
- +#include <linux/device.h>
- +#include <linux/interrupt.h>
- +#include <linux/platform_device.h>
- +#include <linux/spi/spi.h>
- +#include <linux/workqueue.h>
- +#include <linux/delay.h>
- +
- +#include <asm/delay.h>
- +#include <asm/mcfsim.h>
- +#include <asm/mcfqspi.h>
- +#include <asm/coldfire.h>
- +#include <asm/virtconvert.h>
- +
- +#if defined(CONFIG_M54455)
- + #define SPI_DSPI
- + #if defined(CONFIG_SPI_COLDFIRE_DSPI_EDMA)
- + #define SPI_DSPI_EDMA
- + #ifdef CONFIG_MMU
- + #define SPI_USE_MMU
- + #endif
- + #endif
- +#endif
- +
- +#ifdef SPI_DSPI
- +#include <asm/mcf5445x_dspi.h>
- +
- +
- +#endif
- +
- +#if defined(SPI_DSPI_EDMA)
- +
- +/* edma buffer size in transfer units (32bits) */
- +#define EDMA_BUFFER_SIZE (PAGE_SIZE/4)
- +#define EDMA_BUFSIZE_KMALLOC (EDMA_BUFFER_SIZE*4)
- +
- +#define DSPI_DMA_RX_TCD 12
- +#define DSPI_DMA_TX_TCD 13
- +
- +
- +#include <asm/coldfire_edma.h>
- +#include <asm/mcf5445x_edma.h>
- +#endif
- +
- +
- +MODULE_AUTHOR("Mike Lavender");
- +MODULE_DESCRIPTION("ColdFire QSPI Contoller");
- +MODULE_LICENSE("GPL");
- +
- +#define DRIVER_NAME "Coldfire QSPI/DSPI"
- +
- +/****************************************************************************/
- +
- +/*
- + * Local constants and macros
- + */
- +
- +#define QSPI_RAM_SIZE 0x10 /* 16 word table */
- +
- +#define QSPI_TRANSMIT_RAM 0x00
- +#define QSPI_RECEIVE_RAM 0x10
- +#define QSPI_COMMAND_RAM 0x20
- +
- +#define QSPI_COMMAND 0x7000 /* 15: X = Continuous CS
- + * 14: 1 = Get BITSE from QMR[BITS]
- + * 13: 1 = Get DT from QDLYR[DTL]
- + * 12: 1 = Get DSK from QDLYR[QCD]
- + * 8-11: XXXX = next 4 bytes for CS
- + * 0-7: 0000 0000 Reserved
- + */
- +
- +#define QIR_WCEF 0x0008 /* write collison */
- +#define QIR_ABRT 0x0004 /* abort */
- +#define QIR_SPIF 0x0001 /* finished */
- +
- +#define QIR_WCEFE 0x0800
- +#define QIR_ABRTE 0x0400
- +#define QIR_SPIFE 0x0100
- +
- +#define QIR_WCEFB 0x8000
- +#define QIR_ABRTB 0x4000
- +#define QIR_ABRTL 0x1000
- +
- +#define QMR_BITS 0x3C00
- +#define QMR_BITS_8 0x2000
- +
- +#define QCR_CONT 0x8000
- +
- +#define QDLYR_SPE 0x8000
- +
- +#define QWR_ENDQP_MASK 0x0F00
- +#define QWR_CSIV 0x1000 /* 1 = active low chip selects */
- +
- +
- +#define START_STATE ((void*)0)
- +#define RUNNING_STATE ((void*)1)
- +#define DONE_STATE ((void*)2)
- +#define ERROR_STATE ((void*)-1)
- +
- +#define QUEUE_RUNNING 0
- +#define QUEUE_STOPPED 1
- +
- +/****************************************************************************/
- +
- +/*
- + * Local Data Structures
- + */
- +
- +struct transfer_state {
- + u32 index;
- + u32 len;
- + void *tx;
- + void *tx_end;
- + void *rx;
- + void *rx_end;
- + char flags;
- +#define TRAN_STATE_RX_VOID 0x01
- +#define TRAN_STATE_TX_VOID 0x02
- +#define TRAN_STATE_WORD_ODD_NUM 0x04
- + u8 cs;
- + u16 void_write_data;
- + unsigned cs_change:1;
- +};
- +
- +typedef struct {
- + unsigned master:1;
- + unsigned dohie:1;
- + unsigned bits:4;
- + unsigned cpol:1;
- + unsigned cpha:1;
- + unsigned baud:8;
- +} QMR;
- +
- +typedef struct {
- + unsigned spe:1;
- + unsigned qcd:7;
- + unsigned dtl:8;
- +} QDLYR;
- +
- +typedef struct {
- + unsigned halt:1;
- + unsigned wren:1;
- + unsigned wrto:1;
- + unsigned csiv:1;
- + unsigned endqp:4;
- + unsigned cptqp:4;
- + unsigned newqp:4;
- +} QWR;
- +
- +
- +typedef struct {
- + unsigned master:1;
- + unsigned cont_scke:1;
- + unsigned dconf:2;
- + unsigned frz:1;
- + unsigned mtfe:1;
- + unsigned pcsse:1;
- + unsigned rooe:1;
- + unsigned pcsis:8;
- + unsigned reserved15:1;
- + unsigned mdis:1;
- + unsigned dis_tx:1;
- + unsigned dis_rxf:1;
- + unsigned clr_tx:1;
- + unsigned clr_rxf:1;
- + unsigned smpl_pt:2;
- + unsigned reserved71:7;
- + unsigned halt:1;
- +} DSPI_MCR;
- +
- +typedef struct {
- + unsigned dbr:1;
- + unsigned fmsz:4;
- + unsigned cpol:1;
- + unsigned cpha:1;
- + unsigned lsbfe:1;
- + unsigned pcssck:2;
- + unsigned pasc:2;
- + unsigned pdt:2;
- + unsigned pbr:2;
- + unsigned cssck:4;
- + unsigned asc:4;
- + unsigned dt:4;
- + unsigned br:4;
- +} DSPI_CTAR;
- +
- +struct chip_data {
- +#if defined(SPI_DSPI)
- + /* dspi data */
- + union {
- + u32 mcr_val;
- + DSPI_MCR mcr;
- + };
- + union {
- + u32 ctar_val;
- + DSPI_CTAR ctar;
- + };
- +#else
- + union {
- + u16 qmr_val;
- + QMR qmr;
- + };
- + union {
- + u16 qdlyr_val;
- + QDLYR qdlyr;
- + };
- + union {
- + u16 qwr_val;
- + QWR qwr;
- + };
- +#endif
- +
- + u16 void_write_data;
- +};
- +
- +
- +struct driver_data {
- + /* Driver model hookup */
- + struct platform_device *pdev;
- +
- + /* SPI framework hookup */
- + struct spi_master *master;
- +
- + /* Driver message queue */
- + struct workqueue_struct *workqueue;
- + struct work_struct pump_messages;
- + spinlock_t lock;
- + struct list_head queue;
- + int busy;
- + int run;
- +
- + /* Message Transfer pump */
- + struct tasklet_struct pump_transfers;
- +
- + /* Current message transfer state info */
- + struct spi_message* cur_msg;
- + struct spi_transfer* cur_transfer;
- + struct chip_data *cur_chip;
- + size_t len;
- + void *tx;
- + void *tx_end;
- + void *rx;
- + void *rx_end;
- + char flags;
- +#define TRAN_STATE_RX_VOID 0x01
- +#define TRAN_STATE_TX_VOID 0x02
- +#define TRAN_STATE_WORD_ODD_NUM 0x04
- + u8 cs;
- + u16 void_write_data;
- + unsigned cs_change:1;
- +
- + u32 trans_cnt;
- + u32 wce_cnt;
- + u32 abrt_cnt;
- +#if defined(SPI_DSPI)
- + u32 *mcr; /* DSPI MCR register */
- + u32 *ctar; /* DSPI CTAR register */
- + u32 *dspi_dtfr; /* DSPI DTFR register */
- + u32 *dspi_drfr; /* DSPI DRFR register */
- + u32 *dspi_rser; /* DSPI RSER register */
- + u32 *dspi_sr; /* DSPI status register */
- + u8 dspi_ctas; /* DSPI CTAS value*/
- +
- +#if defined(SPI_DSPI_EDMA)
- + void* edma_tx_buf;
- + void* edma_rx_buf;
- +#endif
- +
- +
- +#else
- + u16 *qmr; /* QSPI mode register */
- + u16 *qdlyr; /* QSPI delay register */
- + u16 *qwr; /* QSPI wrap register */
- + u16 *qir; /* QSPI interrupt register */
- + u16 *qar; /* QSPI address register */
- + u16 *qdr; /* QSPI data register */
- + u16 *qcr; /* QSPI command register */
- +#endif
- + u8 *par; /* Pin assignment register */
- + u8 *int_icr; /* Interrupt level and priority register */
- + u32 *int_mr; /* Interrupt mask register */
- + void (*cs_control)(u8 cs, u8 command);
- +};
- +
- +#define DSPI_CS(cs) ((1<<(cs))<<16)
- +
- +
- +/****************************************************************************/
- +
- +/*
- + * SPI local functions
- + */
- +
- +//#define SPI_COLDFIRE_DEBUG
- +
- +static void *next_transfer(struct driver_data *drv_data)
- +{
- + struct spi_message *msg = drv_data->cur_msg;
- + struct spi_transfer *trans = drv_data->cur_transfer;
- +
- + /* Move to next transfer */
- + if (trans->transfer_list.next != &msg->transfers) {
- + drv_data->cur_transfer =
- + list_entry(trans->transfer_list.next,
- + struct spi_transfer,
- + transfer_list);
- + return RUNNING_STATE;
- + } else
- + return DONE_STATE;
- +}
- +
- +
- +#define DSPI_BITS MCF_DSPI_DCTAR_FMSZ(15)
- +#define DSPI_BITS_16 MCF_DSPI_DCTAR_FMSZ(15)
- +#define DSPI_BITS_8 MCF_DSPI_DCTAR_FMSZ(7)
- +#define DSPI_FIFO_SIZE 16
- +
- +static inline int is_word_transfer(struct driver_data *drv_data)
- +{
- +#if defined(SPI_DSPI)
- + return ((*drv_data->ctar & DSPI_BITS_16) == DSPI_BITS_8) ? 0 : 1;
- +#else
- + return ((*drv_data->qmr & QMR_BITS) == QMR_BITS_8) ? 0 : 1;
- +#endif
- +}
- +
- +static void inline set_8bit_transfer_mode(struct driver_data *drv_data)
- +{
- +#if defined(SPI_DSPI)
- + *drv_data->ctar |= (*drv_data->ctar & ~DSPI_BITS) | DSPI_BITS_8;
- +#else
- + *drv_data->qmr |= (*drv_data->qmr & ~QMR_BITS) | QMR_BITS_8;
- +#endif
- +}
- +
- +static void inline set_16bit_transfer_mode(struct driver_data *drv_data)
- +{
- +#if defined(SPI_DSPI)
- + *drv_data->ctar |= (*drv_data->ctar & ~DSPI_BITS) | DSPI_BITS_16;
- +#else
- + *drv_data->qmr |= (*drv_data->qmr & ~QMR_BITS);
- +#endif
- +}
- +
- +static int write(struct driver_data *drv_data)
- +{
- + int tx_count = 0;
- +#ifndef SPI_DSPI
- + int cmd_count = 0;
- +#endif
- + int tx_word;
- +
- +#if defined(SPI_DSPI)
- +
- +#if defined(SPI_DSPI_EDMA)
- + u32* edma_wr;
- +#endif
- +
- + u16 d16;
- + u8 d8;
- + u32 dspi_pushr;
- + int first = 1;
- +#endif
- +
- + tx_word = is_word_transfer(drv_data);
- +
- + // If we are in word mode, but only have a single byte to transfer
- + // then switch to byte mode temporarily. Will switch back at the
- + // end of the transfer.
- + if (tx_word && ((drv_data->tx_end - drv_data->tx) == 1)) {
- + drv_data->flags |= TRAN_STATE_WORD_ODD_NUM;
- + set_8bit_transfer_mode(drv_data);
- + tx_word = 0;
- + }
- +
- +
- +#if defined(SPI_DSPI)
- +
- +#if defined(SPI_DSPI_EDMA)
- + edma_wr = (u32*)(drv_data->edma_tx_buf);
- +#endif
- +
- +
- +#if defined(SPI_DSPI_EDMA)
- + while ((drv_data->tx < drv_data->tx_end) && (tx_count < EDMA_BUFFER_SIZE)) {
- +#else
- + while ((drv_data->tx < drv_data->tx_end) && (tx_count < DSPI_FIFO_SIZE)) {
- +#endif
- + if (tx_word) {
- + if ((drv_data->tx_end - drv_data->tx) == 1)
- + break;
- + if (!(drv_data->flags & TRAN_STATE_TX_VOID)) {
- + d16 = *(u16 *)drv_data->tx;
- + } else {
- + d16 = drv_data->void_write_data;
- + }
- +
- + dspi_pushr = MCF_DSPI_DTFR_TXDATA(d16)
- + | DSPI_CS(drv_data->cs)
- + | MCF_DSPI_DTFR_CTAS(drv_data->dspi_ctas)
- + //| MCF_DSPI_DTFR_CONT
- + ;
- +
- + drv_data->tx += 2;
- +
- +#if defined(SPI_DSPI_EDMA)
- + if (drv_data->tx == drv_data->tx_end || tx_count==EDMA_BUFFER_SIZE-1) {
- +#else
- + if (drv_data->tx == drv_data->tx_end || tx_count==DSPI_FIFO_SIZE-1) {
- +#endif
- + // last transfer in queue
- + dspi_pushr |= MCF_DSPI_DTFR_EOQ;
- + if (drv_data->cs_change) {
- + dspi_pushr &= ~MCF_DSPI_DTFR_CONT;
- + }
- + }
- +
- + if (first) {
- + first = 0;
- + dspi_pushr |= MCF_DSPI_DTFR_CTCNT; // clear counter
- + }
- +#if defined(SPI_DSPI_EDMA)
- + *edma_wr = dspi_pushr;
- + edma_wr++;
- +#else
- + *drv_data->dspi_dtfr = dspi_pushr;
- + //MCF_DSPI_DTFR = dspi_pushr;
- +#endif
- +
- +
- + } else {
- + if (!(drv_data->flags & TRAN_STATE_TX_VOID)) {
- + d8 = *(u8 *)drv_data->tx;
- + } else {
- + d8 = *(u8 *)&drv_data->void_write_data;
- + }
- +
- + dspi_pushr = MCF_DSPI_DTFR_TXDATA(d8)
- + | DSPI_CS(drv_data->cs)
- + /* | MCF_DSPI_DTFR_PCS5 | */
- + | MCF_DSPI_DTFR_CTAS(drv_data->dspi_ctas)
- + | MCF_DSPI_DTFR_CONT;
- +
- + drv_data->tx++;
- +
- + if (drv_data->tx == drv_data->tx_end || tx_count==DSPI_FIFO_SIZE-1) {
- + // last transfer in queue
- + dspi_pushr |= MCF_DSPI_DTFR_EOQ;
- + if (drv_data->cs_change) {
- + dspi_pushr &= ~MCF_DSPI_DTFR_CONT;
- + }
- + }
- +
- + if (first) {
- + first = 0;
- + dspi_pushr |= MCF_DSPI_DTFR_CTCNT; // clear counter
- + }
- +
- +#if defined(SPI_DSPI_EDMA)
- + *edma_wr = dspi_pushr;
- + edma_wr++;
- +#else
- + *drv_data->dspi_dtfr = dspi_pushr;
- + //MCF_DSPI_DTFR = dspi_pushr;
- +#endif
- +
- + }
- + tx_count++;
- + }
- +
- +#if defined(SPI_DSPI_EDMA)
- +
- + if (tx_count>0) {
- +
- + // TODO: initiate eDMA transfer
- + set_edma_params(DSPI_DMA_TX_TCD,
- +#ifdef SPI_USE_MMU
- + virt_to_phys(drv_data->edma_tx_buf),
- +#else
- + drv_data->edma_tx_buf,
- +#endif
- + (u32)drv_data->dspi_dtfr,
- + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
- + 4, // soff
- + 4, // nbytes
- + 0, // slast
- + tx_count, // citer
- + tx_count, // biter
- + 0, // doff
- + 0, // dlastsga
- + 0, // major_int
- + 1 // disable_req
- + );
- +
- + set_edma_params(DSPI_DMA_RX_TCD,
- + (u32)drv_data->dspi_drfr,
- +#ifdef SPI_USE_MMU
- + virt_to_phys(drv_data->edma_rx_buf),
- +#else
- + drv_data->edma_rx_buf,
- +#endif
- + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
- + 0, // soff
- + 4, // nbytes
- + 0, // slast
- + tx_count, // citer
- + tx_count, // biter
- + 4, // doff
- + 0, // dlastsga
- + 0, // major_int
- + 1 // disable_req
- + );
- +
- +
- + start_edma_transfer(DSPI_DMA_TX_TCD); // transmit SPI data
- + start_edma_transfer(DSPI_DMA_RX_TCD); // receive SPI data
- + }
- +#endif
- +
- +#else
- +
- + *drv_data->qar = QSPI_TRANSMIT_RAM;
- + while ((drv_data->tx < drv_data->tx_end) && (tx_count < QSPI_RAM_SIZE)) {
- + if (tx_word) {
- + if ((drv_data->tx_end - drv_data->tx) == 1)
- + break;
- +
- + if (!(drv_data->flags & TRAN_STATE_TX_VOID))
- + *drv_data->qdr = *(u16 *)drv_data->tx;
- + else
- + *drv_data->qdr = drv_data->void_write_data;
- + drv_data->tx += 2;
- + } else {
- + if (!(drv_data->flags & TRAN_STATE_TX_VOID))
- + *drv_data->qdr = *(u8 *)drv_data->tx;
- + else
- + *drv_data->qdr = *(u8 *)&drv_data->void_write_data;
- + drv_data->tx++;
- + }
- + tx_count++;
- + }
- +
- +
- + *drv_data->qar = QSPI_COMMAND_RAM;
- + while (cmd_count < tx_count) {
- + u16 qcr = QSPI_COMMAND
- + | QCR_CONT
- + | (~((0x01 << drv_data->cs) << 8) & 0x0F00);
- +
- + if ( (cmd_count == tx_count - 1)
- + && (drv_data->tx == drv_data->tx_end)
- + && (drv_data->cs_change) ) {
- + qcr &= ~QCR_CONT;
- + }
- + *drv_data->qcr = qcr;
- + cmd_count++;
- + }
- +
- + *drv_data->qwr = (*drv_data->qwr & ~QWR_ENDQP_MASK) | ((cmd_count - 1) << 8);
- +
- + /* Fire it up! */
- + *drv_data->qdlyr |= QDLYR_SPE;
- +#endif
- +
- + return tx_count;
- +}
- +
- +
- +static int read(struct driver_data *drv_data)
- +{
- + int rx_count = 0;
- + int rx_word;
- +#if defined(SPI_DSPI_EDMA)
- + u32* rx_edma;
- +#endif
- + u16 d;
- + rx_word = is_word_transfer(drv_data);
- +
- +#if defined(SPI_DSPI)
- +
- +#if defined(SPI_DSPI_EDMA)
- + rx_edma = (u32*) drv_data->edma_tx_buf;
- + while ((drv_data->rx < drv_data->rx_end) && (rx_count < EDMA_BUFFER_SIZE)) {
- +#else
- + while ((drv_data->rx < drv_data->rx_end) && (rx_count < DSPI_FIFO_SIZE)) {
- +#endif
- + if (rx_word) {
- + if ((drv_data->rx_end - drv_data->rx) == 1)
- + break;
- +#if defined(SPI_DSPI_EDMA)
- + d = MCF_DSPI_DRFR_RXDATA(*rx_edma);
- + rx_edma++;
- +#else
- + d = MCF_DSPI_DRFR_RXDATA(*drv_data->dspi_drfr);
- +#endif
- +
- + if (!(drv_data->flags & TRAN_STATE_RX_VOID))
- + *(u16 *)drv_data->rx = d;
- + drv_data->rx += 2;
- + } else {
- +#if defined(SPI_DSPI_EDMA)
- + d = MCF_DSPI_DRFR_RXDATA(*rx_edma);
- + rx_edma++;
- +#else
- + d = MCF_DSPI_DRFR_RXDATA(*drv_data->dspi_drfr);
- +#endif
- + if (!(drv_data->flags & TRAN_STATE_RX_VOID))
- + *(u8 *)drv_data->rx = d;
- + drv_data->rx++;
- + }
- + rx_count++;
- + }
- +
- +
- +#else
- +
- + *drv_data->qar = QSPI_RECEIVE_RAM;
- + while ((drv_data->rx < drv_data->rx_end) && (rx_count < QSPI_RAM_SIZE)) {
- + if (rx_word) {
- + if ((drv_data->rx_end - drv_data->rx) == 1)
- + break;
- +
- + if (!(drv_data->flags & TRAN_STATE_RX_VOID))
- + *(u16 *)drv_data->rx = *drv_data->qdr;
- + drv_data->rx += 2;
- + } else {
- + if (!(drv_data->flags & TRAN_STATE_RX_VOID))
- + *(u8 *)drv_data->rx = *drv_data->qdr;
- + drv_data->rx++;
- + }
- + rx_count++;
- + }
- +#endif
- +
- + return rx_count;
- +}
- +
- +
- +static inline void qspi_setup_chip(struct driver_data *drv_data)
- +{
- + struct chip_data *chip = drv_data->cur_chip;
- +
- +#if defined(SPI_DSPI)
- +
- + *drv_data->mcr = chip->mcr_val;
- +
- + // TODO: remove later
- + chip->ctar_val = 0x78560118;
- +
- + *drv_data->ctar = chip->ctar_val;
- + *drv_data->dspi_rser = 0
- + | MCF_DSPI_DRSER_EOQFE
- +#if defined(SPI_DSPI_EDMA)
- + | MCF_DSPI_DRSER_TFFFE
- + | MCF_DSPI_DRSER_TFFFS
- +#endif
- + ;
- +
- +
- +#else
- + *drv_data->qmr = chip->qmr_val;
- + *drv_data->qdlyr = chip->qdlyr_val;
- + *drv_data->qwr = chip->qwr_val;
- +
- + /*
- + * Enable all the interrupts and clear all the flags
- + */
- + *drv_data->qir = (QIR_SPIFE | QIR_ABRTE | QIR_WCEFE)
- + | (QIR_WCEFB | QIR_ABRTB | QIR_ABRTL)
- + | (QIR_SPIF | QIR_ABRT | QIR_WCEF);
- +#endif
- +}
- +
- +#if defined(SPI_DSPI_EDMA)
- +static int edma_tx_handler(int channel, void* dev)
- +{
- + if (channel == DSPI_DMA_TX_TCD) {
- + stop_edma_transfer(DSPI_DMA_TX_TCD);
- + }
- + return IRQ_HANDLED;
- +}
- +
- +static int edma_rx_handler(int channel, void* dev)
- +{
- + if (channel == DSPI_DMA_RX_TCD) {
- + stop_edma_transfer(DSPI_DMA_RX_TCD);
- + }
- +
- + return IRQ_HANDLED;
- +}
- +#endif
- +
- +static irqreturn_t qspi_interrupt(int irq, void *dev_id)
- +{
- + struct driver_data *drv_data = (struct driver_data *)dev_id;
- + struct spi_message *msg = drv_data->cur_msg;
- +#if defined(SPI_DSPI)
- +#if !defined(SPI_DSPI_EDMA)
- + u32 irq_status = *drv_data->dspi_sr;
- +#endif
- +#else
- + u16 irq_status = *drv_data->qir;
- +#endif
- +
- + /* Clear all flags immediately */
- +#if defined(SPI_DSPI)
- + *drv_data->dspi_sr = MCF_DSPI_DSR_EOQF;
- +#else
- + *drv_data->qir |= (QIR_SPIF | QIR_ABRT | QIR_WCEF);
- +#endif
- +
- + if (!drv_data->cur_msg || !drv_data->cur_msg->state) {
- +#if !defined(SPI_DSPI_EDMA)
- + /* if eDMA is used it happens some time (at least once)*/
- + printk(KERN_ERR "coldfire-qspi: bad message or transfer "
- + "state in interrupt handler. IRQ status=%x\n", irq_status);
- +#endif
- + return IRQ_NONE;
- + }
- +
- +#if !defined(SPI_DSPI)
- + if (irq_status & QIR_SPIF) {
- +#endif
- + /*
- + * Read the data into the buffer and reload and start
- + * queue with new data if not finished. If finished
- + * then setup the next transfer
- + */
- + read(drv_data);
- +
- + if (drv_data->rx == drv_data->rx_end) {
- + /*
- + * Finished now - fall through and schedule next
- + * transfer tasklet
- + */
- + if (drv_data->flags & TRAN_STATE_WORD_ODD_NUM) {
- + //*drv_data->qmr &= ~QMR_BITS;
- + set_16bit_transfer_mode(drv_data);
- + }
- +
- + msg->state = next_transfer(drv_data);
- + msg->actual_length += drv_data->len;
- + } else {
- + /* not finished yet - keep going */
- + write(drv_data);
- + return IRQ_HANDLED;
- + }
- +#if !defined(SPI_DSPI)
- + } else {
- + if (irq_status & QIR_WCEF)
- + drv_data->wce_cnt++;
- +
- + if (irq_status & QIR_ABRT)
- + drv_data->abrt_cnt++;
- +
- + msg->state = ERROR_STATE;
- + }
- +#endif
- +
- + tasklet_schedule(&drv_data->pump_transfers);
- +
- + return IRQ_HANDLED;
- +}
- +
- +/* caller already set message->status; dma and pio irqs are blocked */
- +static void giveback(struct driver_data *drv_data)
- +{
- + struct spi_transfer* last_transfer;
- + unsigned long flags;
- + struct spi_message *msg;
- +
- + spin_lock_irqsave(&drv_data->lock, flags);
- + msg = drv_data->cur_msg;
- + drv_data->cur_msg = NULL;
- + drv_data->cur_transfer = NULL;
- + drv_data->cur_chip = NULL;
- + queue_work(drv_data->workqueue, &drv_data->pump_messages);
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- +
- + last_transfer = list_entry(msg->transfers.prev,
- + struct spi_transfer,
- + transfer_list);
- +
- + if (!last_transfer->cs_change)
- + drv_data->cs_control(drv_data->cs, QSPI_CS_DROP);
- +
- + msg->state = NULL;
- + if (msg->complete)
- + msg->complete(msg->context);
- +}
- +
- +
- +static void pump_transfers(unsigned long data)
- +{
- + struct driver_data *drv_data = (struct driver_data *)data;
- + struct spi_message *message = NULL;
- + struct spi_transfer *transfer = NULL;
- + struct spi_transfer *previous = NULL;
- + struct chip_data *chip = NULL;
- + unsigned long flags;
- +
- + /* Get current state information */
- + message = drv_data->cur_msg;
- + transfer = drv_data->cur_transfer;
- + chip = drv_data->cur_chip;
- +
- + /* Handle for abort */
- + if (message->state == ERROR_STATE) {
- + message->status = -EIO;
- + giveback(drv_data);
- + return;
- + }
- +
- + /* Handle end of message */
- + if (message->state == DONE_STATE) {
- + message->status = 0;
- + giveback(drv_data);
- + return;
- + }
- +
- + if (message->state == START_STATE) {
- + qspi_setup_chip(drv_data);
- +
- + if (drv_data->cs_control) {
- + //printk( "m s\n" );
- + drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
- + }
- + }
- +
- + /* Delay if requested at end of transfer*/
- + if (message->state == RUNNING_STATE) {
- + previous = list_entry(transfer->transfer_list.prev,
- + struct spi_transfer,
- + transfer_list);
- +
- + if (drv_data->cs_control && transfer->cs_change)
- + drv_data->cs_control(message->spi->chip_select, QSPI_CS_DROP);
- +
- + if (previous->delay_usecs)
- + udelay(previous->delay_usecs);
- +
- + if (drv_data->cs_control && transfer->cs_change)
- + drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
- + }
- +
- + drv_data->flags = 0;
- + drv_data->tx = (void *)transfer->tx_buf;
- + drv_data->tx_end = drv_data->tx + transfer->len;
- + drv_data->rx = transfer->rx_buf;
- + drv_data->rx_end = drv_data->rx + transfer->len;
- + drv_data->len = transfer->len;
- + if (!drv_data->rx)
- + drv_data->flags |= TRAN_STATE_RX_VOID;
- + if (!drv_data->tx)
- + drv_data->flags |= TRAN_STATE_TX_VOID;
- + drv_data->cs = message->spi->chip_select;
- + drv_data->cs_change = transfer->cs_change;
- + drv_data->void_write_data = chip->void_write_data;
- +
- + message->state = RUNNING_STATE;
- +
- + /* Go baby, go */
- + local_irq_save(flags);
- + write(drv_data);
- + local_irq_restore(flags);
- +}
- +
- +
- +static void pump_messages(struct work_struct * work)
- +{
- + struct driver_data *drv_data;
- + unsigned long flags;
- +
- + drv_data = container_of(work, struct driver_data, pump_messages);
- +
- + /* Lock queue and check for queue work */
- + spin_lock_irqsave(&drv_data->lock, flags);
- + if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
- + drv_data->busy = 0;
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- + return;
- + }
- +
- + /* Make sure we are not already running a message */
- + if (drv_data->cur_msg) {
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- + return;
- + }
- +
- + /* Extract head of queue */
- + drv_data->cur_msg = list_entry(drv_data->queue.next,
- + struct spi_message, queue);
- + list_del_init(&drv_data->cur_msg->queue);
- +
- + /* Initial message state*/
- + drv_data->cur_msg->state = START_STATE;
- + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
- + struct spi_transfer,
- + transfer_list);
- +
- + /* Setup the SPI Registers using the per chip configuration */
- + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
- +
- + /* Mark as busy and launch transfers */
- + tasklet_schedule(&drv_data->pump_transfers);
- +
- + drv_data->busy = 1;
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- +}
- +
- +/****************************************************************************/
- +
- +/*
- + * SPI master implementation
- + */
- +
- +static int transfer(struct spi_device *spi, struct spi_message *msg)
- +{
- + struct driver_data *drv_data = spi_master_get_devdata(spi->master);
- + unsigned long flags;
- +
- + spin_lock_irqsave(&drv_data->lock, flags);
- +
- + if (drv_data->run == QUEUE_STOPPED) {
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- + return -ESHUTDOWN;
- + }
- +
- + msg->actual_length = 0;
- + msg->status = -EINPROGRESS;
- + msg->state = START_STATE;
- +
- + list_add_tail(&msg->queue, &drv_data->queue);
- +
- + if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
- + queue_work(drv_data->workqueue, &drv_data->pump_messages);
- +
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- +
- + return 0;
- +}
- +
- +
- +static int setup(struct spi_device *spi)
- +{
- + struct coldfire_spi_chip *chip_info;
- + struct chip_data *chip;
- +#ifndef SPI_DSPI
- + u32 baud_divisor = 255;
- +#endif
- +
- + chip_info = (struct coldfire_spi_chip *)spi->controller_data;
- +
- + /* Only alloc on first setup */
- + chip = spi_get_ctldata(spi);
- + if (chip == NULL) {
- + chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL);
- + if (!chip)
- + return -ENOMEM;
- + spi->mode = chip_info->mode;
- + spi->bits_per_word = chip_info->bits_per_word;
- + }
- +
- +#if defined(SPI_DSPI)
- + chip->mcr.master = 1;
- + chip->mcr.cont_scke = 0;
- + chip->mcr.dconf = 0;
- + chip->mcr.frz = 0;
- + chip->mcr.mtfe = 1;
- + chip->mcr.pcsse = 0;
- + chip->mcr.rooe = 0;
- + chip->mcr.pcsis = 0xFF;
- + chip->mcr.reserved15 = 0;
- + chip->mcr.mdis = 0;
- + chip->mcr.dis_tx = 0;
- + chip->mcr.dis_rxf = 0;
- + chip->mcr.clr_tx = 1;
- + chip->mcr.clr_rxf = 1;
- + chip->mcr.smpl_pt = 0;
- + chip->mcr.reserved71 = 0;
- + chip->mcr.halt = 0;
- +
- + if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
- + chip->ctar.fmsz = spi->bits_per_word-1;
- + } else {
- + printk(KERN_ERR "coldfire-qspi: invalid wordsize\n");
- + kfree(chip);
- + return -ENODEV;
- + }
- +
- + if (spi->mode & SPI_CPHA)
- + chip->ctar.cpha = 1;
- + else
- + chip->ctar.cpha = 0;
- +
- + if (spi->mode & SPI_CPOL)
- + chip->ctar.cpol = 1;
- + else
- + chip->ctar.cpol = 0;
- +
- + if (spi->mode & SPI_LSB_FIRST)
- + chip->ctar.lsbfe = 1;
- + else
- + chip->ctar.lsbfe = 0;
- +
- + /* This values are default for audio device */
- + chip->ctar.dbr = 0;
- + chip->ctar.pbr = 2;
- + chip->ctar.br = 8;
- +
- + /* This values are default for audio device */
- + chip->ctar.pcssck = 1;
- + chip->ctar.pasc = 1;
- + chip->ctar.pdt = 1;
- + chip->ctar.cssck = 0;
- + chip->ctar.asc = 1;
- + chip->ctar.dt = 1;
- +
- + chip->void_write_data = chip_info->void_write_data;
- +
- +#else
- +
- + chip->qwr.csiv = 1; // Chip selects are active low
- + chip->qmr.master = 1; // Must set to master mode
- + chip->qmr.dohie = 1; // Data output high impediance enabled
- + chip->void_write_data = chip_info->void_write_data;
- +
- + chip->qdlyr.qcd = chip_info->del_cs_to_clk;
- + chip->qdlyr.dtl = chip_info->del_after_trans;
- +
- + if (spi->max_speed_hz != 0)
- + baud_divisor = (MCF_CLK/(2*spi->max_speed_hz));
- +
- + if (baud_divisor < 2)
- + baud_divisor = 2;
- +
- + if (baud_divisor > 255)
- + baud_divisor = 255;
- +
- + chip->qmr.baud = baud_divisor;
- +
- + //printk( "QSPI: spi->max_speed_hz %d\n", spi->max_speed_hz );
- + //printk( "QSPI: Baud set to %d\n", chip->qmr.baud );
- +
- + if (spi->mode & SPI_CPHA)
- + chip->qmr.cpha = 1;
- +
- + if (spi->mode & SPI_CPOL)
- + chip->qmr.cpol = 1;
- +
- + if (spi->bits_per_word == 16) {
- + chip->qmr.bits = 0;
- + } else if ((spi->bits_per_word >= 8) && (spi->bits_per_word <= 15)) {
- + chip->qmr.bits = spi->bits_per_word;
- + } else {
- + printk(KERN_ERR "coldfire-qspi: invalid wordsize\n");
- + kfree(chip);
- + return -ENODEV;
- + }
- +
- +#endif
- +
- + spi_set_ctldata(spi, chip);
- +
- + return 0;
- +}
- +
- +static int init_queue(struct driver_data *drv_data)
- +{
- + INIT_LIST_HEAD(&drv_data->queue);
- + spin_lock_init(&drv_data->lock);
- +
- + drv_data->run = QUEUE_STOPPED;
- + drv_data->busy = 0;
- +
- + tasklet_init(&drv_data->pump_transfers,
- + pump_transfers, (unsigned long)drv_data);
- +
- + INIT_WORK(&drv_data->pump_messages, pump_messages/*, drv_data*/);
- +
- + drv_data->workqueue = create_singlethread_workqueue(
- + drv_data->master->cdev.dev->bus_id);
- + if (drv_data->workqueue == NULL)
- + return -EBUSY;
- +
- + return 0;
- +}
- +
- +static int start_queue(struct driver_data *drv_data)
- +{
- + unsigned long flags;
- +
- + spin_lock_irqsave(&drv_data->lock, flags);
- +
- + if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- + return -EBUSY;
- + }
- +
- + drv_data->run = QUEUE_RUNNING;
- + drv_data->cur_msg = NULL;
- + drv_data->cur_transfer = NULL;
- + drv_data->cur_chip = NULL;
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- +
- + queue_work(drv_data->workqueue, &drv_data->pump_messages);
- +
- + return 0;
- +}
- +
- +static int stop_queue(struct driver_data *drv_data)
- +{
- + unsigned long flags;
- + unsigned limit = 500;
- + int status = 0;
- +
- + spin_lock_irqsave(&drv_data->lock, flags);
- +
- + /* This is a bit lame, but is optimized for the common execution path.
- + * A wait_queue on the drv_data->busy could be used, but then the common
- + * execution path (pump_messages) would be required to call wake_up or
- + * friends on every SPI message. Do this instead */
- + drv_data->run = QUEUE_STOPPED;
- + while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- + msleep(10);
- + spin_lock_irqsave(&drv_data->lock, flags);
- + }
- +
- + if (!list_empty(&drv_data->queue) || drv_data->busy)
- + status = -EBUSY;
- +
- + spin_unlock_irqrestore(&drv_data->lock, flags);
- +
- + return status;
- +}
- +
- +static int destroy_queue(struct driver_data *drv_data)
- +{
- + int status;
- +
- + status = stop_queue(drv_data);
- + if (status != 0)
- + return status;
- +
- + destroy_workqueue(drv_data->workqueue);
- +
- + return 0;
- +}
- +
- +
- +static void cleanup(const struct spi_device *spi)
- +{
- + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi);
- +
- + dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n",
- + spi->master->bus_num, spi->chip_select);
- +
- + kfree(chip);
- +}
- +
- +
- +/****************************************************************************/
- +
- +/*
- + * Generic Device driver routines and interface implementation
- + */
- +
- +static int coldfire_spi_probe(struct platform_device *pdev)
- +{
- + struct device *dev = &pdev->dev;
- + struct coldfire_spi_master *platform_info;
- + struct spi_master *master;
- + struct driver_data *drv_data = 0;
- + struct resource *memory_resource;
- + int irq;
- + int status = 0;
- + int i;
- +
- +#if defined(SPI_DSPI_EDMA)
- + init_edma();
- +#endif
- +
- + platform_info = (struct coldfire_spi_master *)pdev->dev.platform_data;
- +
- + master = spi_alloc_master(dev, sizeof(struct driver_data));
- + if (!master)
- + return -ENOMEM;
- +
- + drv_data = class_get_devdata(&master->cdev);
- + drv_data->master = master;
- +
- + INIT_LIST_HEAD(&drv_data->queue);
- + spin_lock_init(&drv_data->lock);
- +
- + master->bus_num = platform_info->bus_num;
- + master->num_chipselect = platform_info->num_chipselect;
- + master->cleanup = cleanup;
- + master->setup = setup;
- + master->transfer = transfer;
- +
- + drv_data->cs_control = platform_info->cs_control;
- + if (drv_data->cs_control)
- + for(i = 0; i < master->num_chipselect; i++)
- + drv_data->cs_control(i, QSPI_CS_INIT | QSPI_CS_DROP);
- +
- + /* Setup register addresses */
- + memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-module");
- + if (!memory_resource) {
- + dev_dbg(dev, "can not find platform module memory\n");
- + goto out_error_master_alloc;
- + }
- +
- +#if defined(SPI_DSPI_EDMA)
- + drv_data->edma_tx_buf = kmalloc(EDMA_BUFSIZE_KMALLOC, GFP_DMA);
- + if (!drv_data->edma_tx_buf) {
- + dev_dbg(dev, "cannot allocate eDMA TX memory\n");
- + goto out_error_master_alloc;
- + }
- + drv_data->edma_rx_buf = kmalloc(EDMA_BUFSIZE_KMALLOC, GFP_DMA);
- + if (!drv_data->edma_rx_buf) {
- + kfree(drv_data->edma_tx_buf);
- + dev_dbg(dev, "cannot allocate eDMA RX memory\n");
- + goto out_error_master_alloc;
- + }
- +#endif
- +
- +#if defined(SPI_DSPI)
- +
- + drv_data->mcr = (void *)(memory_resource->start + 0x00000000);
- + drv_data->ctar = (void *)(memory_resource->start + 0x0000000C);
- + drv_data->dspi_sr = (void *)(memory_resource->start + 0x0000002C);
- + drv_data->dspi_rser = (void *)(memory_resource->start + 0x00000030);
- + drv_data->dspi_dtfr = (void *)(memory_resource->start + 0x00000034);
- + drv_data->dspi_drfr = (void *)(memory_resource->start + 0x00000038);
- +
- +#else
- +
- + drv_data->qmr = (void *)(memory_resource->start + 0x00000000);
- + drv_data->qdlyr = (void *)(memory_resource->start + 0x00000004);
- + drv_data->qwr = (void *)(memory_resource->start + 0x00000008);
- + drv_data->qir = (void *)(memory_resource->start + 0x0000000c);
- + drv_data->qar = (void *)(memory_resource->start + 0x00000010);
- + drv_data->qdr = (void *)(memory_resource->start + 0x00000014);
- + drv_data->qcr = (void *)(memory_resource->start + 0x00000014);
- +
- +#endif
- +
- + /* Setup register addresses */
- + memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-par");
- + if (!memory_resource) {
- + dev_dbg(dev, "can not find platform par memory\n");
- + goto out_error_master_alloc;
- + }
- +
- + drv_data->par = (void *)memory_resource->start;
- +
- + /* Setup register addresses */
- + memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-int-level");
- + if (!memory_resource) {
- + dev_dbg(dev, "can not find platform par memory\n");
- + goto out_error_master_alloc;
- + }
- +
- + drv_data->int_icr = (void *)memory_resource->start;
- +
- + /* Setup register addresses */
- + memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-int-mask");
- + if (!memory_resource) {
- + dev_dbg(dev, "can not find platform par memory\n");
- + goto out_error_master_alloc;
- + }
- +
- + drv_data->int_mr = (void *)memory_resource->start;
- +
- + irq = platform_info->irq_vector;
- +
- + status = request_irq(platform_info->irq_vector, qspi_interrupt, SA_INTERRUPT, dev->bus_id, drv_data);
- + if (status < 0) {
- + dev_err(&pdev->dev, "unable to attach ColdFire QSPI interrupt\n");
- + goto out_error_master_alloc;
- + }
- +
- + /* Now that we have all the addresses etc. Let's set it up */
- + // TODO:
- + //*drv_data->par = platform_info->par_val;
- +
- + MCF_GPIO_PAR_DSPI = 0
- + | MCF_GPIO_PAR_DSPI_PCS5_PCS5
- + | MCF_GPIO_PAR_DSPI_PCS2_PCS2
- + | MCF_GPIO_PAR_DSPI_PCS1_PCS1
- + | MCF_GPIO_PAR_DSPI_PCS0_PCS0
- + | MCF_GPIO_PAR_DSPI_SIN_SIN
- + | MCF_GPIO_PAR_DSPI_SOUT_SOUT
- + | MCF_GPIO_PAR_DSPI_SCK_SCK;
- +
- + *drv_data->int_icr = platform_info->irq_lp;
- + *drv_data->int_mr &= ~platform_info->irq_mask;
- +
- +#ifdef SPI_DSPI
- + drv_data->dspi_ctas = 0; // TODO: change later
- +#endif
- +
- + /* Initial and start queue */
- + status = init_queue(drv_data);
- + if (status != 0) {
- + dev_err(&pdev->dev, "problem initializing queue\n");
- + goto out_error_irq_alloc;
- + }
- + status = start_queue(drv_data);
- + if (status != 0) {
- + dev_err(&pdev->dev, "problem starting queue\n");
- + goto out_error_irq_alloc;
- + }
- +
- + /* Register with the SPI framework */
- + platform_set_drvdata(pdev, drv_data);
- + status = spi_register_master(master);
- + if (status != 0) {
- + dev_err(&pdev->dev, "problem registering spi master\n");
- + status = -EINVAL;
- + goto out_error_queue_alloc;
- + }
- +
- +#if defined(SPI_DSPI_EDMA)
- + if (request_edma_channel(DSPI_DMA_TX_TCD,
- + edma_tx_handler,
- + NULL,
- + pdev,
- + NULL, /* spinlock */
- + DRIVER_NAME
- + )!=0)
- + {
- + dev_err(&pdev->dev, "problem requesting edma transmit channel\n");
- + status = -EINVAL;
- + goto out_error_queue_alloc;
- + }
- +
- + if (request_edma_channel(DSPI_DMA_RX_TCD,
- + edma_rx_handler,
- + NULL,
- + pdev,
- + NULL, /* spinlock */
- + DRIVER_NAME
- + )!=0)
- + {
- + dev_err(&pdev->dev, "problem requesting edma receive channel\n");
- + status = -EINVAL;
- + goto out_edma_transmit;
- + }
- +#endif
- +
- + printk( "SPI: Coldfire master initialized\n" );
- + //dev_info(&pdev->dev, "driver initialized\n");
- + return status;
- +
- +#if defined(SPI_DSPI_EDMA)
- +out_edma_transmit:
- + free_edma_channel(DSPI_DMA_TX_TCD, pdev);
- +#endif
- +
- +out_error_queue_alloc:
- + destroy_queue(drv_data);
- +
- +out_error_irq_alloc:
- + free_irq(irq, drv_data);
- +
- +out_error_master_alloc:
- + spi_master_put(master);
- + return status;
- +
- +}
- +
- +static int coldfire_spi_remove(struct platform_device *pdev)
- +{
- + struct driver_data *drv_data = platform_get_drvdata(pdev);
- + int irq;
- + int status = 0;
- +
- + if (!drv_data)
- + return 0;
- +
- +#if defined(SPI_DSPI_EDMA)
- + free_edma_channel(DSPI_DMA_TX_TCD, pdev);
- + free_edma_channel(DSPI_DMA_RX_TCD, pdev);
- +#endif
- +
- + /* Remove the queue */
- + status = destroy_queue(drv_data);
- + if (status != 0)
- + return status;
- +
- + /* Disable the SSP at the peripheral and SOC level */
- + /*write_SSCR0(0, drv_data->ioaddr);
- + pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
- +
- + /* Release DMA */
- + /*if (drv_data->master_info->enable_dma) {
- + if (drv_data->ioaddr == SSP1_VIRT) {
- + DRCMRRXSSDR = 0;
- + DRCMRTXSSDR = 0;
- + } else if (drv_data->ioaddr == SSP2_VIRT) {
- + DRCMRRXSS2DR = 0;
- + DRCMRTXSS2DR = 0;
- + } else if (drv_data->ioaddr == SSP3_VIRT) {
- + DRCMRRXSS3DR = 0;
- + DRCMRTXSS3DR = 0;
- + }
- + pxa_free_dma(drv_data->tx_channel);
- + pxa_free_dma(drv_data->rx_channel);
- + }*/
- +
- + /* Release IRQ */
- + irq = platform_get_irq(pdev, 0);
- + if (irq >= 0)
- + free_irq(irq, drv_data);
- +
- + /* Disconnect from the SPI framework */
- + spi_unregister_master(drv_data->master);
- +
- + /* Prevent double remove */
- + platform_set_drvdata(pdev, NULL);
- +
- + return 0;
- +}
- +
- +static void coldfire_spi_shutdown(struct platform_device *pdev)
- +{
- + int status = 0;
- +
- + if ((status = coldfire_spi_remove(pdev)) != 0)
- + dev_err(&pdev->dev, "shutdown failed with %d\n", status);
- +}
- +
- +
- +#ifdef CONFIG_PM
- +static int suspend_devices(struct device *dev, void *pm_message)
- +{
- + pm_message_t *state = pm_message;
- +
- + if (dev->power.power_state.event != state->event) {
- + dev_warn(dev, "pm state does not match request\n");
- + return -1;
- + }
- +
- + return 0;
- +}
- +
- +static int coldfire_spi_suspend(struct platform_device *pdev, pm_message_t state)
- +{
- + struct driver_data *drv_data = platform_get_drvdata(pdev);
- + int status = 0;
- +
- + /* Check all childern for current power state */
- + if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) {
- + dev_warn(&pdev->dev, "suspend aborted\n");
- + return -1;
- + }
- +
- + status = stop_queue(drv_data);
- + if (status != 0)
- + return status;
- + /*write_SSCR0(0, drv_data->ioaddr);
- + pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
- +
- + return 0;
- +}
- +
- +static int coldfire_spi_resume(struct platform_device *pdev)
- +{
- + struct driver_data *drv_data = platform_get_drvdata(pdev);
- + int status = 0;
- +
- + /* Enable the SSP clock */
- + /*pxa_set_cken(drv_data->master_info->clock_enable, 1);*/
- +
- + /* Start the queue running */
- + status = start_queue(drv_data);
- + if (status != 0) {
- + dev_err(&pdev->dev, "problem starting queue (%d)\n", status);
- + return status;
- + }
- +
- + return 0;
- +}
- +#else
- +#define coldfire_spi_suspend NULL
- +#define coldfire_spi_resume NULL
- +#endif /* CONFIG_PM */
- +
- +static struct platform_driver driver = {
- + .driver = {
- + .name = "spi_coldfire",
- + .bus = &platform_bus_type,
- + .owner = THIS_MODULE,
- + },
- + .probe = coldfire_spi_probe,
- + .remove = __devexit_p(coldfire_spi_remove),
- + .shutdown = coldfire_spi_shutdown,
- + .suspend = coldfire_spi_suspend,
- + .resume = coldfire_spi_resume,
- +};
- +
- +static int __init coldfire_spi_init(void)
- +{
- + platform_driver_register(&driver);
- +
- + return 0;
- +}
- +module_init(coldfire_spi_init);
- +
- +static void __exit coldfire_spi_exit(void)
- +{
- + platform_driver_unregister(&driver);
- +}
- +module_exit(coldfire_spi_exit);
- --- /dev/null
- +++ b/drivers/spi/ssi_audio.c
- @@ -0,0 +1,906 @@
- +/*
- + * MCF5445x audio driver.
- + *
- + * Yaroslav Vinogradov [email protected]
- + * Copyright Freescale Semiconductor, Inc. 2006
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation; either version 2 of the License, or
- + * (at your option) any later version.
- + */
- +
- +#include <linux/device.h>
- +#include <linux/init.h>
- +#include <linux/delay.h>
- +#include <linux/spi/spi.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <asm/mcfsim.h>
- +#include <linux/interrupt.h>
- +#include <linux/soundcard.h>
- +#include <asm/uaccess.h>
- +#include <asm/virtconvert.h>
- +
- +#include <asm/coldfire.h>
- +#include <asm/coldfire_edma.h>
- +#include <asm/mcf5445x_ssi.h>
- +#include <asm/mcf5445x_ccm.h>
- +#include <asm/mcf5445x_gpio.h>
- +
- +#define SOUND_DEVICE_NAME "sound"
- +#define DRIVER_NAME "ssi_audio"
- +
- +
- +/* #define AUDIO_DEBUG */
- +
- +#ifdef CONFIG_MMU
- +#define USE_MMU
- +#endif
- +
- +#define MAX_SPEED_HZ 12000000
- +
- +#define M5445x_AUDIO_IRQ_SOURCE 49
- +#define M5445x_AUDIO_IRQ_VECTOR (128+M5445x_AUDIO_IRQ_SOURCE)
- +#define M5445x_AUDIO_IRQ_LEVEL 5
- +
- +/* TLV320DAC23 audio chip registers */
- +
- +#define CODEC_LEFT_IN_REG (0x00)
- +#define CODEC_RIGHT_IN_REG (0x01)
- +#define CODEC_LEFT_HP_VOL_REG (0x02)
- +#define CODEC_RIGHT_HP_VOL_REG (0x03)
- +#define CODEC_ANALOG_APATH_REG (0x04)
- +#define CODEC_DIGITAL_APATH_REG (0x05)
- +#define CODEC_POWER_DOWN_REG (0x06)
- +#define CODEC_DIGITAL_IF_FMT_REG (0x07)
- +#define CODEC_SAMPLE_RATE_REG (0x08)
- +#define CODEC_DIGITAL_IF_ACT_REG (0x09)
- +#define CODEC_RESET_REG (0x0f)
- +
- +#define CODEC_SAMPLE_8KHZ (0x0C)
- +#define CODEC_SAMPLE_16KHZ (0x58)
- +#define CODEC_SAMPLE_22KHZ (0x62)
- +#define CODEC_SAMPLE_32KHZ (0x18)
- +#define CODEC_SAMPLE_44KHZ (0x22)
- +#define CODEC_SAMPLE_48KHZ (0x00)
- +
- +/* Audio buffer data size */
- +#define BUFSIZE (64*1024)
- +/* DMA transfer size */
- +#define DMASIZE (16*1024)
- +
- +/* transmit eDMA channel for SSI channel 0 */
- +#define DMA_TCD 10
- +/* transmit eDMA channel for SSI channel 1 */
- +#define DMA_TCD2 11
- +
- +struct ssi_audio {
- + struct spi_device *spi;
- + u32 speed;
- + u32 stereo;
- + u32 bits;
- + u32 format;
- + u8 isopen;
- + u8 dmaing;
- + u8 ssi_enabled;
- + u8 channel;
- + spinlock_t lock;
- + u8* audio_buf;
- +};
- +
- +static struct ssi_audio* audio_device = NULL;
- +volatile u32 audio_start;
- +volatile u32 audio_count;
- +volatile u32 audio_append;
- +volatile u32 audio_appstart;
- +volatile u32 audio_txbusy;
- +
- +struct ssi_audio_format {
- + unsigned int format;
- + unsigned int bits;
- +} ssi_audio_formattable[] = {
- + { AFMT_MU_LAW, 8 },
- + { AFMT_A_LAW, 8 },
- + { AFMT_IMA_ADPCM, 8 },
- + { AFMT_U8, 8 },
- + { AFMT_S16_LE, 16 },
- + { AFMT_S16_BE, 16 },
- + { AFMT_S8, 8 },
- + { AFMT_U16_LE, 16 },
- + { AFMT_U16_BE, 16 },
- +};
- +
- +#define FORMATSIZE (sizeof(ssi_audio_formattable) / sizeof(struct ssi_audio_format))
- +
- +static void ssi_audio_setsamplesize(int val)
- +{
- + int i;
- +
- + if (audio_device == NULL) return;
- +
- + for (i = 0; (i < FORMATSIZE); i++) {
- + if (ssi_audio_formattable[i].format == val) {
- + audio_device->format = ssi_audio_formattable[i].format;
- + audio_device->bits = ssi_audio_formattable[i].bits;
- + break;
- + }
- + }
- +
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_setsamplesize %d %d\n", audio_device->format, audio_device->bits);
- +#endif
- +}
- +
- +static void ssi_audio_txdrain(void)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_txdrain()\n");
- +#endif
- +
- + if (audio_device == NULL) return;
- +
- + while (!signal_pending(current)) {
- + if (audio_txbusy == 0)
- + break;
- + current->state = TASK_INTERRUPTIBLE;
- + schedule_timeout(1);
- + }
- +}
- +
- +#ifdef CONFIG_SSIAUDIO_USE_EDMA
- +/*
- + * Configure and start DMA engine.
- + */
- +void __inline__ ssi_audio_dmarun(void)
- +{
- + set_edma_params(DMA_TCD,
- +#ifdef USE_MMU
- + virt_to_phys(&(audio_device->audio_buf[audio_start])),
- +#else
- + (u32)&(audio_device->audio_buf[audio_start]),
- +#endif
- + (u32)&MCF_SSI_TX0,
- + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
- + 8,
- + 4,
- + 0,
- + audio_count/8,
- + audio_count/8,
- + 0,
- + 0,
- + 0, // major_int
- + 0 // disable_req
- + );
- +
- + set_edma_params(DMA_TCD2,
- +#ifdef USE_MMU
- + virt_to_phys(&(audio_device->audio_buf[audio_start+4])),
- +#else
- + (u32)&(audio_device->audio_buf[audio_start+4]),
- +#endif
- + (u32)&MCF_SSI_TX1,
- + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
- + 8,
- + 4,
- + 0,
- + audio_count/8,
- + audio_count/8,
- + 0,
- + 0,
- + 1, // major_int
- + 0 // disable_req
- + );
- +
- + audio_device->dmaing = 1;
- + audio_txbusy = 1;
- +
- + start_edma_transfer(DMA_TCD);
- + start_edma_transfer(DMA_TCD2);
- +#if 0
- + MCF_EDMA_ERQ |= (1<<DMA_TCD) | (1<<DMA_TCD2);
- + MCF_EDMA_SSRT = DMA_TCD;
- + MCF_EDMA_SSRT = DMA_TCD2;
- +#endif
- +
- +}
- +
- +/*
- + * Start DMA'ing a new buffer of data if any available.
- + */
- +static void ssi_audio_dmabuf(void)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_dmabuf(): append=%x start=%x\n", audio_append, audio_appstart);
- +#endif
- +
- + /* If already running then nothing to do... */
- + if (audio_device->dmaing)
- + return;
- +
- + /* Set DMA buffer size */
- + audio_count = (audio_append >= audio_appstart) ?
- + (audio_append - audio_appstart) :
- + (BUFSIZE - audio_appstart);
- + if (audio_count > DMASIZE)
- + audio_count = DMASIZE;
- +
- + /* Adjust pointers and counters accordingly */
- + audio_appstart += audio_count;
- + if (audio_appstart >= BUFSIZE)
- + audio_appstart = 0;
- +
- + if (audio_count > 0)
- + ssi_audio_dmarun();
- + else {
- + audio_txbusy = 0;
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":DMA buffer is empty!\n");
- +#endif
- + }
- +}
- +
- +void __inline__ stop_dma(void) {
- + stop_edma_transfer(DMA_TCD);
- + stop_edma_transfer(DMA_TCD2);
- +}
- +
- +static int ssi_audio_dma_handler_empty(int channel, void *dev_id)
- +{
- + return IRQ_HANDLED;
- +}
- +
- +static int ssi_audio_dma_handler(int channel, void *dev_id)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_dma_handler(channel=%d)\n", channel);
- +#endif
- +
- + /* Clear DMA interrupt */
- + stop_dma();
- +
- + audio_device->dmaing = 0;
- +
- + /* Update data pointers and counts */
- + audio_start += audio_count;
- + if (audio_start >= BUFSIZE)
- + audio_start = 0;
- + audio_count = 0;
- +
- + /* Start new DMA buffer if we can */
- + ssi_audio_dmabuf();
- +
- + return IRQ_HANDLED;
- +}
- +
- +static void init_dma(void)
- +{
- + /* SSI DMA Signals mapped to DMA request */
- + MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_TIMDMA;
- + init_edma();
- +}
- +
- +#endif /* CONFIG_SSIAUDIO_USE_EDMA */
- +
- +
- +/* Write CODEC register using SPI
- + * address - CODEC register address
- + * data - data to be written into register
- + */
- +static int codec_write(u8 addr, u16 data)
- +{
- + u16 spi_word;
- +
- + if (audio_device==NULL || audio_device->spi==NULL)
- + return -ENODEV;
- +
- + spi_word = ((addr & 0x7F)<<9)|(data & 0x1FF);
- + return spi_write(audio_device->spi, (const u8*)&spi_word, sizeof(spi_word));
- +}
- +
- +static inline void enable_ssi(void)
- +{
- + if (audio_device==NULL || audio_device->ssi_enabled) return;
- + audio_device->ssi_enabled = 1;
- + MCF_SSI_CR |= MCF_SSI_CR_SSI_EN; /* enable SSI module */
- + MCF_SSI_CR |= MCF_SSI_CR_TE; /* enable tranmitter */
- +}
- +
- +static inline void disable_ssi(void)
- +{
- + if (audio_device==NULL || audio_device->ssi_enabled==0) return;
- + MCF_SSI_CR &= ~MCF_SSI_CR_TE; /* disable transmitter */
- + MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN; /* disable SSI module */
- + audio_device->ssi_enabled = 0;
- +}
- +
- +/* Audio CODEC initialization */
- +/* TODO: also the SSI frequency/dividers must be adjusted */
- +static void adjust_codec_speed(void) {
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":adjust_codec_speed: %d\n", audio_device->speed);
- +#endif
- +
- + if (audio_device->speed == 8000) {
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_8KHZ);
- + } else if (audio_device->speed == 16000) {
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_16KHZ);
- + } else if (audio_device->speed == 22000) {
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_22KHZ);
- + } else if (audio_device->speed == 44000 || audio_device->speed == 44100) {
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_44KHZ);
- + } else if (audio_device->speed == 48000) {
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_48KHZ);
- + } else {
- + /* default 44KHz */
- + codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_44KHZ);
- + }
- +}
- +
- +static void codec_reset(void)
- +{
- + codec_write(CODEC_RESET_REG, 0); /* reset the audio chip */
- + udelay(1500); /* wait for reset */
- +}
- +
- +static void init_audio_codec(void)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":init_audio_codec()\n");
- +#endif
- + codec_reset();
- +
- + codec_write(CODEC_LEFT_IN_REG, 0x017);
- + codec_write(CODEC_RIGHT_IN_REG, 0x017);
- + codec_write(CODEC_POWER_DOWN_REG, 0x000); /* Turn off line input */
- + codec_write(CODEC_DIGITAL_IF_FMT_REG, 0x00A); /* I2S slave mode */
- + /* codec_write(CODEC_DIGITAL_IF_FMT_REG, 0x042); // I2S master mode */
- + codec_write(CODEC_DIGITAL_APATH_REG, 0x007); /* Set A path */
- +
- + /* set sample rate */
- + adjust_codec_speed();
- +
- + codec_write(CODEC_LEFT_HP_VOL_REG, 0x075); /* set volume */
- + codec_write(CODEC_RIGHT_HP_VOL_REG, 0x075); /* set volume */
- + codec_write(CODEC_DIGITAL_IF_ACT_REG, 1); /* Activate digital interface */
- + codec_write(CODEC_ANALOG_APATH_REG, 0x0F2);
- +}
- +
- +
- +static void chip_init(void)
- +{
- +#ifdef CONFIG_SSIAUDIO_USE_EDMA
- + init_dma();
- +#endif
- +
- + /* Enable the SSI pins */
- + MCF_GPIO_PAR_SSI = ( 0
- + | MCF_GPIO_PAR_SSI_MCLK
- + | MCF_GPIO_PAR_SSI_STXD(3)
- + | MCF_GPIO_PAR_SSI_SRXD(3)
- + | MCF_GPIO_PAR_SSI_FS(3)
- + | MCF_GPIO_PAR_SSI_BCLK(3) );
- +
- +}
- +
- +static void init_ssi(void)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":init_ssi()\n");
- +#endif
- +
- + /* Dividers are for MCF54445 on 266Mhz, the output is 44.1Khz*/
- + /* Enable SSI clock in CCM */
- + MCF_CCM_CDR = MCF_CCM_CDR_SSIDIV(47);
- +
- + /* Issue a SSI reset */
- + MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN; /* disable SSI module */
- +
- + /* SSI module uses internal CPU clock */
- + MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSISRC;
- +
- + MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSIPUE;
- + MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSIPUS_UP;
- +
- + MCF_SSI_CR = 0
- + | MCF_SSI_CR_CIS
- + | MCF_SSI_CR_TCH /* Enable two channel mode */
- + | MCF_SSI_CR_MCE /* Set clock out on SSI_MCLK pin */
- + | MCF_SSI_CR_I2S_MASTER /* Set I2S master mode */
- + | MCF_SSI_CR_SYN /* Enable synchronous mode */
- + | MCF_SSI_CR_NET
- + ;
- +
- + MCF_SSI_TCR = 0
- + | MCF_SSI_TCR_TXDIR /* internally generated bit clock */
- + | MCF_SSI_TCR_TFDIR /* internally generated frame sync */
- + | MCF_SSI_TCR_TSCKP /* Clock data on falling edge of bit clock */
- + | MCF_SSI_TCR_TFSI /* Frame sync active low */
- + | MCF_SSI_TCR_TEFS /* TX frame sync 1 bit before data */
- + | MCF_SSI_TCR_TFEN0 /* TX FIFO 0 enabled */
- + | MCF_SSI_TCR_TFEN1 /* TX FIFO 1 enabled */
- + | MCF_SSI_TCR_TXBIT0
- + ;
- +
- + MCF_SSI_CCR = MCF_SSI_CCR_WL(7) /* 16 bit word length */
- + | MCF_SSI_CCR_DC(1) /* Frame rate divider */
- + | MCF_SSI_CCR_PM(0)
- + | MCF_SSI_CCR_DIV2
- + ;
- +
- + MCF_SSI_FCSR = 0
- + | MCF_SSI_FCSR_TFWM0(0)
- + | MCF_SSI_FCSR_TFWM1(0)
- + ;
- +
- + MCF_SSI_IER = 0 // interrupts
- +#ifndef CONFIG_SSIAUDIO_USE_EDMA
- + | MCF_SSI_IER_TIE /* transmit interrupts */
- + | MCF_SSI_IER_TFE0 /* transmit FIFO 0 empty */
- + | MCF_SSI_IER_TFE1 /* transmit FIFO 1 empty */
- +#else
- + | MCF_SSI_IER_TDMAE /* DMA request enabled */
- + | MCF_SSI_IER_TFE0 /* transmit FIFO 0 empty */
- + | MCF_SSI_IER_TFE1 /* transmit FIFO 1 empty */
- +#endif
- + ;
- +
- +#ifndef CONFIG_SSIAUDIO_USE_EDMA
- + /* enable IRQ: SSI interrupt */
- + MCF_INTC1_ICR(M5445x_AUDIO_IRQ_SOURCE) = M5445x_AUDIO_IRQ_LEVEL;
- + MCF_INTC1_CIMR = M5445x_AUDIO_IRQ_SOURCE;
- +#endif
- +}
- +
- +#ifndef CONFIG_SSIAUDIO_USE_EDMA
- +/* interrupt for SSI */
- +static int ssi_audio_isr(int irq, void *dev_id)
- +{
- + unsigned long *bp;
- +
- + if (audio_txbusy==0) {
- + return IRQ_HANDLED;
- + }
- +
- + spin_lock(&(audio_device->lock));
- +
- + if (audio_start == audio_append) {
- + disable_ssi();
- + audio_txbusy = 0;
- + } else {
- + if (MCF_SSI_ISR & (MCF_SSI_ISR_TFE0|MCF_SSI_ISR_TFE1)) {
- + bp = (unsigned long *) &audio_device->audio_buf[audio_start];
- + if (audio_device->channel) {
- + MCF_SSI_TX1 = *bp;
- + audio_device->channel = 0;
- + } else {
- + MCF_SSI_TX0 = *bp;
- + audio_device->channel = 1;
- + }
- + audio_start += 4;
- + if (audio_start >= BUFSIZE)
- + audio_start = 0;
- + }
- + }
- +
- + spin_unlock(&(audio_device->lock));
- +
- + return IRQ_HANDLED;
- +}
- +#endif
- +
- +/* Set initial driver playback defaults. */
- +static void init_driver_variables(void)
- +{
- + audio_device->speed = 44100;
- + audio_device->format = AFMT_S16_LE;
- + audio_device->bits = 16;
- + audio_device->stereo = 1;
- + audio_device->ssi_enabled = 0;
- +
- + audio_start = 0;
- + audio_count = 0;
- + audio_append = 0;
- + audio_appstart = 0;
- + audio_txbusy = 0;
- + audio_device->dmaing = 0;
- +}
- +
- +/* open audio device */
- +static int ssi_audio_open(struct inode *inode, struct file *filp)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_open()\n");
- +#endif
- +
- + if (audio_device==NULL) return (-ENODEV);
- +
- + if (audio_device->isopen)
- + return(-EBUSY);
- +
- + spin_lock(&(audio_device->lock));
- +
- + audio_device->isopen = 1;
- +
- + init_driver_variables();
- + init_ssi();
- + init_audio_codec();
- +
- + spin_unlock(&(audio_device->lock));
- +
- + udelay(100);
- +
- + return 0;
- +}
- +
- +/* close audio device */
- +static int ssi_audio_close(struct inode *inode, struct file *filp)
- +{
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_close()\n");
- +#endif
- +
- + if (audio_device==NULL) return (-ENODEV);
- +
- + ssi_audio_txdrain();
- +
- + spin_lock(&(audio_device->lock));
- +
- +#ifdef CONFIG_SSIAUDIO_USE_EDMA
- + stop_dma();
- +#endif
- + disable_ssi();
- + codec_reset();
- + init_driver_variables();
- + audio_device->isopen = 0;
- +
- + spin_unlock(&(audio_device->lock));
- + return 0;
- +}
- +
- +/* write to audio device */
- +static ssize_t ssi_audio_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
- +{
- + unsigned long *dp, *buflp;
- + unsigned short *bufwp;
- + unsigned char *bufbp;
- + unsigned int slen, bufcnt, i, s, e;
- +
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_write(buf=%x,count=%d)\n", (int) buf, count);
- +#endif
- +
- + if (audio_device==NULL) return (-ENODEV);
- +
- + if (count <= 0)
- + return 0;
- +
- + spin_lock(&(audio_device->lock));
- +
- + buflp = (unsigned long *) buf;
- + bufwp = (unsigned short *) buf;
- + bufbp = (unsigned char *) buf;
- +
- + bufcnt = count & ~0x3;
- +
- + bufcnt <<= 1;
- + if (audio_device->stereo == 0)
- + bufcnt <<= 1;
- + if (audio_device->bits == 8)
- + bufcnt <<= 1;
- +
- +tryagain:
- + /*
- + * Get a snapshot of buffer, so we can figure out how
- + * much data we can fit in...
- + */
- + s = audio_start;
- + e = audio_append;
- + dp = (unsigned long *) &(audio_device->audio_buf[e]);
- +
- + slen = ((s > e) ? (s - e) : (BUFSIZE - (e - s))) - 4;
- + if (slen > bufcnt)
- + slen = bufcnt;
- + if ((BUFSIZE - e) < slen)
- + slen = BUFSIZE - e;
- +
- + if (slen == 0) {
- + if (signal_pending(current))
- + return(-ERESTARTSYS);
- + set_current_state(TASK_INTERRUPTIBLE);
- + schedule_timeout(1);
- + goto tryagain;
- + }
- +
- + /* For DMA we need to have data as 32 bit
- + values (since SSI TX register is 32 bit).
- + So, the incomming 16 bit data must be put to buffer as 32 bit values.
- + Also, the endianess is converted if needed
- + */
- + if (audio_device->stereo) {
- + if (audio_device->bits == 16) {
- + if (audio_device->format==AFMT_S16_LE) {
- + /*- convert endianess, probably could be done by SSI also */
- + for (i = 0; (i < slen); i += 4) {
- + unsigned short val = le16_to_cpu((*bufwp++));
- + *dp++ = val;
- + }
- + } else {
- + for (i = 0; (i < slen); i += 4) {
- + *dp++ = *bufwp++;
- + }
- + }
- + } else {
- + for (i = 0; (i < slen); i += 4) {
- + *dp = (((unsigned long) *bufbp++) << 24);
- + *dp++ |= (((unsigned long) *bufbp++) << 8);
- + }
- + }
- + } else {
- + if (audio_device->bits == 16) {
- + for (i = 0; (i < slen); i += 4) {
- + *dp++ = (((unsigned long)*bufwp)<<16) | *bufwp;
- + bufwp++;
- + }
- + } else {
- + for (i = 0; (i < slen); i += 4) {
- + *dp++ = (((unsigned long) *bufbp) << 24) |
- + (((unsigned long) *bufbp) << 8);
- + bufbp++;
- + }
- + }
- + }
- +
- + e += slen;
- + if (e >= BUFSIZE)
- + e = 0;
- + audio_append = e;
- +
- + /* If not outputing audio, then start now */
- + if (audio_txbusy == 0) {
- + audio_txbusy++;
- + audio_device->channel = 0;
- + enable_ssi();
- +#ifdef CONFIG_SSIAUDIO_USE_EDMA
- + ssi_audio_dmabuf(); /* start first DMA transfer */
- +#endif
- + }
- +
- + bufcnt -= slen;
- +
- + if (bufcnt > 0)
- + goto tryagain;
- +
- + spin_unlock(&(audio_device->lock));
- +
- + return count;
- +}
- +
- +/* ioctl: control the driver */
- +static int ssi_audio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
- +{
- + long val;
- + int rc = 0;
- +
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_ioctl(cmd=%x,arg=%x)\n", (int) cmd, (int) arg);
- +#endif
- +
- + if (audio_device==NULL) return (-ENODEV);
- +
- + switch (cmd) {
- +
- + case SNDCTL_DSP_SPEED:
- + if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
- + get_user(val, (unsigned long *) arg);
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME ":ssi_audio_ioctl: SNDCTL_DSP_SPEED: %ld\n", val);
- +#endif
- + ssi_audio_txdrain();
- + audio_device->speed = val;
- + init_audio_codec();
- + } else {
- + rc = -EINVAL;
- + }
- + break;
- +
- + case SNDCTL_DSP_SAMPLESIZE:
- + if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
- + get_user(val, (unsigned long *) arg);
- + ssi_audio_txdrain();
- + ssi_audio_setsamplesize(val);
- + } else {
- + rc = -EINVAL;
- + }
- + break;
- +
- + case SNDCTL_DSP_STEREO:
- + if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
- + get_user(val, (unsigned long *) arg);
- + ssi_audio_txdrain();
- + audio_device->stereo = val;
- + } else {
- + rc = -EINVAL;
- + }
- + break;
- +
- + case SNDCTL_DSP_GETBLKSIZE:
- + if (access_ok(VERIFY_WRITE, (void *) arg, sizeof(long)))
- + put_user(BUFSIZE, (long *) arg);
- + else
- + rc = -EINVAL;
- + break;
- +
- + case SNDCTL_DSP_SYNC:
- + ssi_audio_txdrain();
- + break;
- +
- + default:
- + rc = -EINVAL;
- + break;
- + }
- +
- + return rc;
- +}
- +
- +/****************************************************************************/
- +
- +struct file_operations ssi_audio_fops = {
- + open: ssi_audio_open, /* open */
- + release: ssi_audio_close, /* close */
- + write: ssi_audio_write, /* write */
- + ioctl: ssi_audio_ioctl, /* ioctl */
- +};
- +
- +/* initialize audio driver */
- +static int __devinit ssi_audio_probe(struct spi_device *spi)
- +{
- + struct ssi_audio *audio;
- + int err;
- +
- +#ifdef AUDIO_DEBUG
- + printk(DRIVER_NAME": probe\n");
- +#endif
- +
- + if (!spi->irq) {
- + dev_dbg(&spi->dev, "no IRQ?\n");
- + return -ENODEV;
- + }
- +
- + /* don't exceed max specified sample rate */
- + if (spi->max_speed_hz > MAX_SPEED_HZ) {
- + dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
- + (spi->max_speed_hz)/1000);
- + return -EINVAL;
- + }
- +
- + /* register charcter device */
- + if (register_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME, &ssi_audio_fops) < 0) {
- + printk(KERN_WARNING DRIVER_NAME ": failed to register major %d\n", SOUND_MAJOR);
- + dev_dbg(&spi->dev, DRIVER_NAME ": failed to register major %d\n", SOUND_MAJOR);
- + return -ENODEV;
- + }
- +
- + audio = kzalloc(sizeof(struct ssi_audio), GFP_KERNEL);
- + if (!audio) {
- + err = -ENOMEM;
- + goto err_out;
- + }
- +
- + /* DMA buffer must be from GFP_DMA zone, so it will not be cached */
- + audio->audio_buf = kmalloc(BUFSIZE, GFP_DMA);
- + if (audio->audio_buf == NULL) {
- + dev_dbg(&spi->dev, DRIVER_NAME ": failed to allocate DMA[%d] buffer\n", BUFSIZE);
- + err = -ENOMEM;
- + goto err_free_mem;
- + }
- +
- + audio_device = audio;
- +
- + dev_set_drvdata(&spi->dev, audio);
- + spi->dev.power.power_state = PMSG_ON;
- +
- + audio->spi = spi;
- +
- +#ifndef CONFIG_SSIAUDIO_USE_EDMA
- + if (request_irq(spi->irq, ssi_audio_isr, SA_INTERRUPT, spi->dev.bus_id, audio)) {
- + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
- + err = -EBUSY;
- + goto err_free_mem;
- + }
- +
- +#else
- + /* request 2 eDMA channels since two channel output mode is used */
- + if (request_edma_channel(DMA_TCD,
- + ssi_audio_dma_handler_empty,
- + NULL,
- + audio,
- + &(audio_device->lock),
- + DRIVER_NAME
- + )!=0)
- + {
- + dev_dbg(&spi->dev, "DMA channel %d busy?\n", DMA_TCD);
- + err = -EBUSY;
- + goto err_free_mem;
- + }
- + if (request_edma_channel(DMA_TCD2,
- + ssi_audio_dma_handler,
- + NULL,
- + audio,
- + &(audio_device->lock),
- + DRIVER_NAME
- + )!=0)
- + {
- + dev_dbg(&spi->dev, "DMA channel %d busy?\n", DMA_TCD2);
- + err = -EBUSY;
- + goto err_free_mem;
- + }
- +
- +#endif
- + chip_init();
- + printk(DRIVER_NAME ": Probed successfully\n");
- +
- + return 0;
- +
- + err_free_mem:
- + kfree(audio);
- + audio_device = NULL;
- + err_out:
- + unregister_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME);
- + return err;
- +}
- +
- +static int __devexit ssi_audio_remove(struct spi_device *spi)
- +{
- + struct ssi_audio *audio = dev_get_drvdata(&spi->dev);
- +
- + ssi_audio_txdrain();
- +#ifndef CONFIG_SSIAUDIO_USE_EDMA
- + free_irq(spi->irq, audio);
- +#else
- + free_edma_channel(DMA_TCD, audio);
- + free_edma_channel(DMA_TCD2, audio);
- +#endif
- + kfree(audio->audio_buf);
- + kfree(audio);
- + audio_device = NULL;
- + unregister_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME);
- + dev_dbg(&spi->dev, "unregistered audio\n");
- + return 0;
- +}
- +
- +static int ssi_audio_suspend(struct spi_device *spi, pm_message_t message) {
- + return 0;
- +}
- +
- +static int ssi_audio_resume(struct spi_device *spi) {
- + return 0;
- +}
- +
- +static struct spi_driver ssi_audio_driver = {
- + .driver = {
- + .name = DRIVER_NAME,
- + .bus = &spi_bus_type,
- + .owner = THIS_MODULE,
- + },
- + .probe = ssi_audio_probe,
- + .remove = __devexit_p(ssi_audio_remove),
- + .suspend = ssi_audio_suspend,
- + .resume = ssi_audio_resume,
- +};
- +
- +static int __init ssi_audio_init(void)
- +{
- + return spi_register_driver(&ssi_audio_driver);
- +}
- +module_init(ssi_audio_init);
- +
- +static void __exit ssi_audio_exit(void)
- +{
- + spi_unregister_driver(&ssi_audio_driver);
- +}
- +module_exit(ssi_audio_exit);
- +
- +MODULE_DESCRIPTION("SSI/I2S Audio Driver");
- +MODULE_LICENSE("GPL");
- --- a/include/asm-m68k/coldfire_edma.h
- +++ b/include/asm-m68k/coldfire_edma.h
- @@ -1,39 +1,102 @@
- +/*
- + * coldfire_edma.h - eDMA driver for Coldfire MCF5445x
- + *
- + * Yaroslav Vinogradov [email protected]
- + *
- + * Copyright Freescale Semiconductor, Inc. 2007
- + *
- + * This program is free software; you can redistribute it and/or modify it
- + * under the terms of the GNU General Public License as published by the
- + * Free Software Foundation; either version 2 of the License, or (at your
- + * option) any later version.
- + */
- +
- #ifndef _LINUX_COLDFIRE_DMA_H
- #define _LINUX_COLDFIRE_DMA_H
-
- #include <linux/interrupt.h>
- +#include <asm/mcf5445x_edma.h>
-
- -#define EDMA_DRIVER_NAME "ColdFire-eDMA"
- -#define DMA_DEV_MINOR 1
- +#define EDMA_DRIVER_NAME "ColdFire-eDMA"
- +#define DMA_DEV_MINOR 1
-
- #define EDMA_INT_CHANNEL_BASE 8
- #define EDMA_INT_CONTROLLER_BASE 64
- #define EDMA_CHANNELS 16
- -
- +
- #define EDMA_IRQ_LEVEL 5
- -
- +
- typedef irqreturn_t (*edma_irq_handler)(int, void *);
- typedef void (*edma_error_handler)(int, void *);
- -
- +
- +/* Setup transfer control descriptor (TCD)
- + * channel - descriptor number
- + * source - source address
- + * dest - destination address
- + * attr - attributes
- + * soff - source offset
- + * nbytes - number of bytes to be transfered in minor loop
- + * slast - last source address adjustment
- + * citer - major loop count
- + * biter - beggining minor loop count
- + * doff - destination offset
- + * dlast_sga - last destination address adjustment
- + * major_int - generate interrupt after each major loop
- + * disable_req - disable DMA request after major loop
- + */
- void set_edma_params(int channel, u32 source, u32 dest,
- - u32 attr, u32 soff, u32 nbytes, u32 slast,
- - u32 citer, u32 biter, u32 doff, u32 dlast_sga);
- -
- -void start_edma_transfer(int channel, int major_int);
- -
- -void stop_edma_transfer(int channel);
- -
- -void confirm_edma_interrupt_handled(int channel);
- -
- + u32 attr, u32 soff, u32 nbytes, u32 slast,
- + u32 citer, u32 biter, u32 doff, u32 dlast_sga,
- + int major_int, int disable_req);
- +
- +/* Starts eDMA transfer on specified channel
- + * channel - eDMA TCD number
- + */
- +static inline void start_edma_transfer(int channel)
- +{
- + MCF_EDMA_SERQ = channel;
- + MCF_EDMA_SSRT = channel;
- +}
- +
- +/* Stops eDMA transfer
- + * channel - eDMA TCD number
- + */
- +static inline void stop_edma_transfer(int channel)
- +{
- + MCF_EDMA_CINT = channel;
- + MCF_EDMA_CERQ = channel;
- +}
- +
- +
- +/* Confirm that interrupt has been handled
- + * channel - eDMA TCD number
- + */
- +static inline void confirm_edma_interrupt_handled(int channel)
- +{
- + MCF_EDMA_CINT = channel;
- +}
- +
- +/* Initialize eDMA controller */
- void init_edma(void);
- -
- -int request_edma_channel(int channel,
- - edma_irq_handler handler,
- - edma_error_handler error_handler,
- - void *dev,
- - spinlock_t *lock,
- - const char *device_id);
- -
- +
- +/* Request eDMA channel:
- + * channel - eDMA TCD number
- + * handler - channel IRQ callback
- + * error_handler - error interrupt handler callback for channel
- + * dev - device
- + * lock - spinlock to be locked (can be NULL)
- + * device_id - device driver name for proc file system output
- + */
- +int request_edma_channel(int channel,
- + edma_irq_handler handler,
- + edma_error_handler error_handler,
- + void *dev,
- + spinlock_t *lock,
- + const char *device_id);
- +
- +/* Free eDMA channel
- + * channel - eDMA TCD number
- + * dev - device
- + */
- int free_edma_channel(int channel, void *dev);
- -
- #endif
- --- /dev/null
- +++ b/include/linux/spi/mcfqspi.h
- @@ -0,0 +1,80 @@
- +/****************************************************************************/
- +
- +/*
- + * mcfqspi.c - Master QSPI controller for the ColdFire processors
- + *
- + * (C) Copyright 2005, Intec Automation,
- + * Mike Lavender (mike@steroidmicros)
- + *
- +
- + This program is free software; you can redistribute it and/or modify
- + it under the terms of the GNU General Public License as published by
- + the Free Software Foundation; either version 2 of the License, or
- + (at your option) any later version.
- +
- + This program is distributed in the hope that it will be useful,
- + but WITHOUT ANY WARRANTY; without even the implied warranty of
- + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + GNU General Public License for more details.
- +
- + You should have received a copy of the GNU General Public License
- + along with this program; if not, write to the Free Software
- + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- +/* ------------------------------------------------------------------------- */
- +
- +#ifndef MCFQSPI_H_
- +#define MCFQSPI_H_
- +
- +#define QSPI_CS_INIT 0x01
- +#define QSPI_CS_ASSERT 0x02
- +#define QSPI_CS_DROP 0x04
- +
- +#define QSPIIOCS_DOUT_HIZ 1 /* QMR[DOHIE] set hi-z dout between transfers */
- +#define QSPIIOCS_BITS 2 /* QMR[BITS] set transfer size */
- +#define QSPIIOCG_BITS 3 /* QMR[BITS] get transfer size */
- +#define QSPIIOCS_CPOL 4 /* QMR[CPOL] set SCK inactive state */
- +#define QSPIIOCS_CPHA 5 /* QMR[CPHA] set SCK phase, 1=rising edge */
- +#define QSPIIOCS_BAUD 6 /* QMR[BAUD] set SCK baud rate divider */
- +#define QSPIIOCS_QCD 7 /* QDLYR[QCD] set start delay */
- +#define QSPIIOCS_DTL 8 /* QDLYR[DTL] set after delay */
- +#define QSPIIOCS_CONT 9 /* continuous CS asserted during transfer */
- +#define QSPIIOCS_READDATA 10 /* set data send during read */
- +#define QSPIIOCS_ODD_MOD 11 /* if length of buffer is a odd number, 16-bit transfers */
- + /* are finalized with a 8-bit transfer */
- +#define QSPIIOCS_DSP_MOD 12 /* transfers are bounded to 15/30 bytes (a multiple of 3 bytes = 1 DSPword) */
- +#define QSPIIOCS_POLL_MOD 13 /* driver uses polling instead of interrupts */
- +
- +#define QSPIIOCS_SET_CSIV 14 /* sets CSIV flag (cs inactive level) */
- +
- +#ifdef CONFIG_M520x
- +#undef MCF_GPIO_PAR_QSPI
- +#define MCF_GPIO_PAR_QSPI (0xA4034)
- +#endif
- +
- +struct coldfire_spi_master {
- + u16 bus_num;
- + u16 num_chipselect;
- + u8 irq_source;
- + u32 irq_vector;
- + u32 irq_mask;
- + u8 irq_lp;
- + u8 par_val;
- + u16 par_val16;
- + void (*cs_control)(u8 cs, u8 command);
- +};
- +
- +
- +struct coldfire_spi_chip {
- + u8 mode;
- + u8 bits_per_word;
- + u8 del_cs_to_clk;
- + u8 del_after_trans;
- + u16 void_write_data;
- +};
- +
- +typedef struct qspi_read_data {
- + __u32 length;
- + __u8 *buf; /* data to send during read */
- + unsigned int loop : 1;
- +} qspi_read_data;
- +#endif /*MCFQSPI_H_*/
|