12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786 |
- /* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- */
- /*
- * SPI driver for Qualcomm MSM platforms
- *
- */
- #include <linux/version.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/spinlock.h>
- #include <linux/list.h>
- #include <linux/irq.h>
- #include <linux/platform_device.h>
- #include <linux/spi/spi.h>
- #include <linux/interrupt.h>
- #include <linux/err.h>
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/workqueue.h>
- #include <linux/io.h>
- #include <linux/debugfs.h>
- #include <linux/gpio.h>
- #include <linux/of.h>
- #include <linux/of_gpio.h>
- #include <linux/dma-mapping.h>
- #include <linux/sched.h>
- #include <linux/mutex.h>
- #include <linux/atomic.h>
- #include <linux/pm_runtime.h>
- #include <linux/spi/qcom-spi.h>
- #include <linux/msm-sps.h>
- #include <linux/msm-bus.h>
- #include <linux/msm-bus-board.h>
- #include "spi_qsd.h"
- #define SPI_MAX_BYTES_PER_WORD (4)
- static int msm_spi_pm_resume_runtime(struct device *device);
- static int msm_spi_pm_suspend_runtime(struct device *device);
- static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd);
- static int get_local_resources(struct msm_spi *dd);
- static void put_local_resources(struct msm_spi *dd);
- static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
- struct platform_device *pdev)
- {
- struct resource *resource;
- unsigned long gsbi_mem_phys_addr;
- size_t gsbi_mem_size;
- void __iomem *gsbi_base;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!resource)
- return 0;
- gsbi_mem_phys_addr = resource->start;
- gsbi_mem_size = resource_size(resource);
- if (!devm_request_mem_region(&pdev->dev, gsbi_mem_phys_addr,
- gsbi_mem_size, SPI_DRV_NAME))
- return -ENXIO;
- gsbi_base = devm_ioremap(&pdev->dev, gsbi_mem_phys_addr,
- gsbi_mem_size);
- if (!gsbi_base)
- return -ENXIO;
- /* Set GSBI to SPI mode */
- writel_relaxed(GSBI_SPI_CONFIG, gsbi_base + GSBI_CTRL_REG);
- return 0;
- }
- static inline void msm_spi_register_init(struct msm_spi *dd)
- {
- writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
- msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- writel_relaxed(0x00000000, dd->base + SPI_OPERATIONAL);
- writel_relaxed(0x00000000, dd->base + SPI_CONFIG);
- writel_relaxed(0x00000000, dd->base + SPI_IO_MODES);
- if (dd->qup_ver)
- writel_relaxed(0x00000000, dd->base + QUP_OPERATIONAL_MASK);
- }
- static int msm_spi_pinctrl_init(struct msm_spi *dd)
- {
- dd->pinctrl = devm_pinctrl_get(dd->dev);
- if (IS_ERR_OR_NULL(dd->pinctrl)) {
- dev_err(dd->dev, "Failed to get pin ctrl\n");
- return PTR_ERR(dd->pinctrl);
- }
- dd->pins_active = pinctrl_lookup_state(dd->pinctrl,
- SPI_PINCTRL_STATE_DEFAULT);
- if (IS_ERR_OR_NULL(dd->pins_active)) {
- dev_err(dd->dev, "Failed to lookup pinctrl default state\n");
- return PTR_ERR(dd->pins_active);
- }
- dd->pins_sleep = pinctrl_lookup_state(dd->pinctrl,
- SPI_PINCTRL_STATE_SLEEP);
- if (IS_ERR_OR_NULL(dd->pins_sleep)) {
- dev_err(dd->dev, "Failed to lookup pinctrl sleep state\n");
- return PTR_ERR(dd->pins_sleep);
- }
- return 0;
- }
- static inline int msm_spi_request_gpios(struct msm_spi *dd)
- {
- int i = 0;
- int result = 0;
- if (!dd->pdata->use_pinctrl) {
- for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
- if (dd->spi_gpios[i] >= 0) {
- result = gpio_request(dd->spi_gpios[i],
- spi_rsrcs[i]);
- if (result) {
- dev_err(dd->dev,
- "error %d gpio_request for pin %d\n",
- result, dd->spi_gpios[i]);
- goto error;
- }
- }
- }
- } else {
- result = pinctrl_select_state(dd->pinctrl, dd->pins_active);
- if (result) {
- dev_err(dd->dev, "%s: Can not set %s pins\n",
- __func__, SPI_PINCTRL_STATE_DEFAULT);
- goto error;
- }
- }
- return 0;
- error:
- if (!dd->pdata->use_pinctrl) {
- for (; --i >= 0;) {
- if (dd->spi_gpios[i] >= 0)
- gpio_free(dd->spi_gpios[i]);
- }
- }
- return result;
- }
- static inline void msm_spi_free_gpios(struct msm_spi *dd)
- {
- int i;
- int result = 0;
- if (!dd->pdata->use_pinctrl) {
- for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
- if (dd->spi_gpios[i] >= 0)
- gpio_free(dd->spi_gpios[i]);
- }
- for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
- if (dd->cs_gpios[i].valid) {
- gpio_free(dd->cs_gpios[i].gpio_num);
- dd->cs_gpios[i].valid = 0;
- }
- }
- } else {
- result = pinctrl_select_state(dd->pinctrl, dd->pins_sleep);
- if (result)
- dev_err(dd->dev, "%s: Can not set %s pins\n",
- __func__, SPI_PINCTRL_STATE_SLEEP);
- }
- }
- static inline int msm_spi_request_cs_gpio(struct msm_spi *dd)
- {
- int cs_num;
- int rc;
- cs_num = dd->spi->chip_select;
- if (!(dd->spi->mode & SPI_LOOP)) {
- if (!dd->pdata->use_pinctrl) {
- if ((!(dd->cs_gpios[cs_num].valid)) &&
- (dd->cs_gpios[cs_num].gpio_num >= 0)) {
- rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
- spi_cs_rsrcs[cs_num]);
- if (rc) {
- dev_err(dd->dev,
- "gpio_request for pin %d failed,error %d\n",
- dd->cs_gpios[cs_num].gpio_num, rc);
- return rc;
- }
- dd->cs_gpios[cs_num].valid = 1;
- }
- }
- }
- return 0;
- }
- static inline void msm_spi_free_cs_gpio(struct msm_spi *dd)
- {
- int cs_num;
- cs_num = dd->spi->chip_select;
- if (!dd->pdata->use_pinctrl) {
- if (dd->cs_gpios[cs_num].valid) {
- gpio_free(dd->cs_gpios[cs_num].gpio_num);
- dd->cs_gpios[cs_num].valid = 0;
- }
- }
- }
- /**
- * msm_spi_clk_max_rate: finds the nearest lower rate for a clk
- * @clk the clock for which to find nearest lower rate
- * @rate clock frequency in Hz
- * @return nearest lower rate or negative error value
- *
- * Public clock API extends clk_round_rate which is a ceiling function. This
- * function is a floor function implemented as a binary search using the
- * ceiling function.
- */
- static long msm_spi_clk_max_rate(struct clk *clk, unsigned long rate)
- {
- long lowest_available, nearest_low, step_size, cur;
- long step_direction = -1;
- long guess = rate;
- int max_steps = 10;
- cur = clk_round_rate(clk, rate);
- if (cur == rate)
- return rate;
- /* if we got here then: cur > rate */
- lowest_available = clk_round_rate(clk, 0);
- if (lowest_available > rate)
- return -EINVAL;
- step_size = (rate - lowest_available) >> 1;
- nearest_low = lowest_available;
- while (max_steps-- && step_size) {
- guess += step_size * step_direction;
- cur = clk_round_rate(clk, guess);
- if ((cur < rate) && (cur > nearest_low))
- nearest_low = cur;
- /*
- * if we stepped too far, then start stepping in the other
- * direction with half the step size
- */
- if (((cur > rate) && (step_direction > 0))
- || ((cur < rate) && (step_direction < 0))) {
- step_direction = -step_direction;
- step_size >>= 1;
- }
- }
- return nearest_low;
- }
- static void msm_spi_clock_set(struct msm_spi *dd, int speed)
- {
- long rate;
- int rc;
- rate = msm_spi_clk_max_rate(dd->clk, speed);
- if (rate < 0) {
- dev_err(dd->dev,
- "%s: no match found for requested clock frequency:%d",
- __func__, speed);
- return;
- }
- rc = clk_set_rate(dd->clk, rate);
- if (!rc)
- dd->clock_speed = rate;
- }
- static void msm_spi_clk_path_vote(struct msm_spi *dd, u32 rate)
- {
- if (dd->bus_cl_hdl) {
- u64 ib = rate * dd->pdata->bus_width;
- msm_bus_scale_update_bw(dd->bus_cl_hdl, 0, ib);
- }
- }
- static void msm_spi_clk_path_teardown(struct msm_spi *dd)
- {
- msm_spi_clk_path_vote(dd, 0);
- if (dd->bus_cl_hdl) {
- msm_bus_scale_unregister(dd->bus_cl_hdl);
- dd->bus_cl_hdl = NULL;
- }
- }
- /**
- * msm_spi_clk_path_postponed_register: reg with bus-scaling after it is probed
- *
- * @return zero on success
- *
- * Workaround: SPI driver may be probed before the bus scaling driver. Calling
- * msm_bus_scale_register_client() will fail if the bus scaling driver is not
- * ready yet. Thus, this function should be called not from probe but from a
- * later context. Also, this function may be called more then once before
- * register succeed. At this case only one error message will be logged. At boot
- * time all clocks are on, so earlier SPI transactions should succeed.
- */
- static int msm_spi_clk_path_postponed_register(struct msm_spi *dd)
- {
- int ret = 0;
- dd->bus_cl_hdl = msm_bus_scale_register(dd->pdata->master_id,
- MSM_BUS_SLAVE_EBI_CH0,
- (char *)dev_name(dd->dev),
- false);
- if (IS_ERR_OR_NULL(dd->bus_cl_hdl)) {
- ret = (dd->bus_cl_hdl ? PTR_ERR(dd->bus_cl_hdl) : -EAGAIN);
- dev_err(dd->dev, "Failed bus registration Err %d", ret);
- }
- return ret;
- }
- static void msm_spi_clk_path_init(struct msm_spi *dd)
- {
- /*
- * bail out if path voting is diabled (master_id == 0) or if it is
- * already registered (client_hdl != 0)
- */
- if (!dd->pdata->master_id || dd->bus_cl_hdl)
- return;
- /* on failure try again later */
- if (msm_spi_clk_path_postponed_register(dd))
- return;
- }
- static int msm_spi_calculate_size(int *fifo_size,
- int *block_size,
- int block,
- int mult)
- {
- int words;
- switch (block) {
- case 0:
- words = 1; /* 4 bytes */
- break;
- case 1:
- words = 4; /* 16 bytes */
- break;
- case 2:
- words = 8; /* 32 bytes */
- break;
- default:
- return -EINVAL;
- }
- switch (mult) {
- case 0:
- *fifo_size = words * 2;
- break;
- case 1:
- *fifo_size = words * 4;
- break;
- case 2:
- *fifo_size = words * 8;
- break;
- case 3:
- *fifo_size = words * 16;
- break;
- default:
- return -EINVAL;
- }
- *block_size = words * sizeof(u32); /* in bytes */
- return 0;
- }
- static void msm_spi_calculate_fifo_size(struct msm_spi *dd)
- {
- u32 spi_iom;
- int block;
- int mult;
- spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
- block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> INPUT_BLOCK_SZ_SHIFT;
- mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> INPUT_FIFO_SZ_SHIFT;
- if (msm_spi_calculate_size(&dd->input_fifo_size, &dd->input_block_size,
- block, mult)) {
- goto fifo_size_err;
- }
- block = (spi_iom & SPI_IO_M_OUTPUT_BLOCK_SIZE) >> OUTPUT_BLOCK_SZ_SHIFT;
- mult = (spi_iom & SPI_IO_M_OUTPUT_FIFO_SIZE) >> OUTPUT_FIFO_SZ_SHIFT;
- if (msm_spi_calculate_size(&dd->output_fifo_size,
- &dd->output_block_size, block, mult)) {
- goto fifo_size_err;
- }
- if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
- /* DM mode is not available for this block size */
- if (dd->input_block_size == 4 || dd->output_block_size == 4)
- dd->use_dma = 0;
- if (dd->use_dma) {
- dd->input_burst_size = max(dd->input_block_size,
- DM_BURST_SIZE);
- dd->output_burst_size = max(dd->output_block_size,
- DM_BURST_SIZE);
- }
- }
- return;
- fifo_size_err:
- dd->use_dma = 0;
- pr_err("%s: invalid FIFO size, SPI_IO_MODES=0x%x\n", __func__, spi_iom);
- }
- static void msm_spi_read_word_from_fifo(struct msm_spi *dd)
- {
- u32 data_in;
- int i;
- int shift;
- int read_bytes = (dd->pack_words ?
- SPI_MAX_BYTES_PER_WORD : dd->bytes_per_word);
- data_in = readl_relaxed(dd->base + SPI_INPUT_FIFO);
- if (dd->read_buf) {
- for (i = 0; (i < read_bytes) &&
- dd->rx_bytes_remaining; i++) {
- /* The data format depends on bytes_per_word:
- * 4 bytes: 0x12345678
- * 3 bytes: 0x00123456
- * 2 bytes: 0x00001234
- * 1 byte : 0x00000012
- */
- shift = BITS_PER_BYTE * i;
- *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift;
- dd->rx_bytes_remaining--;
- }
- } else {
- if (dd->rx_bytes_remaining >= read_bytes)
- dd->rx_bytes_remaining -= read_bytes;
- else
- dd->rx_bytes_remaining = 0;
- }
- dd->read_xfr_cnt++;
- }
- static inline bool msm_spi_is_valid_state(struct msm_spi *dd)
- {
- u32 spi_op = readl_relaxed(dd->base + SPI_STATE);
- return spi_op & SPI_OP_STATE_VALID;
- }
- static inline void msm_spi_udelay(unsigned int delay_usecs)
- {
- /*
- * For smaller values of delay, context switch time
- * would negate the usage of usleep
- */
- if (delay_usecs > 20)
- usleep_range(delay_usecs, delay_usecs + 1);
- else if (delay_usecs)
- udelay(delay_usecs);
- }
- static inline int msm_spi_wait_valid(struct msm_spi *dd)
- {
- unsigned int delay = 0;
- unsigned long timeout = 0;
- if (dd->clock_speed == 0)
- return -EINVAL;
- /*
- * Based on the SPI clock speed, sufficient time
- * should be given for the SPI state transition
- * to occur
- */
- delay = (10 * USEC_PER_SEC) / dd->clock_speed;
- /*
- * For small delay values, the default timeout would
- * be one jiffy
- */
- if (delay < SPI_DELAY_THRESHOLD)
- delay = SPI_DELAY_THRESHOLD;
- /* Adding one to round off to the nearest jiffy */
- timeout = jiffies + msecs_to_jiffies(delay * SPI_DEFAULT_TIMEOUT) + 1;
- while (!msm_spi_is_valid_state(dd)) {
- if (time_after(jiffies, timeout)) {
- if (!msm_spi_is_valid_state(dd)) {
- dev_err(dd->dev, "Invalid SPI operational state\n");
- return -ETIMEDOUT;
- } else
- return 0;
- }
- msm_spi_udelay(delay);
- }
- return 0;
- }
- static inline int msm_spi_set_state(struct msm_spi *dd,
- enum msm_spi_state state)
- {
- enum msm_spi_state cur_state;
- if (msm_spi_wait_valid(dd))
- return -EIO;
- cur_state = readl_relaxed(dd->base + SPI_STATE);
- /* Per spec:
- * For PAUSE_STATE to RESET_STATE, two writes of (10) are required
- */
- if (((cur_state & SPI_OP_STATE) == SPI_OP_STATE_PAUSE) &&
- (state == SPI_OP_STATE_RESET)) {
- writel_relaxed(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
- writel_relaxed(SPI_OP_STATE_CLEAR_BITS, dd->base + SPI_STATE);
- } else {
- writel_relaxed((cur_state & ~SPI_OP_STATE) | state,
- dd->base + SPI_STATE);
- }
- if (msm_spi_wait_valid(dd))
- return -EIO;
- return 0;
- }
- /**
- * msm_spi_set_bpw_and_no_io_flags: configure N, and no-input/no-output flags
- */
- static inline void
- msm_spi_set_bpw_and_no_io_flags(struct msm_spi *dd, u32 *config, int n)
- {
- *config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
- if (n != (*config & SPI_CFG_N))
- *config = (*config & ~SPI_CFG_N) | n;
- if (dd->tx_mode == SPI_BAM_MODE) {
- if (dd->read_buf == NULL)
- *config |= SPI_NO_INPUT;
- if (dd->write_buf == NULL)
- *config |= SPI_NO_OUTPUT;
- }
- }
- /**
- * msm_spi_calc_spi_config_loopback_and_input_first: Calculate the values that
- * should be updated into SPI_CONFIG's LOOPBACK and INPUT_FIRST flags
- * @return calculatd value for SPI_CONFIG
- */
- static u32
- msm_spi_calc_spi_config_loopback_and_input_first(u32 spi_config, u8 mode)
- {
- if (mode & SPI_LOOP)
- spi_config |= SPI_CFG_LOOPBACK;
- else
- spi_config &= ~SPI_CFG_LOOPBACK;
- if (mode & SPI_CPHA)
- spi_config &= ~SPI_CFG_INPUT_FIRST;
- else
- spi_config |= SPI_CFG_INPUT_FIRST;
- return spi_config;
- }
- /**
- * msm_spi_set_spi_config: prepares register SPI_CONFIG to process the
- * next transfer
- */
- static void msm_spi_set_spi_config(struct msm_spi *dd, int bpw)
- {
- u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG);
- spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
- spi_config, dd->spi->mode);
- if (dd->qup_ver == SPI_QUP_VERSION_NONE)
- /* flags removed from SPI_CONFIG in QUP version-2 */
- msm_spi_set_bpw_and_no_io_flags(dd, &spi_config, bpw-1);
- /*
- * HS_MODE improves signal stability for spi-clk high rates
- * but is invalid in LOOPBACK mode.
- */
- if ((dd->clock_speed >= SPI_HS_MIN_RATE) &&
- !(dd->spi->mode & SPI_LOOP))
- spi_config |= SPI_CFG_HS_MODE;
- else
- spi_config &= ~SPI_CFG_HS_MODE;
- writel_relaxed(spi_config, dd->base + SPI_CONFIG);
- }
- /**
- * msm_spi_set_mx_counts: set SPI_MX_INPUT_COUNT and SPI_MX_INPUT_COUNT
- * for FIFO-mode. set SPI_MX_INPUT_COUNT and SPI_MX_OUTPUT_COUNT for
- * BAM and DMOV modes.
- * @n_words The number of reads/writes of size N.
- */
- static void msm_spi_set_mx_counts(struct msm_spi *dd, u32 n_words)
- {
- /*
- * For FIFO mode:
- * - Set the MX_OUTPUT_COUNT/MX_INPUT_COUNT registers to 0
- * - Set the READ/WRITE_COUNT registers to 0 (infinite mode)
- * or num bytes (finite mode) if less than fifo worth of data.
- * For Block mode:
- * - Set the MX_OUTPUT/MX_INPUT_COUNT registers to num xfer bytes.
- * - Set the READ/WRITE_COUNT registers to 0.
- */
- if (dd->tx_mode != SPI_BAM_MODE) {
- if (dd->tx_mode == SPI_FIFO_MODE) {
- if (n_words <= dd->input_fifo_size)
- msm_spi_set_write_count(dd, n_words);
- else
- msm_spi_set_write_count(dd, 0);
- writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
- } else
- writel_relaxed(n_words, dd->base + SPI_MX_OUTPUT_COUNT);
- if (dd->rx_mode == SPI_FIFO_MODE) {
- if (n_words <= dd->input_fifo_size)
- writel_relaxed(n_words,
- dd->base + SPI_MX_READ_COUNT);
- else
- writel_relaxed(0,
- dd->base + SPI_MX_READ_COUNT);
- writel_relaxed(0, dd->base + SPI_MX_INPUT_COUNT);
- } else
- writel_relaxed(n_words, dd->base + SPI_MX_INPUT_COUNT);
- } else {
- /* must be zero for BAM and DMOV */
- writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
- msm_spi_set_write_count(dd, 0);
- /*
- * for DMA transfers, both QUP_MX_INPUT_COUNT and
- * QUP_MX_OUTPUT_COUNT must be zero to all cases but one.
- * That case is a non-balanced transfer when there is
- * only a read_buf.
- */
- if (dd->qup_ver == SPI_QUP_VERSION_BFAM) {
- if (dd->write_buf)
- writel_relaxed(0,
- dd->base + SPI_MX_INPUT_COUNT);
- else
- writel_relaxed(n_words,
- dd->base + SPI_MX_INPUT_COUNT);
- writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
- }
- }
- }
- static int msm_spi_bam_pipe_disconnect(struct msm_spi *dd,
- struct msm_spi_bam_pipe *pipe)
- {
- int ret = sps_disconnect(pipe->handle);
- if (ret) {
- dev_dbg(dd->dev, "%s disconnect bam %s pipe failed\n",
- __func__, pipe->name);
- return ret;
- }
- return 0;
- }
- static int msm_spi_bam_pipe_connect(struct msm_spi *dd,
- struct msm_spi_bam_pipe *pipe, struct sps_connect *config)
- {
- int ret;
- struct sps_register_event event = {
- .mode = SPS_TRIGGER_WAIT,
- .options = SPS_O_EOT,
- };
- if (pipe == &dd->bam.prod)
- event.xfer_done = &dd->rx_transfer_complete;
- else if (pipe == &dd->bam.cons)
- event.xfer_done = &dd->tx_transfer_complete;
- ret = sps_connect(pipe->handle, config);
- if (ret) {
- dev_err(dd->dev, "%s: sps_connect(%s:0x%pK):%d",
- __func__, pipe->name, pipe->handle, ret);
- return ret;
- }
- ret = sps_register_event(pipe->handle, &event);
- if (ret) {
- dev_err(dd->dev, "%s sps_register_event(hndl:0x%pK %s):%d",
- __func__, pipe->handle, pipe->name, ret);
- msm_spi_bam_pipe_disconnect(dd, pipe);
- return ret;
- }
- pipe->teardown_required = true;
- return 0;
- }
- static void msm_spi_bam_pipe_flush(struct msm_spi *dd,
- enum msm_spi_pipe_direction pipe_dir)
- {
- struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
- (&dd->bam.prod) : (&dd->bam.cons);
- struct sps_connect config = pipe->config;
- int ret;
- ret = msm_spi_bam_pipe_disconnect(dd, pipe);
- if (ret)
- return;
- ret = msm_spi_bam_pipe_connect(dd, pipe, &config);
- if (ret)
- return;
- }
- static void msm_spi_bam_flush(struct msm_spi *dd)
- {
- dev_dbg(dd->dev, "%s flushing bam for recovery\n", __func__);
- msm_spi_bam_pipe_flush(dd, SPI_BAM_CONSUMER_PIPE);
- msm_spi_bam_pipe_flush(dd, SPI_BAM_PRODUCER_PIPE);
- }
- static int
- msm_spi_bam_process_rx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
- {
- int ret = 0;
- u32 data_xfr_size = 0, rem_bc = 0;
- u32 prod_flags = 0;
- rem_bc = dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd;
- data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
- /*
- * set flags for last descriptor only
- */
- if ((desc_cnt == 1)
- || (*bytes_to_send == data_xfr_size))
- prod_flags = (dd->write_buf)
- ? 0 : (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD);
- /*
- * enqueue read buffer in BAM
- */
- ret = sps_transfer_one(dd->bam.prod.handle,
- dd->cur_rx_transfer->rx_dma
- + dd->bam.curr_rx_bytes_recvd,
- data_xfr_size, dd, prod_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue producer BAM transfer",
- __func__);
- return ret;
- }
- dd->bam.curr_rx_bytes_recvd += data_xfr_size;
- *bytes_to_send -= data_xfr_size;
- dd->bam.bam_rx_len -= data_xfr_size;
- return data_xfr_size;
- }
- static int
- msm_spi_bam_process_tx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
- {
- int ret = 0;
- u32 data_xfr_size = 0, rem_bc = 0;
- u32 cons_flags = 0;
- rem_bc = dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent;
- data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
- /*
- * set flags for last descriptor only
- */
- if ((desc_cnt == 1)
- || (*bytes_to_send == data_xfr_size))
- cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
- /*
- * enqueue write buffer in BAM
- */
- ret = sps_transfer_one(dd->bam.cons.handle,
- dd->cur_tx_transfer->tx_dma
- + dd->bam.curr_tx_bytes_sent,
- data_xfr_size, dd, cons_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue consumer BAM transfer",
- __func__);
- return ret;
- }
- dd->bam.curr_tx_bytes_sent += data_xfr_size;
- *bytes_to_send -= data_xfr_size;
- dd->bam.bam_tx_len -= data_xfr_size;
- return data_xfr_size;
- }
- /**
- * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
- * using BAM.
- * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
- * transfer. Between transfer QUP must change to reset state. A loop is
- * issuing a single BAM transfer at a time.
- * @return zero on success
- */
- static int
- msm_spi_bam_begin_transfer(struct msm_spi *dd)
- {
- u32 tx_bytes_to_send = 0, rx_bytes_to_recv = 0;
- u32 n_words_xfr;
- s32 ret = 0;
- u32 prod_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
- u32 cons_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
- u32 byte_count = 0;
- rx_bytes_to_recv = min_t(u32, dd->bam.bam_rx_len,
- SPI_MAX_TRFR_BTWN_RESETS);
- tx_bytes_to_send = min_t(u32, dd->bam.bam_tx_len,
- SPI_MAX_TRFR_BTWN_RESETS);
- n_words_xfr = DIV_ROUND_UP(rx_bytes_to_recv,
- dd->bytes_per_word);
- msm_spi_set_mx_counts(dd, n_words_xfr);
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to set QUP state to run",
- __func__);
- goto xfr_err;
- }
- while ((rx_bytes_to_recv + tx_bytes_to_send) &&
- ((cons_desc_cnt + prod_desc_cnt) > 0)) {
- struct spi_transfer *t = NULL;
- if (dd->read_buf && (prod_desc_cnt > 0)) {
- ret = msm_spi_bam_process_rx(dd, &rx_bytes_to_recv,
- prod_desc_cnt);
- if (ret < 0)
- goto xfr_err;
- if (!(dd->cur_rx_transfer->len
- - dd->bam.curr_rx_bytes_recvd))
- t = dd->cur_rx_transfer;
- prod_desc_cnt--;
- }
- if (dd->write_buf && (cons_desc_cnt > 0)) {
- ret = msm_spi_bam_process_tx(dd, &tx_bytes_to_send,
- cons_desc_cnt);
- if (ret < 0)
- goto xfr_err;
- if (!(dd->cur_tx_transfer->len
- - dd->bam.curr_tx_bytes_sent))
- t = dd->cur_tx_transfer;
- cons_desc_cnt--;
- }
- byte_count += ret;
- }
- dd->tx_bytes_remaining -= min_t(u32, byte_count,
- SPI_MAX_TRFR_BTWN_RESETS);
- return 0;
- xfr_err:
- return ret;
- }
- static int
- msm_spi_bam_next_transfer(struct msm_spi *dd)
- {
- if (dd->tx_mode != SPI_BAM_MODE)
- return 0;
- if (dd->tx_bytes_remaining > 0) {
- if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
- return 0;
- if ((msm_spi_bam_begin_transfer(dd)) < 0) {
- dev_err(dd->dev, "%s: BAM transfer setup failed\n",
- __func__);
- return 0;
- }
- return 1;
- }
- return 0;
- }
- static int msm_spi_dma_send_next(struct msm_spi *dd)
- {
- int ret = 0;
- if (dd->tx_mode == SPI_BAM_MODE)
- ret = msm_spi_bam_next_transfer(dd);
- return ret;
- }
- static inline void msm_spi_ack_transfer(struct msm_spi *dd)
- {
- writel_relaxed(SPI_OP_MAX_INPUT_DONE_FLAG |
- SPI_OP_MAX_OUTPUT_DONE_FLAG,
- dd->base + SPI_OPERATIONAL);
- /* Ensure done flag was cleared before proceeding further */
- mb();
- }
- /* Figure which irq occurred and call the relevant functions */
- static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
- {
- u32 op, ret = IRQ_NONE;
- struct msm_spi *dd = dev_id;
- if (pm_runtime_suspended(dd->dev)) {
- dev_warn(dd->dev, "QUP: pm runtime suspend, irq:%d\n", irq);
- return ret;
- }
- if (readl_relaxed(dd->base + SPI_ERROR_FLAGS) ||
- readl_relaxed(dd->base + QUP_ERROR_FLAGS)) {
- struct spi_master *master = dev_get_drvdata(dd->dev);
- ret |= msm_spi_error_irq(irq, master);
- }
- op = readl_relaxed(dd->base + SPI_OPERATIONAL);
- writel_relaxed(op, dd->base + SPI_OPERATIONAL);
- /*
- * Ensure service flag was cleared before further
- * processing of interrupt.
- */
- mb();
- if (op & SPI_OP_INPUT_SERVICE_FLAG)
- ret |= msm_spi_input_irq(irq, dev_id);
- if (op & SPI_OP_OUTPUT_SERVICE_FLAG)
- ret |= msm_spi_output_irq(irq, dev_id);
- if (dd->tx_mode != SPI_BAM_MODE) {
- if (!dd->rx_done) {
- if (dd->rx_bytes_remaining == 0)
- dd->rx_done = true;
- }
- if (!dd->tx_done) {
- if (!dd->tx_bytes_remaining &&
- (op & SPI_OP_IP_FIFO_NOT_EMPTY)) {
- dd->tx_done = true;
- }
- }
- }
- if (dd->tx_done && dd->rx_done) {
- msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- dd->tx_done = false;
- dd->rx_done = false;
- complete(&dd->rx_transfer_complete);
- complete(&dd->tx_transfer_complete);
- }
- return ret;
- }
- static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
- {
- struct msm_spi *dd = dev_id;
- dd->stat_rx++;
- if (dd->rx_mode == SPI_MODE_NONE)
- return IRQ_HANDLED;
- if (dd->rx_mode == SPI_FIFO_MODE) {
- while ((readl_relaxed(dd->base + SPI_OPERATIONAL) &
- SPI_OP_IP_FIFO_NOT_EMPTY) &&
- (dd->rx_bytes_remaining > 0)) {
- msm_spi_read_word_from_fifo(dd);
- }
- } else if (dd->rx_mode == SPI_BLOCK_MODE) {
- int count = 0;
- while (dd->rx_bytes_remaining &&
- (count < dd->input_block_size)) {
- msm_spi_read_word_from_fifo(dd);
- count += SPI_MAX_BYTES_PER_WORD;
- }
- }
- return IRQ_HANDLED;
- }
- static void msm_spi_write_word_to_fifo(struct msm_spi *dd)
- {
- u32 word;
- u8 byte;
- int i;
- int write_bytes =
- (dd->pack_words ? SPI_MAX_BYTES_PER_WORD : dd->bytes_per_word);
- word = 0;
- if (dd->write_buf) {
- for (i = 0; (i < write_bytes) &&
- dd->tx_bytes_remaining; i++) {
- dd->tx_bytes_remaining--;
- byte = *dd->write_buf++;
- word |= (byte << (BITS_PER_BYTE * i));
- }
- } else
- if (dd->tx_bytes_remaining > write_bytes)
- dd->tx_bytes_remaining -= write_bytes;
- else
- dd->tx_bytes_remaining = 0;
- dd->write_xfr_cnt++;
- writel_relaxed(word, dd->base + SPI_OUTPUT_FIFO);
- }
- static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd)
- {
- int count = 0;
- if (dd->tx_mode == SPI_FIFO_MODE) {
- while ((dd->tx_bytes_remaining > 0) &&
- (count < dd->input_fifo_size) &&
- !(readl_relaxed(dd->base + SPI_OPERATIONAL)
- & SPI_OP_OUTPUT_FIFO_FULL)) {
- msm_spi_write_word_to_fifo(dd);
- count++;
- }
- }
- if (dd->tx_mode == SPI_BLOCK_MODE) {
- while (dd->tx_bytes_remaining &&
- (count < dd->output_block_size)) {
- msm_spi_write_word_to_fifo(dd);
- count += SPI_MAX_BYTES_PER_WORD;
- }
- }
- }
- static irqreturn_t msm_spi_output_irq(int irq, void *dev_id)
- {
- struct msm_spi *dd = dev_id;
- dd->stat_tx++;
- if (dd->tx_mode == SPI_MODE_NONE)
- return IRQ_HANDLED;
- /* Output FIFO is empty. Transmit any outstanding write data. */
- if ((dd->tx_mode == SPI_FIFO_MODE) || (dd->tx_mode == SPI_BLOCK_MODE))
- msm_spi_write_rmn_to_fifo(dd);
- return IRQ_HANDLED;
- }
- static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
- {
- struct spi_master *master = dev_id;
- struct msm_spi *dd = spi_master_get_devdata(master);
- u32 spi_err;
- spi_err = readl_relaxed(dd->base + SPI_ERROR_FLAGS);
- if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR)
- dev_warn(master->dev.parent, "SPI output overrun error\n");
- if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR)
- dev_warn(master->dev.parent, "SPI input underrun error\n");
- if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR)
- dev_warn(master->dev.parent, "SPI output underrun error\n");
- msm_spi_get_clk_err(dd, &spi_err);
- if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR)
- dev_warn(master->dev.parent, "SPI clock overrun error\n");
- if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR)
- dev_warn(master->dev.parent, "SPI clock underrun error\n");
- msm_spi_clear_error_flags(dd);
- msm_spi_ack_clk_err(dd);
- /* Ensure clearing of QUP_ERROR_FLAGS was completed */
- mb();
- return IRQ_HANDLED;
- }
- static int msm_spi_bam_map_buffers(struct msm_spi *dd)
- {
- int ret = -EINVAL;
- struct device *dev;
- struct spi_transfer *xfr;
- void *tx_buf, *rx_buf;
- u32 tx_len, rx_len;
- dev = dd->dev;
- xfr = dd->cur_transfer;
- tx_buf = (void *)xfr->tx_buf;
- rx_buf = xfr->rx_buf;
- tx_len = rx_len = xfr->len;
- if (tx_buf != NULL) {
- xfr->tx_dma = dma_map_single(dev, tx_buf,
- tx_len, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, xfr->tx_dma)) {
- ret = -ENOMEM;
- goto error;
- }
- }
- if (rx_buf != NULL) {
- xfr->rx_dma = dma_map_single(dev, rx_buf, rx_len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, xfr->rx_dma)) {
- if (tx_buf != NULL)
- dma_unmap_single(dev,
- xfr->tx_dma,
- tx_len, DMA_TO_DEVICE);
- ret = -ENOMEM;
- goto error;
- }
- }
- return 0;
- error:
- msm_spi_dma_unmap_buffers(dd);
- return ret;
- }
- static int msm_spi_dma_map_buffers(struct msm_spi *dd)
- {
- int ret = 0;
- if (dd->tx_mode == SPI_BAM_MODE)
- ret = msm_spi_bam_map_buffers(dd);
- return ret;
- }
- static void msm_spi_bam_unmap_buffers(struct msm_spi *dd)
- {
- struct device *dev;
- struct spi_transfer *xfr;
- void *tx_buf, *rx_buf;
- u32 tx_len, rx_len;
- dev = dd->dev;
- xfr = dd->cur_transfer;
- tx_buf = (void *)xfr->tx_buf;
- rx_buf = xfr->rx_buf;
- tx_len = rx_len = xfr->len;
- if (tx_buf != NULL)
- dma_unmap_single(dev, xfr->tx_dma,
- tx_len, DMA_TO_DEVICE);
- if (rx_buf != NULL)
- dma_unmap_single(dev, xfr->rx_dma,
- rx_len, DMA_FROM_DEVICE);
- }
- static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd)
- {
- if (dd->tx_mode == SPI_BAM_MODE)
- msm_spi_bam_unmap_buffers(dd);
- }
- /**
- * msm_spi_use_dma - decides whether to use Data-Mover or BAM for
- * the given transfer
- * @dd: device
- * @tr: transfer
- *
- * Start using DMA if:
- * 1. Is supported by HW
- * 2. Is not diabled by platform data
- * 3. Transfer size is greater than 3*block size.
- * 4. Buffers are aligned to cache line.
- * 5. Bytes-per-word is 8,16 or 32.
- */
- static inline bool
- msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
- {
- if (!dd->use_dma)
- return false;
- /* check constraints from platform data */
- if ((dd->qup_ver == SPI_QUP_VERSION_BFAM) && !dd->pdata->use_bam)
- return false;
- if (dd->cur_msg_len < 3*dd->input_block_size)
- return false;
- if ((dd->qup_ver != SPI_QUP_VERSION_BFAM) &&
- !dd->read_len && !dd->write_len)
- return false;
- if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
- u32 cache_line = dma_get_cache_alignment();
- if (tr->tx_buf) {
- if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
- return 0;
- }
- if (tr->rx_buf) {
- if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
- return false;
- }
- if (tr->cs_change &&
- ((bpw != 8) && (bpw != 16) && (bpw != 32)))
- return false;
- }
- return true;
- }
- /**
- * msm_spi_set_transfer_mode: Chooses optimal transfer mode. Sets dd->mode and
- * prepares to process a transfer.
- */
- static void
- msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count)
- {
- if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
- dd->tx_mode = SPI_BAM_MODE;
- dd->rx_mode = SPI_BAM_MODE;
- } else {
- dd->rx_mode = SPI_FIFO_MODE;
- dd->tx_mode = SPI_FIFO_MODE;
- dd->read_len = dd->cur_transfer->len;
- dd->write_len = dd->cur_transfer->len;
- }
- }
- /**
- * msm_spi_set_qup_io_modes: prepares register QUP_IO_MODES to process a
- * transfer
- */
- static void msm_spi_set_qup_io_modes(struct msm_spi *dd)
- {
- u32 spi_iom;
- spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
- /* Set input and output transfer mode: FIFO, DMOV, or BAM */
- spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
- spi_iom = (spi_iom | (dd->tx_mode << OUTPUT_MODE_SHIFT));
- spi_iom = (spi_iom | (dd->rx_mode << INPUT_MODE_SHIFT));
- /* Always enable packing for the BAM mode and for non BAM mode only
- * if bpw is % 8 and transfer length is % 4 Bytes.
- */
- if (dd->tx_mode == SPI_BAM_MODE ||
- ((dd->cur_msg_len % SPI_MAX_BYTES_PER_WORD == 0) &&
- (dd->cur_transfer->bits_per_word) &&
- (dd->cur_transfer->bits_per_word <= 32) &&
- (dd->cur_transfer->bits_per_word % 8 == 0))) {
- spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
- dd->pack_words = true;
- } else {
- spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
- spi_iom |= SPI_IO_M_OUTPUT_BIT_SHIFT_EN;
- dd->pack_words = false;
- }
- writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
- }
- static u32 msm_spi_calc_spi_ioc_clk_polarity(u32 spi_ioc, u8 mode)
- {
- if (mode & SPI_CPOL)
- spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
- else
- spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
- return spi_ioc;
- }
- /**
- * msm_spi_set_spi_io_control: prepares register SPI_IO_CONTROL to process the
- * next transfer
- * @return the new set value of SPI_IO_CONTROL
- */
- static u32 msm_spi_set_spi_io_control(struct msm_spi *dd)
- {
- u32 spi_ioc, spi_ioc_orig, chip_select;
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- spi_ioc_orig = spi_ioc;
- spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc
- , dd->spi->mode);
- /* Set chip-select */
- chip_select = dd->spi->chip_select << 2;
- if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
- spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
- if (!dd->cur_transfer->cs_change)
- spi_ioc |= SPI_IO_C_MX_CS_MODE;
- if (spi_ioc != spi_ioc_orig)
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
- /*
- * Ensure that the IO control mode register gets written
- * before proceeding with the transfer.
- */
- mb();
- return spi_ioc;
- }
- /**
- * msm_spi_set_qup_op_mask: prepares register QUP_OPERATIONAL_MASK to process
- * the next transfer
- */
- static void msm_spi_set_qup_op_mask(struct msm_spi *dd)
- {
- /* mask INPUT and OUTPUT service flags in to prevent IRQs on FIFO status
- * change in BAM mode
- */
- u32 mask = (dd->tx_mode == SPI_BAM_MODE) ?
- QUP_OP_MASK_OUTPUT_SERVICE_FLAG | QUP_OP_MASK_INPUT_SERVICE_FLAG
- : 0;
- writel_relaxed(mask, dd->base + QUP_OPERATIONAL_MASK);
- }
- static void get_transfer_length(struct msm_spi *dd)
- {
- struct spi_transfer *xfer = dd->cur_transfer;
- dd->cur_msg_len = 0;
- dd->read_len = dd->write_len = 0;
- dd->bam.bam_tx_len = dd->bam.bam_rx_len = 0;
- if (xfer->tx_buf)
- dd->bam.bam_tx_len = dd->write_len = xfer->len;
- if (xfer->rx_buf)
- dd->bam.bam_rx_len = dd->read_len = xfer->len;
- dd->cur_msg_len = xfer->len;
- }
- static int msm_spi_process_transfer(struct msm_spi *dd)
- {
- u8 bpw;
- u32 max_speed;
- u32 read_count;
- u32 timeout;
- u32 spi_ioc;
- u32 int_loopback = 0;
- int ret;
- int status = 0;
- get_transfer_length(dd);
- dd->cur_tx_transfer = dd->cur_transfer;
- dd->cur_rx_transfer = dd->cur_transfer;
- dd->bam.curr_rx_bytes_recvd = dd->bam.curr_tx_bytes_sent = 0;
- dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
- dd->tx_bytes_remaining = dd->cur_msg_len;
- dd->rx_bytes_remaining = dd->cur_msg_len;
- dd->read_buf = dd->cur_transfer->rx_buf;
- dd->write_buf = dd->cur_transfer->tx_buf;
- dd->tx_done = false;
- dd->rx_done = false;
- init_completion(&dd->tx_transfer_complete);
- init_completion(&dd->rx_transfer_complete);
- if (dd->cur_transfer->bits_per_word)
- bpw = dd->cur_transfer->bits_per_word;
- else
- bpw = 8;
- dd->bytes_per_word = (bpw + 7) / 8;
- if (dd->cur_transfer->speed_hz)
- max_speed = dd->cur_transfer->speed_hz;
- else
- max_speed = dd->spi->max_speed_hz;
- if (!dd->clock_speed || max_speed != dd->clock_speed)
- msm_spi_clock_set(dd, max_speed);
- timeout = 100 * msecs_to_jiffies(
- DIV_ROUND_UP(dd->cur_msg_len * 8,
- DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
- read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
- if (dd->spi->mode & SPI_LOOP)
- int_loopback = 1;
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Error setting QUP to reset-state",
- __func__);
- return ret;
- }
- msm_spi_set_transfer_mode(dd, bpw, read_count);
- msm_spi_set_mx_counts(dd, read_count);
- if (dd->tx_mode == SPI_BAM_MODE) {
- ret = msm_spi_dma_map_buffers(dd);
- if (ret < 0) {
- pr_err("Mapping DMA buffers\n");
- dd->tx_mode = SPI_MODE_NONE;
- dd->rx_mode = SPI_MODE_NONE;
- return ret;
- }
- }
- msm_spi_set_qup_io_modes(dd);
- msm_spi_set_spi_config(dd, bpw);
- msm_spi_set_qup_config(dd, bpw);
- spi_ioc = msm_spi_set_spi_io_control(dd);
- msm_spi_set_qup_op_mask(dd);
- /* The output fifo interrupt handler will handle all writes after
- * the first. Restricting this to one write avoids contention
- * issues and race conditions between this thread and the int handler
- */
- if (dd->tx_mode != SPI_BAM_MODE) {
- if (msm_spi_prepare_for_write(dd))
- goto transfer_end;
- msm_spi_start_write(dd, read_count);
- } else {
- if ((msm_spi_bam_begin_transfer(dd)) < 0) {
- dev_err(dd->dev, "%s: BAM transfer setup failed\n",
- __func__);
- status = -EIO;
- goto transfer_end;
- }
- }
- /*
- * On BAM mode, current state here is run.
- * Only enter the RUN state after the first word is written into
- * the output FIFO. Otherwise, the output FIFO EMPTY interrupt
- * might fire before the first word is written resulting in a
- * possible race condition.
- */
- if (dd->tx_mode != SPI_BAM_MODE)
- if (msm_spi_set_state(dd, SPI_OP_STATE_RUN)) {
- dev_warn(dd->dev,
- "%s: Failed to set QUP to run-state. Mode:%d",
- __func__, dd->tx_mode);
- goto transfer_end;
- }
- /* Assume success, this might change later upon transaction result */
- do {
- if (dd->write_buf &&
- !wait_for_completion_timeout(&dd->tx_transfer_complete,
- timeout)) {
- dev_err(dd->dev, "%s: SPI Tx transaction timeout\n",
- __func__);
- status = -EIO;
- break;
- }
- if (dd->read_buf &&
- !wait_for_completion_timeout(&dd->rx_transfer_complete,
- timeout)) {
- dev_err(dd->dev, "%s: SPI Rx transaction timeout\n",
- __func__);
- status = -EIO;
- break;
- }
- } while (msm_spi_dma_send_next(dd));
- msm_spi_udelay(dd->xfrs_delay_usec);
- transfer_end:
- if ((dd->tx_mode == SPI_BAM_MODE) && status)
- msm_spi_bam_flush(dd);
- msm_spi_dma_unmap_buffers(dd);
- dd->tx_mode = SPI_MODE_NONE;
- dd->rx_mode = SPI_MODE_NONE;
- msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (!dd->cur_transfer->cs_change)
- writel_relaxed(spi_ioc & ~SPI_IO_C_MX_CS_MODE,
- dd->base + SPI_IO_CONTROL);
- return status;
- }
- static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
- {
- struct msm_spi *dd = spi_master_get_devdata(spi->master);
- u32 spi_ioc;
- u32 spi_ioc_orig;
- int rc = 0;
- rc = pm_runtime_get_sync(dd->dev);
- if (rc < 0) {
- dev_err(dd->dev, "Failure during runtime get,rc=%d", rc);
- return;
- }
- if (dd->pdata->is_shared) {
- rc = get_local_resources(dd);
- if (rc)
- return;
- }
- msm_spi_clk_path_vote(dd, spi->max_speed_hz);
- if (!(spi->mode & SPI_CS_HIGH))
- set_flag = !set_flag;
- /* Serve only under mutex lock as RT suspend may cause a race */
- mutex_lock(&dd->core_lock);
- if (dd->suspended) {
- dev_err(dd->dev, "%s: SPI operational state=%d Invalid\n",
- __func__, dd->suspended);
- mutex_unlock(&dd->core_lock);
- return;
- }
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- spi_ioc_orig = spi_ioc;
- if (set_flag)
- spi_ioc |= SPI_IO_C_FORCE_CS;
- else
- spi_ioc &= ~SPI_IO_C_FORCE_CS;
- if (spi_ioc != spi_ioc_orig)
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
- if (dd->pdata->is_shared)
- put_local_resources(dd);
- mutex_unlock(&dd->core_lock);
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
- }
- static void reset_core(struct msm_spi *dd)
- {
- u32 spi_ioc;
- msm_spi_register_init(dd);
- /*
- * The SPI core generates a bogus input overrun error on some targets,
- * when a transition from run to reset state occurs and if the FIFO has
- * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
- * bit.
- */
- msm_spi_enable_error_flags(dd);
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- spi_ioc |= SPI_IO_C_NO_TRI_STATE;
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
- /*
- * Ensure that the IO control is written to before returning.
- */
- mb();
- msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- }
- static void put_local_resources(struct msm_spi *dd)
- {
- if (IS_ERR_OR_NULL(dd->clk) || IS_ERR_OR_NULL(dd->pclk)) {
- dev_err(dd->dev,
- "%s: error clk put\n",
- __func__);
- return;
- }
- msm_spi_disable_irqs(dd);
- clk_disable_unprepare(dd->clk);
- dd->clock_speed = 0;
- clk_disable_unprepare(dd->pclk);
- /* Free the spi clk, miso, mosi, cs gpio */
- if (dd->pdata && dd->pdata->gpio_release)
- dd->pdata->gpio_release();
- msm_spi_free_gpios(dd);
- }
- static int get_local_resources(struct msm_spi *dd)
- {
- int ret = -EINVAL;
- if (IS_ERR_OR_NULL(dd->clk) || IS_ERR_OR_NULL(dd->pclk)) {
- dev_err(dd->dev,
- "%s: error clk put\n",
- __func__);
- return ret;
- }
- /* Configure the spi clk, miso, mosi and cs gpio */
- if (dd->pdata->gpio_config) {
- ret = dd->pdata->gpio_config();
- if (ret) {
- dev_err(dd->dev,
- "%s: error configuring GPIOs\n",
- __func__);
- return ret;
- }
- }
- ret = msm_spi_request_gpios(dd);
- if (ret)
- return ret;
- ret = clk_prepare_enable(dd->clk);
- if (ret)
- goto clk0_err;
- ret = clk_prepare_enable(dd->pclk);
- if (ret)
- goto clk1_err;
- msm_spi_enable_irqs(dd);
- return 0;
- clk1_err:
- clk_disable_unprepare(dd->clk);
- clk0_err:
- msm_spi_free_gpios(dd);
- return ret;
- }
- /**
- * msm_spi_transfer_one: To process one spi transfer at a time
- * @master: spi master controller reference
- * @msg: one multi-segment SPI transaction
- * @return zero on success or negative error value
- *
- */
- static int msm_spi_transfer_one(struct spi_master *master,
- struct spi_device *spi,
- struct spi_transfer *xfer)
- {
- struct msm_spi *dd;
- unsigned long flags;
- u32 status_error = 0;
- dd = spi_master_get_devdata(master);
- /* Check message parameters */
- if (xfer->speed_hz > dd->pdata->max_clock_speed ||
- (xfer->bits_per_word &&
- (xfer->bits_per_word < 4 || xfer->bits_per_word > 32)) ||
- (xfer->tx_buf == NULL && xfer->rx_buf == NULL)) {
- dev_err(dd->dev,
- "Invalid transfer: %d Hz, %d bpw tx=%pK, rx=%pK\n",
- xfer->speed_hz, xfer->bits_per_word,
- xfer->tx_buf, xfer->rx_buf);
- return -EINVAL;
- }
- dd->spi = spi;
- dd->cur_transfer = xfer;
- mutex_lock(&dd->core_lock);
- spin_lock_irqsave(&dd->queue_lock, flags);
- dd->transfer_pending = 1;
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- /*
- * get local resources for each transfer to ensure we're in a good
- * state and not interfering with other EE's using this device
- */
- if (dd->pdata->is_shared) {
- if (get_local_resources(dd)) {
- mutex_unlock(&dd->core_lock);
- spi_finalize_current_message(master);
- return -EINVAL;
- }
- reset_core(dd);
- if (dd->use_dma) {
- msm_spi_bam_pipe_connect(dd, &dd->bam.prod,
- &dd->bam.prod.config);
- msm_spi_bam_pipe_connect(dd, &dd->bam.cons,
- &dd->bam.cons.config);
- }
- }
- if (dd->suspended || !msm_spi_is_valid_state(dd)) {
- dev_err(dd->dev, "%s: SPI operational state not valid\n",
- __func__);
- status_error = 1;
- }
- if (!status_error)
- status_error =
- msm_spi_process_transfer(dd);
- spin_lock_irqsave(&dd->queue_lock, flags);
- dd->transfer_pending = 0;
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- /*
- * Put local resources prior to calling finalize to ensure the hw
- * is in a known state before notifying the calling thread (which is a
- * different context since we're running in the spi kthread here) to
- * prevent race conditions between us and any other EE's using this hw.
- */
- if (dd->pdata->is_shared) {
- if (dd->use_dma) {
- msm_spi_bam_pipe_disconnect(dd, &dd->bam.prod);
- msm_spi_bam_pipe_disconnect(dd, &dd->bam.cons);
- }
- put_local_resources(dd);
- }
- mutex_unlock(&dd->core_lock);
- if (dd->suspended)
- wake_up_interruptible(&dd->continue_suspend);
- return status_error;
- }
- static int msm_spi_prepare_transfer_hardware(struct spi_master *master)
- {
- struct msm_spi *dd = spi_master_get_devdata(master);
- int resume_state = 0;
- resume_state = pm_runtime_get_sync(dd->dev);
- if (resume_state < 0)
- goto spi_finalize;
- /*
- * Counter-part of system-suspend when runtime-pm is not enabled.
- * This way, resume can be left empty and device will be put in
- * active mode only if client requests anything on the bus
- */
- if (!pm_runtime_enabled(dd->dev))
- resume_state = msm_spi_pm_resume_runtime(dd->dev);
- if (resume_state < 0)
- goto spi_finalize;
- if (dd->suspended) {
- resume_state = -EBUSY;
- goto spi_finalize;
- }
- return 0;
- spi_finalize:
- spi_finalize_current_message(master);
- return resume_state;
- }
- static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
- {
- struct msm_spi *dd = spi_master_get_devdata(master);
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
- return 0;
- }
- static int msm_spi_setup(struct spi_device *spi)
- {
- struct msm_spi *dd;
- int rc = 0;
- u32 spi_ioc;
- u32 spi_config;
- u32 mask;
- if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
- dev_err(&spi->dev, "%s: invalid bits_per_word %d\n",
- __func__, spi->bits_per_word);
- return -EINVAL;
- }
- if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) {
- dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n",
- __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1);
- return -EINVAL;
- }
- dd = spi_master_get_devdata(spi->master);
- rc = pm_runtime_get_sync(dd->dev);
- if (rc < 0 && !dd->is_init_complete &&
- pm_runtime_enabled(dd->dev)) {
- pm_runtime_set_suspended(dd->dev);
- pm_runtime_put_sync(dd->dev);
- rc = 0;
- goto err_setup_exit;
- } else
- rc = 0;
- mutex_lock(&dd->core_lock);
- /* Counter-part of system-suspend when runtime-pm is not enabled. */
- if (!pm_runtime_enabled(dd->dev)) {
- rc = msm_spi_pm_resume_runtime(dd->dev);
- if (rc < 0 && !dd->is_init_complete) {
- rc = 0;
- mutex_unlock(&dd->core_lock);
- goto err_setup_exit;
- }
- }
- if (dd->suspended) {
- rc = -EBUSY;
- mutex_unlock(&dd->core_lock);
- goto err_setup_exit;
- }
- if (dd->pdata->is_shared) {
- rc = get_local_resources(dd);
- if (rc)
- goto no_resources;
- }
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
- if (spi->mode & SPI_CS_HIGH)
- spi_ioc |= mask;
- else
- spi_ioc &= ~mask;
- spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc, spi->mode);
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
- spi_config = readl_relaxed(dd->base + SPI_CONFIG);
- spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
- spi_config, spi->mode);
- writel_relaxed(spi_config, dd->base + SPI_CONFIG);
- /* Ensure previous write completed before disabling the clocks */
- mb();
- if (dd->pdata->is_shared)
- put_local_resources(dd);
- /* Counter-part of system-resume when runtime-pm is not enabled. */
- if (!pm_runtime_enabled(dd->dev))
- msm_spi_pm_suspend_runtime(dd->dev);
- no_resources:
- mutex_unlock(&dd->core_lock);
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
- err_setup_exit:
- return rc;
- }
- #ifdef CONFIG_DEBUG_FS
- static int debugfs_iomem_x32_set(void *data, u64 val)
- {
- struct msm_spi_debugfs_data *reg = (struct msm_spi_debugfs_data *)data;
- struct msm_spi *dd = reg->dd;
- int ret;
- ret = pm_runtime_get_sync(dd->dev);
- if (ret < 0)
- return ret;
- writel_relaxed(val, (dd->base + reg->offset));
- /* Ensure the previous write completed. */
- mb();
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
- return 0;
- }
- static int debugfs_iomem_x32_get(void *data, u64 *val)
- {
- struct msm_spi_debugfs_data *reg = (struct msm_spi_debugfs_data *)data;
- struct msm_spi *dd = reg->dd;
- int ret;
- ret = pm_runtime_get_sync(dd->dev);
- if (ret < 0)
- return ret;
- *val = readl_relaxed(dd->base + reg->offset);
- /* Ensure the previous read completed. */
- mb();
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
- debugfs_iomem_x32_set, "0x%08llx\n");
- static void spi_debugfs_init(struct msm_spi *dd)
- {
- char dir_name[20];
- scnprintf(dir_name, sizeof(dir_name), "%s_dbg", dev_name(dd->dev));
- dd->dent_spi = debugfs_create_dir(dir_name, NULL);
- if (dd->dent_spi) {
- int i;
- for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) {
- dd->reg_data[i].offset = debugfs_spi_regs[i].offset;
- dd->reg_data[i].dd = dd;
- dd->debugfs_spi_regs[i] =
- debugfs_create_file(
- debugfs_spi_regs[i].name,
- debugfs_spi_regs[i].mode,
- dd->dent_spi, &dd->reg_data[i],
- &fops_iomem_x32);
- }
- }
- }
- static void spi_debugfs_exit(struct msm_spi *dd)
- {
- if (dd->dent_spi) {
- int i;
- debugfs_remove_recursive(dd->dent_spi);
- dd->dent_spi = NULL;
- for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++)
- dd->debugfs_spi_regs[i] = NULL;
- }
- }
- #else
- static void spi_debugfs_init(struct msm_spi *dd) {}
- static void spi_debugfs_exit(struct msm_spi *dd) {}
- #endif
- /* ===Device attributes begin=== */
- static ssize_t show_stats(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct spi_master *master = dev_get_drvdata(dev);
- struct msm_spi *dd = spi_master_get_devdata(master);
- return snprintf(buf, PAGE_SIZE,
- "Device %s\n"
- "rx fifo_size = %d spi words\n"
- "tx fifo_size = %d spi words\n"
- "use_dma ? %s\n"
- "rx block size = %d bytes\n"
- "tx block size = %d bytes\n"
- "input burst size = %d bytes\n"
- "output burst size = %d bytes\n"
- "DMA configuration:\n"
- "tx_ch=%d, rx_ch=%d, tx_crci= %d, rx_crci=%d\n"
- "--statistics--\n"
- "Rx isrs = %d\n"
- "Tx isrs = %d\n"
- "--debug--\n"
- "NA yet\n",
- dev_name(dev),
- dd->input_fifo_size,
- dd->output_fifo_size,
- dd->use_dma ? "yes" : "no",
- dd->input_block_size,
- dd->output_block_size,
- dd->input_burst_size,
- dd->output_burst_size,
- dd->tx_dma_chan,
- dd->rx_dma_chan,
- dd->tx_dma_crci,
- dd->rx_dma_crci,
- dd->stat_rx,
- dd->stat_tx
- );
- }
- /* Reset statistics on write */
- static ssize_t set_stats(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct msm_spi *dd = dev_get_drvdata(dev);
- dd->stat_rx = 0;
- dd->stat_tx = 0;
- return count;
- }
- static DEVICE_ATTR(stats, 0644, show_stats, set_stats);
- static struct attribute *dev_attrs[] = {
- &dev_attr_stats.attr,
- NULL,
- };
- static struct attribute_group dev_attr_grp = {
- .attrs = dev_attrs,
- };
- /* ===Device attributes end=== */
- static void msm_spi_bam_pipe_teardown(struct msm_spi *dd,
- enum msm_spi_pipe_direction pipe_dir)
- {
- struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
- (&dd->bam.prod) : (&dd->bam.cons);
- if (!pipe->teardown_required)
- return;
- msm_spi_bam_pipe_disconnect(dd, pipe);
- dma_free_coherent(dd->dev, pipe->config.desc.size,
- pipe->config.desc.base, pipe->config.desc.phys_base);
- sps_free_endpoint(pipe->handle);
- pipe->handle = NULL;
- pipe->teardown_required = false;
- }
- static int msm_spi_bam_pipe_init(struct msm_spi *dd,
- enum msm_spi_pipe_direction pipe_dir)
- {
- int rc = 0;
- struct sps_pipe *pipe_handle;
- struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
- (&dd->bam.prod) : (&dd->bam.cons);
- struct sps_connect *pipe_conf = &pipe->config;
- pipe->name = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ? "cons" : "prod";
- pipe->handle = NULL;
- pipe_handle = sps_alloc_endpoint();
- if (!pipe_handle) {
- dev_err(dd->dev, "%s: Failed to allocate BAM endpoint\n"
- , __func__);
- return -ENOMEM;
- }
- memset(pipe_conf, 0, sizeof(*pipe_conf));
- rc = sps_get_config(pipe_handle, pipe_conf);
- if (rc) {
- dev_err(dd->dev, "%s: Failed to get BAM pipe config\n"
- , __func__);
- goto config_err;
- }
- if (pipe_dir == SPI_BAM_CONSUMER_PIPE) {
- pipe_conf->source = dd->bam.handle;
- pipe_conf->destination = SPS_DEV_HANDLE_MEM;
- pipe_conf->mode = SPS_MODE_SRC;
- pipe_conf->src_pipe_index =
- dd->pdata->bam_producer_pipe_index;
- pipe_conf->dest_pipe_index = 0;
- } else {
- pipe_conf->source = SPS_DEV_HANDLE_MEM;
- pipe_conf->destination = dd->bam.handle;
- pipe_conf->mode = SPS_MODE_DEST;
- pipe_conf->src_pipe_index = 0;
- pipe_conf->dest_pipe_index =
- dd->pdata->bam_consumer_pipe_index;
- }
- pipe_conf->options = SPS_O_EOT | SPS_O_AUTO_ENABLE;
- pipe_conf->desc.size = SPI_BAM_MAX_DESC_NUM * sizeof(struct sps_iovec);
- pipe_conf->desc.base = dma_alloc_coherent(dd->dev,
- pipe_conf->desc.size,
- &pipe_conf->desc.phys_base,
- GFP_KERNEL);
- if (!pipe_conf->desc.base) {
- dev_err(dd->dev, "%s: Failed allocate BAM pipe memory"
- , __func__);
- rc = -ENOMEM;
- goto config_err;
- }
- /* zero descriptor FIFO for convenient debugging of first descs */
- memset(pipe_conf->desc.base, 0x00, pipe_conf->desc.size);
- pipe->handle = pipe_handle;
- return 0;
- config_err:
- sps_free_endpoint(pipe_handle);
- return rc;
- }
- static void msm_spi_bam_teardown(struct msm_spi *dd)
- {
- msm_spi_bam_pipe_teardown(dd, SPI_BAM_PRODUCER_PIPE);
- msm_spi_bam_pipe_teardown(dd, SPI_BAM_CONSUMER_PIPE);
- if (dd->bam.deregister_required) {
- sps_deregister_bam_device(dd->bam.handle);
- dd->bam.deregister_required = false;
- }
- }
- static int msm_spi_bam_init(struct msm_spi *dd)
- {
- struct sps_bam_props bam_props = {0};
- uintptr_t bam_handle;
- int rc = 0;
- rc = sps_phy2h(dd->bam.phys_addr, &bam_handle);
- if (rc || !bam_handle) {
- bam_props.phys_addr = dd->bam.phys_addr;
- bam_props.virt_addr = dd->bam.base;
- bam_props.irq = dd->bam.irq;
- bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
- bam_props.summing_threshold = 0x10;
- rc = sps_register_bam_device(&bam_props, &bam_handle);
- if (rc) {
- dev_err(dd->dev,
- "%s: Failed to register BAM device",
- __func__);
- return rc;
- }
- dd->bam.deregister_required = true;
- }
- dd->bam.handle = bam_handle;
- rc = msm_spi_bam_pipe_init(dd, SPI_BAM_PRODUCER_PIPE);
- if (rc) {
- dev_err(dd->dev,
- "%s: Failed to init producer BAM-pipe",
- __func__);
- goto bam_init_error;
- }
- rc = msm_spi_bam_pipe_init(dd, SPI_BAM_CONSUMER_PIPE);
- if (rc) {
- dev_err(dd->dev,
- "%s: Failed to init consumer BAM-pipe",
- __func__);
- goto bam_init_error;
- }
- return 0;
- bam_init_error:
- msm_spi_bam_teardown(dd);
- return rc;
- }
- enum msm_spi_dt_entry_status {
- DT_REQ, /* Required: fail if missing */
- DT_SGST, /* Suggested: warn if missing */
- DT_OPT, /* Optional: don't warn if missing */
- };
- enum msm_spi_dt_entry_type {
- DT_U32,
- DT_GPIO,
- DT_BOOL,
- };
- struct msm_spi_dt_to_pdata_map {
- const char *dt_name;
- void *ptr_data;
- enum msm_spi_dt_entry_status status;
- enum msm_spi_dt_entry_type type;
- int default_val;
- };
- static int msm_spi_dt_to_pdata_populate(struct platform_device *pdev,
- struct msm_spi_platform_data *pdata,
- struct msm_spi_dt_to_pdata_map *itr)
- {
- int ret, err = 0;
- struct device_node *node = pdev->dev.of_node;
- for (; itr->dt_name; ++itr) {
- switch (itr->type) {
- case DT_GPIO:
- ret = of_get_named_gpio(node, itr->dt_name, 0);
- if (ret >= 0) {
- *((int *) itr->ptr_data) = ret;
- ret = 0;
- }
- break;
- case DT_U32:
- ret = of_property_read_u32(node, itr->dt_name,
- (u32 *) itr->ptr_data);
- break;
- case DT_BOOL:
- *((bool *) itr->ptr_data) =
- of_property_read_bool(node, itr->dt_name);
- ret = 0;
- break;
- default:
- dev_err(&pdev->dev, "%d is an unknown DT entry type\n",
- itr->type);
- ret = -EBADE;
- }
- dev_dbg(&pdev->dev, "DT entry ret:%d name:%s val:%d\n",
- ret, itr->dt_name, *((int *)itr->ptr_data));
- if (ret) {
- *((int *)itr->ptr_data) = itr->default_val;
- if (itr->status < DT_OPT) {
- dev_err(&pdev->dev, "Missing '%s' DT entry\n",
- itr->dt_name);
- /* cont on err to dump all missing entries */
- if (itr->status == DT_REQ && !err)
- err = ret;
- }
- }
- }
- return err;
- }
- /**
- * msm_spi_dt_to_pdata: create pdata and read gpio config from device tree
- */
- static struct msm_spi_platform_data *msm_spi_dt_to_pdata(
- struct platform_device *pdev, struct msm_spi *dd)
- {
- struct msm_spi_platform_data *pdata;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
- if (pdata) {
- struct msm_spi_dt_to_pdata_map map[] = {
- {"spi-max-frequency",
- &pdata->max_clock_speed, DT_SGST, DT_U32, 0},
- {"qcom,infinite-mode",
- &pdata->infinite_mode, DT_OPT, DT_U32, 0},
- {"qcom,master-id",
- &pdata->master_id, DT_SGST, DT_U32, 0},
- {"qcom,bus-width",
- &pdata->bus_width, DT_OPT, DT_U32, 8},
- {"qcom,ver-reg-exists",
- &pdata->ver_reg_exists, DT_OPT, DT_BOOL, 0},
- {"qcom,use-bam",
- &pdata->use_bam, DT_OPT, DT_BOOL, 0},
- {"qcom,use-pinctrl",
- &pdata->use_pinctrl, DT_OPT, DT_BOOL, 0},
- {"qcom,bam-consumer-pipe-index",
- &pdata->bam_consumer_pipe_index, DT_OPT, DT_U32, 0},
- {"qcom,bam-producer-pipe-index",
- &pdata->bam_producer_pipe_index, DT_OPT, DT_U32, 0},
- {"qcom,gpio-clk",
- &dd->spi_gpios[0], DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-miso",
- &dd->spi_gpios[1], DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-mosi",
- &dd->spi_gpios[2], DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-cs0",
- &dd->cs_gpios[0].gpio_num, DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-cs1",
- &dd->cs_gpios[1].gpio_num, DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-cs2",
- &dd->cs_gpios[2].gpio_num, DT_OPT, DT_GPIO, -1},
- {"qcom,gpio-cs3",
- &dd->cs_gpios[3].gpio_num, DT_OPT, DT_GPIO, -1},
- {"qcom,rt-priority",
- &pdata->rt_priority, DT_OPT, DT_BOOL, 0},
- {"qcom,shared",
- &pdata->is_shared, DT_OPT, DT_BOOL, 0},
- {NULL, NULL, 0, 0, 0},
- };
- if (msm_spi_dt_to_pdata_populate(pdev, pdata, map)) {
- devm_kfree(&pdev->dev, pdata);
- return NULL;
- }
- }
- if (pdata->use_bam) {
- if (!pdata->bam_consumer_pipe_index) {
- dev_warn(&pdev->dev,
- "missing qcom,bam-consumer-pipe-index entry in device-tree\n");
- pdata->use_bam = false;
- }
- if (!pdata->bam_producer_pipe_index) {
- dev_warn(&pdev->dev,
- "missing qcom,bam-producer-pipe-index entry in device-tree\n");
- pdata->use_bam = false;
- }
- }
- return pdata;
- }
- static int msm_spi_get_qup_hw_ver(struct device *dev, struct msm_spi *dd)
- {
- u32 data = readl_relaxed(dd->base + QUP_HARDWARE_VER);
- return (data >= QUP_HARDWARE_VER_2_1_1) ? SPI_QUP_VERSION_BFAM
- : SPI_QUP_VERSION_NONE;
- }
- static int msm_spi_bam_get_resources(struct msm_spi *dd,
- struct platform_device *pdev, struct spi_master *master)
- {
- struct resource *resource;
- size_t bam_mem_size;
- resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "spi_bam_physical");
- if (!resource) {
- dev_warn(&pdev->dev,
- "%s: Missing spi_bam_physical entry in DT",
- __func__);
- return -ENXIO;
- }
- dd->bam.phys_addr = resource->start;
- bam_mem_size = resource_size(resource);
- dd->bam.base = devm_ioremap(&pdev->dev, dd->bam.phys_addr,
- bam_mem_size);
- if (!dd->bam.base) {
- dev_warn(&pdev->dev,
- "%s: Failed to ioremap(spi_bam_physical)",
- __func__);
- return -ENXIO;
- }
- dd->bam.irq = platform_get_irq_byname(pdev, "spi_bam_irq");
- if (dd->bam.irq < 0) {
- dev_warn(&pdev->dev, "%s: Missing spi_bam_irq entry in DT",
- __func__);
- return -EINVAL;
- }
- dd->dma_init = msm_spi_bam_init;
- dd->dma_teardown = msm_spi_bam_teardown;
- return 0;
- }
- static int init_resources(struct platform_device *pdev)
- {
- struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
- int rc = -ENXIO;
- int clk_enabled = 0;
- int pclk_enabled = 0;
- dd = spi_master_get_devdata(master);
- if (dd->pdata && dd->pdata->use_pinctrl) {
- rc = msm_spi_pinctrl_init(dd);
- if (rc) {
- dev_err(&pdev->dev, "%s: pinctrl init failed\n",
- __func__);
- return rc;
- }
- }
- mutex_lock(&dd->core_lock);
- dd->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(dd->clk)) {
- dev_err(&pdev->dev, "%s: unable to get core_clk\n", __func__);
- rc = PTR_ERR(dd->clk);
- goto err_clk_get;
- }
- dd->pclk = clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(dd->pclk)) {
- dev_err(&pdev->dev, "%s: unable to get iface_clk\n", __func__);
- rc = PTR_ERR(dd->pclk);
- goto err_pclk_get;
- }
- if (dd->pdata && dd->pdata->max_clock_speed)
- msm_spi_clock_set(dd, dd->pdata->max_clock_speed);
- rc = clk_prepare_enable(dd->clk);
- if (rc) {
- dev_err(&pdev->dev, "%s: unable to enable core_clk\n",
- __func__);
- goto err_clk_enable;
- }
- clk_enabled = 1;
- rc = clk_prepare_enable(dd->pclk);
- if (rc) {
- dev_err(&pdev->dev, "%s: unable to enable iface_clk\n",
- __func__);
- goto err_pclk_enable;
- }
- pclk_enabled = 1;
- if (dd->pdata && dd->pdata->ver_reg_exists) {
- enum msm_spi_qup_version ver =
- msm_spi_get_qup_hw_ver(&pdev->dev, dd);
- if (dd->qup_ver != ver)
- dev_warn(&pdev->dev,
- "%s: HW version different then initially assumed by probe",
- __func__);
- }
- /* GSBI dose not exists on B-family MSM-chips */
- if (dd->qup_ver != SPI_QUP_VERSION_BFAM) {
- rc = msm_spi_configure_gsbi(dd, pdev);
- if (rc)
- goto err_config_gsbi;
- }
- msm_spi_calculate_fifo_size(dd);
- if (dd->use_dma) {
- rc = dd->dma_init(dd);
- if (rc) {
- dev_err(&pdev->dev,
- "%s: failed to init DMA. Disabling DMA mode\n",
- __func__);
- dd->use_dma = 0;
- }
- }
- msm_spi_register_init(dd);
- /*
- * The SPI core generates a bogus input overrun error on some targets,
- * when a transition from run to reset state occurs and if the FIFO has
- * an odd number of entries. Hence we disable the INPUT_OVER_RUN_ERR_EN
- * bit.
- */
- msm_spi_enable_error_flags(dd);
- writel_relaxed(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL);
- rc = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (rc)
- goto err_spi_state;
- clk_disable_unprepare(dd->clk);
- clk_disable_unprepare(dd->pclk);
- clk_enabled = 0;
- pclk_enabled = 0;
- dd->transfer_pending = 0;
- dd->tx_mode = SPI_MODE_NONE;
- dd->rx_mode = SPI_MODE_NONE;
- rc = msm_spi_request_irq(dd, pdev, master);
- if (rc)
- goto err_irq;
- msm_spi_disable_irqs(dd);
- mutex_unlock(&dd->core_lock);
- return 0;
- err_irq:
- err_spi_state:
- if (dd->use_dma && dd->dma_teardown)
- dd->dma_teardown(dd);
- err_config_gsbi:
- if (pclk_enabled)
- clk_disable_unprepare(dd->pclk);
- err_pclk_enable:
- if (clk_enabled)
- clk_disable_unprepare(dd->clk);
- err_clk_enable:
- clk_put(dd->pclk);
- err_pclk_get:
- clk_put(dd->clk);
- err_clk_get:
- mutex_unlock(&dd->core_lock);
- return rc;
- }
- static int msm_spi_probe(struct platform_device *pdev)
- {
- struct spi_master *master;
- struct msm_spi *dd;
- struct resource *resource;
- int i = 0;
- int rc = -ENXIO;
- struct msm_spi_platform_data *pdata;
- master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi));
- if (!master) {
- rc = -ENOMEM;
- dev_err(&pdev->dev, "master allocation failed\n");
- goto err_probe_exit;
- }
- master->bus_num = pdev->id;
- master->mode_bits = SPI_SUPPORTED_MODES;
- master->num_chipselect = SPI_NUM_CHIPSELECTS;
- master->set_cs = msm_spi_set_cs;
- master->setup = msm_spi_setup;
- master->prepare_transfer_hardware = msm_spi_prepare_transfer_hardware;
- master->transfer_one = msm_spi_transfer_one;
- master->unprepare_transfer_hardware
- = msm_spi_unprepare_transfer_hardware;
- platform_set_drvdata(pdev, master);
- dd = spi_master_get_devdata(master);
- if (pdev->dev.of_node) {
- dd->qup_ver = SPI_QUP_VERSION_BFAM;
- master->dev.of_node = pdev->dev.of_node;
- pdata = msm_spi_dt_to_pdata(pdev, dd);
- if (!pdata) {
- dev_err(&pdev->dev, "platform data allocation failed\n");
- rc = -ENOMEM;
- goto err_probe_exit;
- }
- rc = of_alias_get_id(pdev->dev.of_node, "spi");
- if (rc < 0)
- dev_warn(&pdev->dev,
- "using default bus_num %d\n", pdev->id);
- else
- master->bus_num = pdev->id = rc;
- } else {
- pdata = pdev->dev.platform_data;
- dd->qup_ver = SPI_QUP_VERSION_NONE;
- for (i = 0; i < ARRAY_SIZE(spi_rsrcs); ++i) {
- resource = platform_get_resource(pdev, IORESOURCE_IO,
- i);
- dd->spi_gpios[i] = resource ? resource->start : -1;
- }
- for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i) {
- resource = platform_get_resource(pdev, IORESOURCE_IO,
- i + ARRAY_SIZE(spi_rsrcs));
- dd->cs_gpios[i].gpio_num = resource ?
- resource->start : -1;
- }
- }
- for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i)
- dd->cs_gpios[i].valid = 0;
- dd->pdata = pdata;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!resource) {
- rc = -ENXIO;
- goto err_probe_res;
- }
- dd->mem_phys_addr = resource->start;
- dd->mem_size = resource_size(resource);
- dd->dev = &pdev->dev;
- if (pdata) {
- master->rt = pdata->rt_priority;
- if (pdata->dma_config) {
- rc = pdata->dma_config();
- if (rc) {
- dev_warn(&pdev->dev,
- "%s: DM mode not supported\n",
- __func__);
- dd->use_dma = 0;
- goto skip_dma_resources;
- }
- }
- if (!dd->pdata->use_bam)
- goto skip_dma_resources;
- rc = msm_spi_bam_get_resources(dd, pdev, master);
- if (rc) {
- dev_warn(dd->dev,
- "%s: Failed to get BAM resources",
- __func__);
- goto skip_dma_resources;
- }
- dd->use_dma = 1;
- }
- spi_dma_mask(&pdev->dev);
- skip_dma_resources:
- spin_lock_init(&dd->queue_lock);
- mutex_init(&dd->core_lock);
- init_waitqueue_head(&dd->continue_suspend);
- if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
- dd->mem_size, SPI_DRV_NAME)) {
- rc = -ENXIO;
- goto err_probe_reqmem;
- }
- dd->base = devm_ioremap(&pdev->dev, dd->mem_phys_addr, dd->mem_size);
- if (!dd->base) {
- rc = -ENOMEM;
- goto err_probe_reqmem;
- }
- pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- dd->suspended = 1;
- rc = spi_register_master(master);
- if (rc)
- goto err_probe_reg_master;
- rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp);
- if (rc) {
- dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc);
- goto err_attrs;
- }
- spi_debugfs_init(dd);
- return 0;
- err_attrs:
- spi_unregister_master(master);
- err_probe_reg_master:
- pm_runtime_disable(&pdev->dev);
- err_probe_reqmem:
- err_probe_res:
- spi_master_put(master);
- err_probe_exit:
- return rc;
- }
- static int msm_spi_pm_suspend_runtime(struct device *device)
- {
- struct platform_device *pdev = to_platform_device(device);
- struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
- unsigned long flags;
- dev_dbg(device, "pm_runtime: suspending...\n");
- if (!master)
- goto suspend_exit;
- dd = spi_master_get_devdata(master);
- if (!dd)
- goto suspend_exit;
- if (dd->suspended)
- return 0;
- /*
- * Make sure nothing is added to the queue while we're
- * suspending
- */
- spin_lock_irqsave(&dd->queue_lock, flags);
- dd->suspended = 1;
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- /* Wait for transactions to end, or time out */
- wait_event_interruptible(dd->continue_suspend,
- !dd->transfer_pending);
- mutex_lock(&dd->core_lock);
- if (dd->pdata && !dd->pdata->is_shared && dd->use_dma) {
- msm_spi_bam_pipe_disconnect(dd, &dd->bam.prod);
- msm_spi_bam_pipe_disconnect(dd, &dd->bam.cons);
- }
- if (dd->pdata && !dd->pdata->is_shared)
- put_local_resources(dd);
- if (dd->pdata)
- msm_spi_clk_path_vote(dd, 0);
- mutex_unlock(&dd->core_lock);
- suspend_exit:
- return 0;
- }
- static int msm_spi_pm_resume_runtime(struct device *device)
- {
- struct platform_device *pdev = to_platform_device(device);
- struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
- int ret = 0;
- dev_dbg(device, "pm_runtime: resuming...\n");
- if (!master)
- goto resume_exit;
- dd = spi_master_get_devdata(master);
- if (!dd)
- goto resume_exit;
- if (!dd->suspended)
- return 0;
- if (!dd->is_init_complete) {
- ret = init_resources(pdev);
- if (ret != 0)
- return ret;
- dd->is_init_complete = true;
- }
- msm_spi_clk_path_init(dd);
- msm_spi_clk_path_vote(dd, dd->pdata->max_clock_speed);
- if (!dd->pdata->is_shared) {
- ret = get_local_resources(dd);
- if (ret)
- return ret;
- }
- if (!dd->pdata->is_shared && dd->use_dma) {
- msm_spi_bam_pipe_connect(dd, &dd->bam.prod,
- &dd->bam.prod.config);
- msm_spi_bam_pipe_connect(dd, &dd->bam.cons,
- &dd->bam.cons.config);
- }
- dd->suspended = 0;
- resume_exit:
- return 0;
- }
- #ifdef CONFIG_PM_SLEEP
- static int msm_spi_suspend(struct device *device)
- {
- if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
- struct platform_device *pdev = to_platform_device(device);
- struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd;
- dev_dbg(device, "system suspend");
- if (!master)
- goto suspend_exit;
- dd = spi_master_get_devdata(master);
- if (!dd)
- goto suspend_exit;
- msm_spi_pm_suspend_runtime(device);
- /*
- * set the device's runtime PM status to 'suspended'
- */
- pm_runtime_disable(device);
- pm_runtime_set_suspended(device);
- pm_runtime_enable(device);
- }
- suspend_exit:
- return 0;
- }
- static int msm_spi_resume(struct device *device)
- {
- /*
- * Rely on runtime-PM to call resume in case it is enabled
- * Even if it's not enabled, rely on 1st client transaction to do
- * clock ON and gpio configuration
- */
- dev_dbg(device, "system resume");
- return 0;
- }
- #else
- #define msm_spi_suspend NULL
- #define msm_spi_resume NULL
- #endif
- static int msm_spi_remove(struct platform_device *pdev)
- {
- struct spi_master *master = platform_get_drvdata(pdev);
- struct msm_spi *dd = spi_master_get_devdata(master);
- spi_debugfs_exit(dd);
- sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
- if (dd->dma_teardown)
- dd->dma_teardown(dd);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
- clk_put(dd->clk);
- clk_put(dd->pclk);
- msm_spi_clk_path_teardown(dd);
- platform_set_drvdata(pdev, NULL);
- spi_unregister_master(master);
- spi_master_put(master);
- return 0;
- }
- static const struct of_device_id msm_spi_dt_match[] = {
- {
- .compatible = "qcom,spi-qup-v2",
- },
- {}
- };
- static const struct dev_pm_ops msm_spi_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume)
- SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime,
- msm_spi_pm_resume_runtime, NULL)
- };
- static struct platform_driver msm_spi_driver = {
- .driver = {
- .name = SPI_DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &msm_spi_dev_pm_ops,
- .of_match_table = msm_spi_dt_match,
- },
- .probe = msm_spi_probe,
- .remove = msm_spi_remove,
- };
- module_platform_driver(msm_spi_driver);
- MODULE_LICENSE("GPL v2");
- MODULE_ALIAS("platform:"SPI_DRV_NAME);
|