1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814 |
- /* drivers/serial/msm_serial_hs.c
- *
- * MSM 7k High speed uart driver
- *
- * Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved.
- * Modified: Nick Pelly <[email protected]>
- *
- * All source code in this file is licensed under the following license
- * except where indicated.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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.
- *
- * Has optional support for uart power management independent of linux
- * suspend/resume:
- *
- * RX wakeup.
- * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the
- * UART RX pin). This should only be used if there is not a wakeup
- * GPIO on the UART CTS, and the first RX byte is known (for example, with the
- * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will
- * always be lost. RTS will be asserted even while the UART is off in this mode
- * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq.
- */
- #include <linux/module.h>
- #include <linux/serial.h>
- #include <linux/serial_core.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
- #include <linux/atomic.h>
- #include <linux/kernel.h>
- #include <linux/timer.h>
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <linux/pm_runtime.h>
- #include <linux/dma-mapping.h>
- #include <linux/tty_flip.h>
- #include <linux/wait.h>
- #include <linux/sysfs.h>
- #include <linux/stat.h>
- #include <linux/device.h>
- #include <linux/debugfs.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/of_gpio.h>
- #include <linux/gpio.h>
- #include <linux/ipc_logging.h>
- #include <asm/irq.h>
- #include <linux/kthread.h>
- #include <uapi/linux/sched.h>
- #include <linux/msm-sps.h>
- #include <linux/platform_data/msm_serial_hs.h>
- #include <linux/msm-bus.h>
- #include "msm_serial_hs_hwreg.h"
- #define UART_SPS_CONS_PERIPHERAL 0
- #define UART_SPS_PROD_PERIPHERAL 1
- #define IPC_MSM_HS_LOG_STATE_PAGES 2
- #define IPC_MSM_HS_LOG_USER_PAGES 2
- #define IPC_MSM_HS_LOG_DATA_PAGES 3
- #define UART_DMA_DESC_NR 8
- #define BUF_DUMP_SIZE 32
- /* If the debug_mask gets set to FATAL_LEV,
- * a fatal error has happened and further IPC logging
- * is disabled so that this problem can be detected
- */
- enum {
- FATAL_LEV = 0U,
- ERR_LEV = 1U,
- WARN_LEV = 2U,
- INFO_LEV = 3U,
- DBG_LEV = 4U,
- };
- #define MSM_HS_DBG(x...) do { \
- if (msm_uport->ipc_debug_mask >= DBG_LEV) { \
- if (msm_uport->ipc_msm_hs_log_ctxt) \
- ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
- } \
- } while (0)
- #define MSM_HS_INFO(x...) do { \
- if (msm_uport->ipc_debug_mask >= INFO_LEV) {\
- if (msm_uport->ipc_msm_hs_log_ctxt) \
- ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
- } \
- } while (0)
- /* warnings and errors show up on console always */
- #define MSM_HS_WARN(x...) do { \
- pr_warn(x); \
- if (msm_uport->ipc_msm_hs_log_ctxt && \
- msm_uport->ipc_debug_mask >= WARN_LEV) \
- ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
- } while (0)
- /* ERROR condition in the driver sets the hs_serial_debug_mask
- * to ERR_FATAL level, so that this message can be seen
- * in IPC logging. Further errors continue to log on the console
- */
- #define MSM_HS_ERR(x...) do { \
- pr_err(x); \
- if (msm_uport->ipc_msm_hs_log_ctxt && \
- msm_uport->ipc_debug_mask >= ERR_LEV) { \
- ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
- msm_uport->ipc_debug_mask = FATAL_LEV; \
- } \
- } while (0)
- #define LOG_USR_MSG(ctx, x...) do { \
- if (ctx) \
- ipc_log_string(ctx, x); \
- } while (0)
- /*
- * There are 3 different kind of UART Core available on MSM.
- * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
- * and BSLP based HSUART.
- */
- enum uart_core_type {
- LEGACY_HSUART,
- GSBI_HSUART,
- BLSP_HSUART,
- };
- enum flush_reason {
- FLUSH_NONE,
- FLUSH_DATA_READY,
- FLUSH_DATA_INVALID, /* values after this indicate invalid data */
- FLUSH_IGNORE,
- FLUSH_STOP,
- FLUSH_SHUTDOWN,
- };
- /*
- * SPS data structures to support HSUART with BAM
- * @sps_pipe - This struct defines BAM pipe descriptor
- * @sps_connect - This struct defines a connection's end point
- * @sps_register - This struct defines a event registration parameters
- */
- struct msm_hs_sps_ep_conn_data {
- struct sps_pipe *pipe_handle;
- struct sps_connect config;
- struct sps_register_event event;
- };
- struct msm_hs_tx {
- bool dma_in_flight; /* tx dma in progress */
- enum flush_reason flush;
- wait_queue_head_t wait;
- int tx_count;
- dma_addr_t dma_base;
- struct kthread_work kwork;
- struct kthread_worker kworker;
- struct task_struct *task;
- struct msm_hs_sps_ep_conn_data cons;
- struct timer_list tx_timeout_timer;
- void *ipc_tx_ctxt;
- };
- struct msm_hs_rx {
- enum flush_reason flush;
- wait_queue_head_t wait;
- dma_addr_t rbuffer;
- unsigned char *buffer;
- unsigned int buffer_pending;
- struct delayed_work flip_insert_work;
- struct kthread_work kwork;
- struct kthread_worker kworker;
- struct task_struct *task;
- struct msm_hs_sps_ep_conn_data prod;
- unsigned long queued_flag;
- unsigned long pending_flag;
- int rx_inx;
- struct sps_iovec iovec[UART_DMA_DESC_NR]; /* track descriptors */
- void *ipc_rx_ctxt;
- };
- enum buffer_states {
- NONE_PENDING = 0x0,
- FIFO_OVERRUN = 0x1,
- PARITY_ERROR = 0x2,
- CHARS_NORMAL = 0x4,
- };
- enum msm_hs_pm_state {
- MSM_HS_PM_ACTIVE,
- MSM_HS_PM_SUSPENDED,
- MSM_HS_PM_SYS_SUSPENDED,
- };
- /* optional low power wakeup, typically on a GPIO RX irq */
- struct msm_hs_wakeup {
- int irq; /* < 0 indicates low power wakeup disabled */
- unsigned char ignore; /* bool */
- /* bool: inject char into rx tty on wakeup */
- bool inject_rx;
- unsigned char rx_to_inject;
- bool enabled;
- bool freed;
- };
- struct msm_hs_port {
- struct uart_port uport;
- unsigned long imr_reg; /* shadow value of UARTDM_IMR */
- struct clk *clk;
- struct clk *pclk;
- struct msm_hs_tx tx;
- struct msm_hs_rx rx;
- atomic_t resource_count;
- struct msm_hs_wakeup wakeup;
- struct dentry *loopback_dir;
- struct work_struct clock_off_w; /* work for actual clock off */
- struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
- struct mutex mtx; /* resource access mutex */
- enum uart_core_type uart_type;
- unsigned long bam_handle;
- resource_size_t bam_mem;
- int bam_irq;
- unsigned char __iomem *bam_base;
- unsigned int bam_tx_ep_pipe_index;
- unsigned int bam_rx_ep_pipe_index;
- /* struct sps_event_notify is an argument passed when triggering a
- * callback event object registered for an SPS connection end point.
- */
- struct sps_event_notify notify;
- /* bus client handler */
- u32 bus_perf_client;
- /* BLSP UART required BUS Scaling data */
- struct msm_bus_scale_pdata *bus_scale_table;
- bool rx_bam_inprogress;
- wait_queue_head_t bam_disconnect_wait;
- bool use_pinctrl;
- struct pinctrl *pinctrl;
- struct pinctrl_state *gpio_state_active;
- struct pinctrl_state *gpio_state_suspend;
- bool flow_control;
- enum msm_hs_pm_state pm_state;
- atomic_t client_count;
- bool obs; /* out of band sleep flag */
- atomic_t client_req_state;
- void *ipc_msm_hs_log_ctxt;
- void *ipc_msm_hs_pwr_ctxt;
- int ipc_debug_mask;
- };
- static const struct of_device_id msm_hs_match_table[] = {
- { .compatible = "qcom,msm-hsuart-v14"},
- {}
- };
- #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
- #define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE
- #define UARTDM_RX_BUF_SIZE 512
- #define RETRY_TIMEOUT 5
- #define UARTDM_NR 256
- #define BAM_PIPE_MIN 0
- #define BAM_PIPE_MAX 11
- #define BUS_SCALING 1
- #define BUS_RESET 0
- #define RX_FLUSH_COMPLETE_TIMEOUT 300 /* In jiffies */
- #define BLSP_UART_CLK_FMAX 63160000
- static struct dentry *debug_base;
- static struct platform_driver msm_serial_hs_platform_driver;
- static struct uart_driver msm_hs_driver;
- static const struct uart_ops msm_hs_ops;
- static void msm_hs_start_rx_locked(struct uart_port *uport);
- static void msm_serial_hs_rx_work(struct kthread_work *work);
- static void flip_insert_work(struct work_struct *work);
- static void msm_hs_bus_voting(struct msm_hs_port *msm_uport, unsigned int vote);
- static struct msm_hs_port *msm_hs_get_hs_port(int port_index);
- static void msm_hs_queue_rx_desc(struct msm_hs_port *msm_uport);
- static int disconnect_rx_endpoint(struct msm_hs_port *msm_uport);
- static int msm_hs_pm_resume(struct device *dev);
- #define UARTDM_TO_MSM(uart_port) \
- container_of((uart_port), struct msm_hs_port, uport)
- static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd,
- unsigned long arg)
- {
- int ret = 0, state = 1;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- if (!msm_uport)
- return -ENODEV;
- switch (cmd) {
- case MSM_ENABLE_UART_CLOCK: {
- ret = msm_hs_request_clock_on(&msm_uport->uport);
- break;
- }
- case MSM_DISABLE_UART_CLOCK: {
- ret = msm_hs_request_clock_off(&msm_uport->uport);
- break;
- }
- case MSM_GET_UART_CLOCK_STATUS: {
- /* Return value 0 - UART CLOCK is OFF
- * Return value 1 - UART CLOCK is ON
- */
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
- state = 0;
- ret = state;
- MSM_HS_INFO("%s():GET UART CLOCK STATUS: cmd=%d state=%d\n",
- __func__, cmd, state);
- break;
- }
- default: {
- MSM_HS_INFO("%s():Unknown cmd specified: cmd=%d\n", __func__,
- cmd);
- ret = -ENOIOCTLCMD;
- break;
- }
- }
- return ret;
- }
- /*
- * This function is called initially during probe and then
- * through the runtime PM framework. The function directly calls
- * resource APIs to enable them.
- */
- static int msm_hs_clk_bus_vote(struct msm_hs_port *msm_uport)
- {
- int rc = 0;
- msm_hs_bus_voting(msm_uport, BUS_SCALING);
- /* Turn on core clk and iface clk */
- if (msm_uport->pclk) {
- rc = clk_prepare_enable(msm_uport->pclk);
- if (rc) {
- dev_err(msm_uport->uport.dev,
- "%s: Could not turn on pclk [%d]\n",
- __func__, rc);
- goto busreset;
- }
- }
- rc = clk_prepare_enable(msm_uport->clk);
- if (rc) {
- dev_err(msm_uport->uport.dev,
- "%s: Could not turn on core clk [%d]\n",
- __func__, rc);
- goto core_unprepare;
- }
- MSM_HS_DBG("%s: Clock ON successful\n", __func__);
- return rc;
- core_unprepare:
- clk_disable_unprepare(msm_uport->pclk);
- busreset:
- msm_hs_bus_voting(msm_uport, BUS_RESET);
- return rc;
- }
- /*
- * This function is called initially during probe and then
- * through the runtime PM framework. The function directly calls
- * resource apis to disable them.
- */
- static void msm_hs_clk_bus_unvote(struct msm_hs_port *msm_uport)
- {
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
- msm_hs_bus_voting(msm_uport, BUS_RESET);
- MSM_HS_DBG("%s: Clock OFF successful\n", __func__);
- }
- /* Remove vote for resources when done */
- static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport)
- {
- struct uart_port *uport = &(msm_uport->uport);
- int rc = atomic_read(&msm_uport->resource_count);
- MSM_HS_DBG("%s(): power usage count %d", __func__, rc);
- if (rc <= 0) {
- MSM_HS_WARN("%s(): rc zero, bailing\n", __func__);
- WARN_ON(1);
- return;
- }
- atomic_dec(&msm_uport->resource_count);
- pm_runtime_mark_last_busy(uport->dev);
- pm_runtime_put_autosuspend(uport->dev);
- }
- /* Vote for resources before accessing them */
- static void msm_hs_resource_vote(struct msm_hs_port *msm_uport)
- {
- int ret;
- struct uart_port *uport = &(msm_uport->uport);
- ret = pm_runtime_get_sync(uport->dev);
- if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s:%s runtime callback not invoked ret:%d st:%d",
- __func__, dev_name(uport->dev), ret,
- msm_uport->pm_state);
- msm_hs_pm_resume(uport->dev);
- }
- atomic_inc(&msm_uport->resource_count);
- }
- /* Check if the uport line number matches with user id stored in pdata.
- * User id information is stored during initialization. This function
- * ensues that the same device is selected
- */
- static struct msm_hs_port *get_matching_hs_port(struct platform_device *pdev)
- {
- struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
- struct msm_hs_port *msm_uport = msm_hs_get_hs_port(pdev->id);
- if ((!msm_uport) || (msm_uport->uport.line != pdev->id
- && msm_uport->uport.line != pdata->userid)) {
- pr_err("uport line number mismatch!");
- WARN_ON(1);
- return NULL;
- }
- return msm_uport;
- }
- static ssize_t show_clock(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- int state = 1;
- ssize_t ret = 0;
- struct platform_device *pdev = container_of(dev, struct
- platform_device, dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- /* This check should not fail */
- if (msm_uport) {
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
- state = 0;
- ret = snprintf(buf, PAGE_SIZE, "%d\n", state);
- }
- return ret;
- }
- static ssize_t set_clock(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- int state;
- ssize_t ret = 0;
- struct platform_device *pdev = container_of(dev, struct
- platform_device, dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- /* This check should not fail */
- if (msm_uport) {
- state = buf[0] - '0';
- switch (state) {
- case 0:
- MSM_HS_DBG("%s: Request clock OFF\n", __func__);
- msm_hs_request_clock_off(&msm_uport->uport);
- ret = count;
- break;
- case 1:
- MSM_HS_DBG("%s: Request clock ON\n", __func__);
- msm_hs_request_clock_on(&msm_uport->uport);
- ret = count;
- break;
- default:
- ret = -EINVAL;
- }
- }
- return ret;
- }
- static DEVICE_ATTR(clock, 0644, show_clock, set_clock);
- static ssize_t show_debug_mask(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- ssize_t ret = 0;
- struct platform_device *pdev = container_of(dev, struct
- platform_device, dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- /* This check should not fail */
- if (msm_uport)
- ret = snprintf(buf, sizeof(int), "%u\n",
- msm_uport->ipc_debug_mask);
- return ret;
- }
- static ssize_t set_debug_mask(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct platform_device *pdev = container_of(dev, struct
- platform_device, dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- /* This check should not fail */
- if (msm_uport) {
- msm_uport->ipc_debug_mask = buf[0] - '0';
- if (msm_uport->ipc_debug_mask < FATAL_LEV ||
- msm_uport->ipc_debug_mask > DBG_LEV) {
- /* set to default level */
- msm_uport->ipc_debug_mask = INFO_LEV;
- MSM_HS_ERR("Range is 0 to 4;Set to default level 3\n");
- return -EINVAL;
- }
- }
- return count;
- }
- static DEVICE_ATTR(debug_mask, 0644, show_debug_mask,
- set_debug_mask);
- static inline bool is_use_low_power_wakeup(struct msm_hs_port *msm_uport)
- {
- return msm_uport->wakeup.irq > 0;
- }
- static void msm_hs_bus_voting(struct msm_hs_port *msm_uport, unsigned int vote)
- {
- int ret;
- if (msm_uport->bus_perf_client) {
- MSM_HS_DBG("Bus voting:%d\n", vote);
- ret = msm_bus_scale_client_update_request(
- msm_uport->bus_perf_client, vote);
- if (ret)
- MSM_HS_ERR("%s(): Failed for Bus voting: %d\n",
- __func__, vote);
- }
- }
- static inline unsigned int msm_hs_read(struct uart_port *uport,
- unsigned int index)
- {
- return readl_relaxed(uport->membase + index);
- }
- static inline void msm_hs_write(struct uart_port *uport, unsigned int index,
- unsigned int value)
- {
- writel_relaxed(value, uport->membase + index);
- }
- static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler)
- {
- struct sps_connect config;
- int ret;
- ret = sps_get_config(sps_pipe_handler, &config);
- if (ret) {
- pr_err("%s: sps_get_config() failed ret %d\n", __func__, ret);
- return ret;
- }
- config.options |= SPS_O_POLL;
- ret = sps_set_config(sps_pipe_handler, &config);
- if (ret) {
- pr_err("%s: sps_set_config() failed ret %d\n", __func__, ret);
- return ret;
- }
- return sps_disconnect(sps_pipe_handler);
- }
- static void hex_dump_ipc(struct msm_hs_port *msm_uport, void *ipc_ctx,
- char *prefix, char *string, u64 addr, int size)
- {
- char buf[(BUF_DUMP_SIZE * 3) + 2];
- int len = 0;
- len = min(size, BUF_DUMP_SIZE);
- /*
- * Print upto 32 data bytes, 32 bytes per line, 1 byte at a time and
- * don't include the ASCII text at the end of the buffer.
- */
- hex_dump_to_buffer(string, len, 32, 1, buf, sizeof(buf), false);
- ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix,
- (unsigned int)addr, size, buf);
- }
- /*
- * This API read and provides UART Core registers information.
- */
- static void dump_uart_hs_registers(struct msm_hs_port *msm_uport)
- {
- struct uart_port *uport = &(msm_uport->uport);
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_INFO("%s:Failed clocks are off, resource_count %d",
- __func__, atomic_read(&msm_uport->resource_count));
- return;
- }
- MSM_HS_DBG(
- "MR1:%x MR2:%x TFWR:%x RFWR:%x DMEN:%x IMR:%x MISR:%x NCF_TX:%x\n",
- msm_hs_read(uport, UART_DM_MR1),
- msm_hs_read(uport, UART_DM_MR2),
- msm_hs_read(uport, UART_DM_TFWR),
- msm_hs_read(uport, UART_DM_RFWR),
- msm_hs_read(uport, UART_DM_DMEN),
- msm_hs_read(uport, UART_DM_IMR),
- msm_hs_read(uport, UART_DM_MISR),
- msm_hs_read(uport, UART_DM_NCF_TX));
- MSM_HS_INFO("SR:%x ISR:%x DMRX:%x RX_SNAP:%x TXFS:%x RXFS:%x\n",
- msm_hs_read(uport, UART_DM_SR),
- msm_hs_read(uport, UART_DM_ISR),
- msm_hs_read(uport, UART_DM_DMRX),
- msm_hs_read(uport, UART_DM_RX_TOTAL_SNAP),
- msm_hs_read(uport, UART_DM_TXFS),
- msm_hs_read(uport, UART_DM_RXFS));
- MSM_HS_DBG("rx.flush:%u\n", msm_uport->rx.flush);
- }
- static int msm_serial_loopback_enable_set(void *data, u64 val)
- {
- struct msm_hs_port *msm_uport = data;
- struct uart_port *uport = &(msm_uport->uport);
- unsigned long flags;
- int ret = 0;
- msm_hs_resource_vote(msm_uport);
- if (val) {
- spin_lock_irqsave(&uport->lock, flags);
- ret = msm_hs_read(uport, UART_DM_MR2);
- ret |= (UARTDM_MR2_LOOP_MODE_BMSK |
- UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
- msm_hs_write(uport, UART_DM_MR2, ret);
- spin_unlock_irqrestore(&uport->lock, flags);
- } else {
- spin_lock_irqsave(&uport->lock, flags);
- ret = msm_hs_read(uport, UART_DM_MR2);
- ret &= ~(UARTDM_MR2_LOOP_MODE_BMSK |
- UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
- msm_hs_write(uport, UART_DM_MR2, ret);
- spin_unlock_irqrestore(&uport->lock, flags);
- }
- /* Calling CLOCK API. Hence mb() requires here. */
- mb();
- msm_hs_resource_unvote(msm_uport);
- return 0;
- }
- static int msm_serial_loopback_enable_get(void *data, u64 *val)
- {
- struct msm_hs_port *msm_uport = data;
- struct uart_port *uport = &(msm_uport->uport);
- unsigned long flags;
- int ret = 0;
- msm_hs_resource_vote(msm_uport);
- spin_lock_irqsave(&uport->lock, flags);
- ret = msm_hs_read(&msm_uport->uport, UART_DM_MR2);
- spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
- *val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_serial_loopback_enable_get,
- msm_serial_loopback_enable_set, "%llu\n");
- /*
- * msm_serial_hs debugfs node: <debugfs_root>/msm_serial_hs/loopback.<id>
- * writing 1 turns on internal loopback mode in HW. Useful for automation
- * test scripts.
- * writing 0 disables the internal loopback mode. Default is disabled.
- */
- static void msm_serial_debugfs_init(struct msm_hs_port *msm_uport,
- int id)
- {
- char node_name[15];
- snprintf(node_name, sizeof(node_name), "loopback.%d", id);
- msm_uport->loopback_dir = debugfs_create_file(node_name,
- 0644,
- debug_base,
- msm_uport,
- &loopback_enable_fops);
- if (IS_ERR_OR_NULL(msm_uport->loopback_dir))
- MSM_HS_ERR("%s(): Cannot create loopback.%d debug entry",
- __func__, id);
- }
- static int msm_hs_remove(struct platform_device *pdev)
- {
- struct msm_hs_port *msm_uport;
- struct device *dev;
- if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- pr_err("Invalid plaform device ID = %d\n", pdev->id);
- return -EINVAL;
- }
- msm_uport = get_matching_hs_port(pdev);
- if (!msm_uport)
- return -EINVAL;
- dev = msm_uport->uport.dev;
- sysfs_remove_file(&pdev->dev.kobj, &dev_attr_clock.attr);
- sysfs_remove_file(&pdev->dev.kobj, &dev_attr_debug_mask.attr);
- debugfs_remove(msm_uport->loopback_dir);
- dma_free_coherent(msm_uport->uport.dev,
- UART_DMA_DESC_NR * UARTDM_RX_BUF_SIZE,
- msm_uport->rx.buffer, msm_uport->rx.rbuffer);
- msm_uport->rx.buffer = NULL;
- msm_uport->rx.rbuffer = 0;
- destroy_workqueue(msm_uport->hsuart_wq);
- mutex_destroy(&msm_uport->mtx);
- uart_remove_one_port(&msm_hs_driver, &msm_uport->uport);
- clk_put(msm_uport->clk);
- if (msm_uport->pclk)
- clk_put(msm_uport->pclk);
- iounmap(msm_uport->uport.membase);
- return 0;
- }
- /* Connect a UART peripheral's SPS endpoint(consumer endpoint)
- *
- * Also registers a SPS callback function for the consumer
- * process with the SPS driver
- *
- * @uport - Pointer to uart uport structure
- *
- * @return - 0 if successful else negative value.
- *
- */
- static int msm_hs_spsconnect_tx(struct msm_hs_port *msm_uport)
- {
- int ret;
- struct uart_port *uport = &msm_uport->uport;
- struct msm_hs_tx *tx = &msm_uport->tx;
- struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
- struct sps_connect *sps_config = &tx->cons.config;
- struct sps_register_event *sps_event = &tx->cons.event;
- unsigned long flags;
- unsigned int data;
- if (tx->flush != FLUSH_SHUTDOWN) {
- MSM_HS_ERR("%s:Invalid flush state:%d\n", __func__, tx->flush);
- return 0;
- }
- /* Establish connection between peripheral and memory endpoint */
- ret = sps_connect(sps_pipe_handle, sps_config);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: sps_connect() failed for tx!!\n"
- "pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
- return ret;
- }
- /* Register callback event for EOT (End of transfer) event. */
- ret = sps_register_event(sps_pipe_handle, sps_event);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: sps_connect() failed for tx!!\n"
- "pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
- goto reg_event_err;
- }
- spin_lock_irqsave(&(msm_uport->uport.lock), flags);
- msm_uport->tx.flush = FLUSH_STOP;
- spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
- data = msm_hs_read(uport, UART_DM_DMEN);
- /* Enable UARTDM Tx BAM Interface */
- data |= UARTDM_TX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- msm_hs_write(uport, UART_DM_CR, RESET_TX);
- msm_hs_write(uport, UART_DM_CR, START_TX_BAM_IFC);
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_EN_BMSK);
- MSM_HS_DBG("%s(): TX Connect", __func__);
- return 0;
- reg_event_err:
- sps_disconnect(sps_pipe_handle);
- return ret;
- }
- /* Connect a UART peripheral's SPS endpoint(producer endpoint)
- *
- * Also registers a SPS callback function for the producer
- * process with the SPS driver
- *
- * @uport - Pointer to uart uport structure
- *
- * @return - 0 if successful else negative value.
- *
- */
- static int msm_hs_spsconnect_rx(struct uart_port *uport)
- {
- int ret;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_rx *rx = &msm_uport->rx;
- struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
- struct sps_connect *sps_config = &rx->prod.config;
- struct sps_register_event *sps_event = &rx->prod.event;
- unsigned long flags;
- /* Establish connection between peripheral and memory endpoint */
- ret = sps_connect(sps_pipe_handle, sps_config);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: sps_connect() failed for rx!!\n"
- "pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
- return ret;
- }
- /* Register callback event for DESC_DONE event. */
- ret = sps_register_event(sps_pipe_handle, sps_event);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: sps_connect() failed for rx!!\n"
- "pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
- goto reg_event_err;
- }
- spin_lock_irqsave(&uport->lock, flags);
- if (msm_uport->rx.pending_flag)
- MSM_HS_WARN("%s(): Buffers may be pending 0x%lx",
- __func__, msm_uport->rx.pending_flag);
- msm_uport->rx.queued_flag = 0;
- msm_uport->rx.pending_flag = 0;
- msm_uport->rx.rx_inx = 0;
- msm_uport->rx.flush = FLUSH_STOP;
- spin_unlock_irqrestore(&uport->lock, flags);
- MSM_HS_DBG("%s(): RX Connect\n", __func__);
- return 0;
- reg_event_err:
- sps_disconnect(sps_pipe_handle);
- return ret;
- }
- /*
- * programs the UARTDM_CSR register with correct bit rates
- *
- * Interrupts should be disabled before we are called, as
- * we modify Set Baud rate
- * Set receive stale interrupt level, dependent on Bit Rate
- * Goal is to have around 8 ms before indicate stale.
- * roundup (((Bit Rate * .008) / 10) + 1
- */
- static void msm_hs_set_bps_locked(struct uart_port *uport,
- unsigned int bps)
- {
- unsigned long rxstale;
- unsigned long data;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- switch (bps) {
- case 300:
- msm_hs_write(uport, UART_DM_CSR, 0x00);
- rxstale = 1;
- break;
- case 600:
- msm_hs_write(uport, UART_DM_CSR, 0x11);
- rxstale = 1;
- break;
- case 1200:
- msm_hs_write(uport, UART_DM_CSR, 0x22);
- rxstale = 1;
- break;
- case 2400:
- msm_hs_write(uport, UART_DM_CSR, 0x33);
- rxstale = 1;
- break;
- case 4800:
- msm_hs_write(uport, UART_DM_CSR, 0x44);
- rxstale = 1;
- break;
- case 9600:
- msm_hs_write(uport, UART_DM_CSR, 0x55);
- rxstale = 2;
- break;
- case 14400:
- msm_hs_write(uport, UART_DM_CSR, 0x66);
- rxstale = 3;
- break;
- case 19200:
- msm_hs_write(uport, UART_DM_CSR, 0x77);
- rxstale = 4;
- break;
- case 28800:
- msm_hs_write(uport, UART_DM_CSR, 0x88);
- rxstale = 6;
- break;
- case 38400:
- msm_hs_write(uport, UART_DM_CSR, 0x99);
- rxstale = 8;
- break;
- case 57600:
- msm_hs_write(uport, UART_DM_CSR, 0xaa);
- rxstale = 16;
- break;
- case 76800:
- msm_hs_write(uport, UART_DM_CSR, 0xbb);
- rxstale = 16;
- break;
- case 115200:
- msm_hs_write(uport, UART_DM_CSR, 0xcc);
- rxstale = 31;
- break;
- case 230400:
- msm_hs_write(uport, UART_DM_CSR, 0xee);
- rxstale = 31;
- break;
- case 460800:
- msm_hs_write(uport, UART_DM_CSR, 0xff);
- rxstale = 31;
- break;
- case 4000000:
- case 3686400:
- case 3200000:
- case 3500000:
- case 3000000:
- case 2500000:
- case 2000000:
- case 1500000:
- case 1152000:
- case 1000000:
- case 921600:
- msm_hs_write(uport, UART_DM_CSR, 0xff);
- rxstale = 31;
- break;
- default:
- msm_hs_write(uport, UART_DM_CSR, 0xff);
- /* default to 9600 */
- bps = 9600;
- rxstale = 2;
- break;
- }
- /*
- * uart baud rate depends on CSR and MND Values
- * we are updating CSR before and then calling
- * clk_set_rate which updates MND Values. Hence
- * dsb requires here.
- */
- mb();
- if (bps > 460800) {
- uport->uartclk = bps * 16;
- /* BLSP based UART supports maximum clock frequency
- * of 63.16 Mhz. With this (63.16 Mhz) clock frequency
- * UART can support baud rate of 3.94 Mbps which is
- * equivalent to 4 Mbps.
- * UART hardware is robust enough to handle this
- * deviation to achieve baud rate ~4 Mbps.
- */
- if (bps == 4000000)
- uport->uartclk = BLSP_UART_CLK_FMAX;
- } else {
- uport->uartclk = 7372800;
- }
- if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
- MSM_HS_WARN("Error setting clock rate on UART\n");
- WARN_ON(1);
- }
- data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
- data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
- msm_hs_write(uport, UART_DM_IPR, data);
- /*
- * It is suggested to do reset of transmitter and receiver after
- * changing any protocol configuration. Here Baud rate and stale
- * timeout are getting updated. Hence reset transmitter and receiver.
- */
- msm_hs_write(uport, UART_DM_CR, RESET_TX);
- msm_hs_write(uport, UART_DM_CR, RESET_RX);
- }
- static void msm_hs_set_std_bps_locked(struct uart_port *uport,
- unsigned int bps)
- {
- unsigned long rxstale;
- unsigned long data;
- switch (bps) {
- case 9600:
- msm_hs_write(uport, UART_DM_CSR, 0x99);
- rxstale = 2;
- break;
- case 14400:
- msm_hs_write(uport, UART_DM_CSR, 0xaa);
- rxstale = 3;
- break;
- case 19200:
- msm_hs_write(uport, UART_DM_CSR, 0xbb);
- rxstale = 4;
- break;
- case 28800:
- msm_hs_write(uport, UART_DM_CSR, 0xcc);
- rxstale = 6;
- break;
- case 38400:
- msm_hs_write(uport, UART_DM_CSR, 0xdd);
- rxstale = 8;
- break;
- case 57600:
- msm_hs_write(uport, UART_DM_CSR, 0xee);
- rxstale = 16;
- break;
- case 115200:
- msm_hs_write(uport, UART_DM_CSR, 0xff);
- rxstale = 31;
- break;
- default:
- msm_hs_write(uport, UART_DM_CSR, 0x99);
- /* default to 9600 */
- bps = 9600;
- rxstale = 2;
- break;
- }
- data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
- data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
- msm_hs_write(uport, UART_DM_IPR, data);
- }
- static void msm_hs_enable_flow_control(struct uart_port *uport, bool override)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- unsigned int data;
- if (msm_uport->flow_control || override) {
- /* Enable RFR line */
- msm_hs_write(uport, UART_DM_CR, RFR_LOW);
- /* Enable auto RFR */
- data = msm_hs_read(uport, UART_DM_MR1);
- data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
- msm_hs_write(uport, UART_DM_MR1, data);
- /* Ensure register IO completion */
- mb();
- }
- }
- static void msm_hs_disable_flow_control(struct uart_port *uport, bool override)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- unsigned int data;
- /*
- * Clear the Rx Ready Ctl bit - This ensures that
- * flow control lines stop the other side from sending
- * data while we change the parameters
- */
- if (msm_uport->flow_control || override) {
- data = msm_hs_read(uport, UART_DM_MR1);
- /* disable auto ready-for-receiving */
- data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
- msm_hs_write(uport, UART_DM_MR1, data);
- /* Disable RFR line */
- msm_hs_write(uport, UART_DM_CR, RFR_HIGH);
- /* Ensure register IO completion */
- mb();
- }
- }
- /*
- * termios : new ktermios
- * oldtermios: old ktermios previous setting
- *
- * Configure the serial port
- */
- static void msm_hs_set_termios(struct uart_port *uport,
- struct ktermios *termios,
- struct ktermios *oldtermios)
- {
- unsigned int bps;
- unsigned long data;
- unsigned int c_cflag = termios->c_cflag;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- /**
- * set_termios can be invoked from the framework when
- * the clocks are off and the client has not had a chance
- * to turn them on. Make sure that they are on
- */
- msm_hs_resource_vote(msm_uport);
- mutex_lock(&msm_uport->mtx);
- msm_hs_write(uport, UART_DM_IMR, 0);
- msm_hs_disable_flow_control(uport, true);
- /*
- * Disable Rx channel of UARTDM
- * DMA Rx Stall happens if enqueue and flush of Rx command happens
- * concurrently. Hence before changing the baud rate/protocol
- * configuration and sending flush command to ADM, disable the Rx
- * channel of UARTDM.
- * Note: should not reset the receiver here immediately as it is not
- * suggested to do disable/reset or reset/disable at the same time.
- */
- data = msm_hs_read(uport, UART_DM_DMEN);
- /* Disable UARTDM RX BAM Interface */
- data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- /*
- * Reset RX and TX.
- * Resetting the RX enables it, therefore we must reset and disable.
- */
- msm_hs_write(uport, UART_DM_CR, RESET_RX);
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_DISABLE_BMSK);
- msm_hs_write(uport, UART_DM_CR, RESET_TX);
- /* 300 is the minimum baud support by the driver */
- bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000);
- /* Temporary remapping 200 BAUD to 3.2 mbps */
- if (bps == 200)
- bps = 3200000;
- uport->uartclk = clk_get_rate(msm_uport->clk);
- if (!uport->uartclk)
- msm_hs_set_std_bps_locked(uport, bps);
- else
- msm_hs_set_bps_locked(uport, bps);
- data = msm_hs_read(uport, UART_DM_MR2);
- data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
- /* set parity */
- if (c_cflag & PARENB) {
- if (c_cflag & PARODD)
- data |= ODD_PARITY;
- else if (c_cflag & CMSPAR)
- data |= SPACE_PARITY;
- else
- data |= EVEN_PARITY;
- }
- /* Set bits per char */
- data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK;
- switch (c_cflag & CSIZE) {
- case CS5:
- data |= FIVE_BPC;
- break;
- case CS6:
- data |= SIX_BPC;
- break;
- case CS7:
- data |= SEVEN_BPC;
- break;
- default:
- data |= EIGHT_BPC;
- break;
- }
- /* stop bits */
- if (c_cflag & CSTOPB) {
- data |= STOP_BIT_TWO;
- } else {
- /* otherwise 1 stop bit */
- data |= STOP_BIT_ONE;
- }
- data |= UARTDM_MR2_ERROR_MODE_BMSK;
- /* write parity/bits per char/stop bit configuration */
- msm_hs_write(uport, UART_DM_MR2, data);
- uport->ignore_status_mask = termios->c_iflag & INPCK;
- uport->ignore_status_mask |= termios->c_iflag & IGNPAR;
- uport->ignore_status_mask |= termios->c_iflag & IGNBRK;
- uport->read_status_mask = (termios->c_cflag & CREAD);
- /* Set Transmit software time out */
- uart_update_timeout(uport, c_cflag, bps);
- /* Enable UARTDM Rx BAM Interface */
- data = msm_hs_read(uport, UART_DM_DMEN);
- data |= UARTDM_RX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_EN_BMSK);
- /* Issue TX,RX BAM Start IFC command */
- msm_hs_write(uport, UART_DM_CR, START_TX_BAM_IFC);
- msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
- /* Ensure Register Writes Complete */
- mb();
- /* Configure HW flow control
- * UART Core would see status of CTS line when it is sending data
- * to remote uart to confirm that it can receive or not.
- * UART Core would trigger RFR if it is not having any space with
- * RX FIFO.
- */
- /* Pulling RFR line high */
- msm_hs_write(uport, UART_DM_CR, RFR_LOW);
- data = msm_hs_read(uport, UART_DM_MR1);
- data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK);
- if (c_cflag & CRTSCTS) {
- data |= UARTDM_MR1_CTS_CTL_BMSK;
- data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
- msm_uport->flow_control = true;
- }
- msm_hs_write(uport, UART_DM_MR1, data);
- MSM_HS_INFO("%s: Cflags 0x%x Baud %u\n", __func__, c_cflag, bps);
- mutex_unlock(&msm_uport->mtx);
- msm_hs_resource_unvote(msm_uport);
- }
- /*
- * Standard API, Transmitter
- * Any character in the transmit shift register is sent
- */
- unsigned int msm_hs_tx_empty(struct uart_port *uport)
- {
- unsigned int data;
- unsigned int isr;
- unsigned int ret = 0;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_hs_resource_vote(msm_uport);
- data = msm_hs_read(uport, UART_DM_SR);
- isr = msm_hs_read(uport, UART_DM_ISR);
- msm_hs_resource_unvote(msm_uport);
- MSM_HS_INFO("%s(): SR:0x%x ISR:0x%x ", __func__, data, isr);
- if (data & UARTDM_SR_TXEMT_BMSK) {
- ret = TIOCSER_TEMT;
- } else
- /*
- * Add an extra sleep here because sometimes the framework's
- * delay (based on baud rate) isn't good enough.
- * Note that this won't happen during every port close, only
- * on select occassions when the userspace does back to back
- * write() and close().
- */
- usleep_range(5000, 7000);
- return ret;
- }
- EXPORT_SYMBOL(msm_hs_tx_empty);
- /*
- * Standard API, Stop transmitter.
- * Any character in the transmit shift register is sent as
- * well as the current data mover transfer .
- */
- static void msm_hs_stop_tx_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_tx *tx = &msm_uport->tx;
- tx->flush = FLUSH_STOP;
- }
- static int disconnect_rx_endpoint(struct msm_hs_port *msm_uport)
- {
- struct msm_hs_rx *rx = &msm_uport->rx;
- struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
- int ret = 0;
- ret = sps_rx_disconnect(sps_pipe_handle);
- if (msm_uport->rx.pending_flag)
- MSM_HS_WARN("%s(): Buffers may be pending 0x%lx",
- __func__, msm_uport->rx.pending_flag);
- MSM_HS_DBG("%s(): clearing desc usage flag", __func__);
- msm_uport->rx.queued_flag = 0;
- msm_uport->rx.pending_flag = 0;
- msm_uport->rx.rx_inx = 0;
- if (ret)
- MSM_HS_ERR("%s(): sps_disconnect failed\n", __func__);
- msm_uport->rx.flush = FLUSH_SHUTDOWN;
- MSM_HS_DBG("%s: Calling Completion\n", __func__);
- wake_up(&msm_uport->bam_disconnect_wait);
- MSM_HS_DBG("%s: Done Completion\n", __func__);
- wake_up(&msm_uport->rx.wait);
- return ret;
- }
- static int sps_tx_disconnect(struct msm_hs_port *msm_uport)
- {
- struct uart_port *uport = &msm_uport->uport;
- struct msm_hs_tx *tx = &msm_uport->tx;
- struct sps_pipe *tx_pipe = tx->cons.pipe_handle;
- unsigned long flags;
- int ret = 0;
- if (msm_uport->tx.flush == FLUSH_SHUTDOWN) {
- MSM_HS_DBG("%s(): pipe already disonnected", __func__);
- return ret;
- }
- ret = sps_disconnect(tx_pipe);
- if (ret) {
- MSM_HS_ERR("%s(): sps_disconnect failed %d", __func__, ret);
- return ret;
- }
- spin_lock_irqsave(&uport->lock, flags);
- msm_uport->tx.flush = FLUSH_SHUTDOWN;
- spin_unlock_irqrestore(&uport->lock, flags);
- MSM_HS_DBG("%s(): TX Disconnect", __func__);
- return ret;
- }
- static void msm_hs_disable_rx(struct uart_port *uport)
- {
- unsigned int data;
- data = msm_hs_read(uport, UART_DM_DMEN);
- data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- }
- /*
- * Standard API, Stop receiver as soon as possible.
- *
- * Function immediately terminates the operation of the
- * channel receiver and any incoming characters are lost. None
- * of the receiver status bits are affected by this command and
- * characters that are already in the receive FIFO there.
- */
- static void msm_hs_stop_rx_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
- MSM_HS_WARN("%s(): Clocks are off\n", __func__);
- else
- msm_hs_disable_rx(uport);
- if (msm_uport->rx.flush == FLUSH_NONE)
- msm_uport->rx.flush = FLUSH_STOP;
- }
- static void msm_hs_disconnect_rx(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_hs_disable_rx(uport);
- /* Disconnect the BAM RX pipe */
- if (msm_uport->rx.flush == FLUSH_NONE)
- msm_uport->rx.flush = FLUSH_STOP;
- disconnect_rx_endpoint(msm_uport);
- MSM_HS_DBG("%s(): rx->flush %d", __func__, msm_uport->rx.flush);
- }
- /* Tx timeout callback function */
- void tx_timeout_handler(unsigned long arg)
- {
- struct msm_hs_port *msm_uport = (struct msm_hs_port *) arg;
- struct uart_port *uport = &msm_uport->uport;
- int isr;
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): clocks are off", __func__);
- return;
- }
- isr = msm_hs_read(uport, UART_DM_ISR);
- if (UARTDM_ISR_CURRENT_CTS_BMSK & isr)
- MSM_HS_WARN("%s(): CTS Disabled, ISR 0x%x", __func__, isr);
- dump_uart_hs_registers(msm_uport);
- }
- /* Transmit the next chunk of data */
- static void msm_hs_submit_tx_locked(struct uart_port *uport)
- {
- int left;
- int tx_count;
- int aligned_tx_count;
- dma_addr_t src_addr;
- dma_addr_t aligned_src_addr;
- u32 flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_tx *tx = &msm_uport->tx;
- struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
- struct sps_pipe *sps_pipe_handle;
- int ret;
- if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
- tx->dma_in_flight = false;
- msm_hs_stop_tx_locked(uport);
- return;
- }
- tx_count = uart_circ_chars_pending(tx_buf);
- if (tx_count > UARTDM_TX_BUF_SIZE)
- tx_count = UARTDM_TX_BUF_SIZE;
- left = UART_XMIT_SIZE - tx_buf->tail;
- if (tx_count > left)
- tx_count = left;
- src_addr = tx->dma_base + tx_buf->tail;
- /* Mask the src_addr to align on a cache
- * and add those bytes to tx_count
- */
- aligned_src_addr = src_addr & ~(dma_get_cache_alignment() - 1);
- aligned_tx_count = tx_count + src_addr - aligned_src_addr;
- dma_sync_single_for_device(uport->dev, aligned_src_addr,
- aligned_tx_count, DMA_TO_DEVICE);
- tx->tx_count = tx_count;
- hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx",
- &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count);
- sps_pipe_handle = tx->cons.pipe_handle;
- /* Set 1 second timeout */
- mod_timer(&tx->tx_timeout_timer,
- jiffies + msecs_to_jiffies(MSEC_PER_SEC));
- /* Queue transfer request to SPS */
- ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
- msm_uport, flags);
- MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret);
- }
- /* This function queues the rx descriptor for BAM transfer */
- static void msm_hs_post_rx_desc(struct msm_hs_port *msm_uport, int inx)
- {
- u32 flags = SPS_IOVEC_FLAG_INT;
- struct msm_hs_rx *rx = &msm_uport->rx;
- int ret;
- phys_addr_t rbuff_addr = rx->rbuffer + (UARTDM_RX_BUF_SIZE * inx);
- u8 *virt_addr = rx->buffer + (UARTDM_RX_BUF_SIZE * inx);
- MSM_HS_DBG("%s: %d:Queue desc %d, 0x%llx, base 0x%llx virtaddr %p",
- __func__, msm_uport->uport.line, inx,
- (u64)rbuff_addr, (u64)rx->rbuffer, virt_addr);
- rx->iovec[inx].size = 0;
- ret = sps_transfer_one(rx->prod.pipe_handle, rbuff_addr,
- UARTDM_RX_BUF_SIZE, msm_uport, flags);
- if (ret)
- MSM_HS_ERR("Error processing descriptor %d", ret);
- }
- /* Update the rx descriptor index to specify the next one to be processed */
- static void msm_hs_mark_next(struct msm_hs_port *msm_uport, int inx)
- {
- struct msm_hs_rx *rx = &msm_uport->rx;
- int prev;
- inx %= UART_DMA_DESC_NR;
- MSM_HS_DBG("%s(): inx %d, pending 0x%lx", __func__, inx,
- rx->pending_flag);
- if (!inx)
- prev = UART_DMA_DESC_NR - 1;
- else
- prev = inx - 1;
- if (!test_bit(prev, &rx->pending_flag))
- msm_uport->rx.rx_inx = inx;
- MSM_HS_DBG("%s(): prev %d pending flag 0x%lx, next %d", __func__,
- prev, rx->pending_flag, msm_uport->rx.rx_inx);
- }
- /*
- * Queue the rx descriptor that has just been processed or
- * all of them if queueing for the first time
- */
- static void msm_hs_queue_rx_desc(struct msm_hs_port *msm_uport)
- {
- struct msm_hs_rx *rx = &msm_uport->rx;
- int i, flag = 0;
- /* At first, queue all, if not, queue only one */
- if (rx->queued_flag || rx->pending_flag) {
- if (!test_bit(rx->rx_inx, &rx->queued_flag) &&
- !test_bit(rx->rx_inx, &rx->pending_flag)) {
- msm_hs_post_rx_desc(msm_uport, rx->rx_inx);
- set_bit(rx->rx_inx, &rx->queued_flag);
- MSM_HS_DBG("%s(): Set Queued Bit %d",
- __func__, rx->rx_inx);
- } else
- MSM_HS_ERR("%s(): rx_inx pending or queued", __func__);
- return;
- }
- for (i = 0; i < UART_DMA_DESC_NR; i++) {
- if (!test_bit(i, &rx->queued_flag) &&
- !test_bit(i, &rx->pending_flag)) {
- MSM_HS_DBG("%s(): Calling post rx %d", __func__, i);
- msm_hs_post_rx_desc(msm_uport, i);
- set_bit(i, &rx->queued_flag);
- flag = 1;
- }
- }
- if (!flag)
- MSM_HS_ERR("%s(): error queueing descriptor", __func__);
- }
- /* Start to receive the next chunk of data */
- static void msm_hs_start_rx_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_rx *rx = &msm_uport->rx;
- unsigned int buffer_pending = msm_uport->rx.buffer_pending;
- unsigned int data;
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): Clocks are off\n", __func__);
- return;
- }
- if (rx->pending_flag) {
- MSM_HS_INFO("%s: Rx Cmd got executed, wait for rx_tlet\n",
- __func__);
- rx->flush = FLUSH_IGNORE;
- return;
- }
- if (buffer_pending)
- MSM_HS_ERR("Error: rx started in buffer state =%x",
- buffer_pending);
- msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
- msm_hs_write(uport, UART_DM_DMRX, UARTDM_RX_BUF_SIZE);
- msm_hs_write(uport, UART_DM_CR, STALE_EVENT_ENABLE);
- /*
- * Enable UARTDM Rx Interface as previously it has been
- * disable in set_termios before configuring baud rate.
- */
- data = msm_hs_read(uport, UART_DM_DMEN);
- /* Enable UARTDM Rx BAM Interface */
- data |= UARTDM_RX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
- /* Calling next DMOV API. Hence mb() here. */
- mb();
- /*
- * RX-transfer will be automatically re-activated
- * after last data of previous transfer was read.
- */
- data = (RX_STALE_AUTO_RE_EN | RX_TRANS_AUTO_RE_ACTIVATE |
- RX_DMRX_CYCLIC_EN);
- msm_hs_write(uport, UART_DM_RX_TRANS_CTRL, data);
- /* Issue RX BAM Start IFC command */
- msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
- /* Ensure register IO completion */
- mb();
- msm_uport->rx.flush = FLUSH_NONE;
- msm_uport->rx_bam_inprogress = true;
- msm_hs_queue_rx_desc(msm_uport);
- msm_uport->rx_bam_inprogress = false;
- wake_up(&msm_uport->rx.wait);
- MSM_HS_DBG("%s:Enqueue Rx Cmd\n", __func__);
- }
- static void flip_insert_work(struct work_struct *work)
- {
- unsigned long flags;
- int retval;
- struct msm_hs_port *msm_uport =
- container_of(work, struct msm_hs_port,
- rx.flip_insert_work.work);
- struct tty_struct *tty = msm_uport->uport.state->port.tty;
- spin_lock_irqsave(&msm_uport->uport.lock, flags);
- if (!tty || msm_uport->rx.flush == FLUSH_SHUTDOWN) {
- dev_err(msm_uport->uport.dev,
- "%s:Invalid driver state flush %d\n",
- __func__, msm_uport->rx.flush);
- MSM_HS_ERR("%s:Invalid driver state flush %d\n",
- __func__, msm_uport->rx.flush);
- spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
- return;
- }
- if (msm_uport->rx.buffer_pending == NONE_PENDING) {
- MSM_HS_ERR("Error: No buffer pending in %s", __func__);
- spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
- return;
- }
- if (msm_uport->rx.buffer_pending & FIFO_OVERRUN) {
- retval = tty_insert_flip_char(tty->port, 0, TTY_OVERRUN);
- if (retval)
- msm_uport->rx.buffer_pending &= ~FIFO_OVERRUN;
- }
- if (msm_uport->rx.buffer_pending & PARITY_ERROR) {
- retval = tty_insert_flip_char(tty->port, 0, TTY_PARITY);
- if (retval)
- msm_uport->rx.buffer_pending &= ~PARITY_ERROR;
- }
- if (msm_uport->rx.buffer_pending & CHARS_NORMAL) {
- int rx_count, rx_offset;
- rx_count = (msm_uport->rx.buffer_pending & 0xFFFF0000) >> 16;
- rx_offset = (msm_uport->rx.buffer_pending & 0xFFD0) >> 5;
- retval = tty_insert_flip_string(tty->port,
- msm_uport->rx.buffer +
- (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)
- + rx_offset, rx_count);
- msm_uport->rx.buffer_pending &= (FIFO_OVERRUN |
- PARITY_ERROR);
- if (retval != rx_count)
- msm_uport->rx.buffer_pending |= CHARS_NORMAL |
- retval << 8 | (rx_count - retval) << 16;
- }
- if (msm_uport->rx.buffer_pending) {
- schedule_delayed_work(&msm_uport->rx.flip_insert_work,
- msecs_to_jiffies(RETRY_TIMEOUT));
- } else if (msm_uport->rx.flush <= FLUSH_IGNORE) {
- MSM_HS_WARN("Pending buffers cleared, restarting");
- clear_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.pending_flag);
- msm_hs_start_rx_locked(&msm_uport->uport);
- msm_hs_mark_next(msm_uport, msm_uport->rx.rx_inx+1);
- }
- spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
- tty_flip_buffer_push(tty->port);
- }
- static void msm_serial_hs_rx_work(struct kthread_work *work)
- {
- int retval;
- int rx_count = 0;
- unsigned long status;
- unsigned long flags;
- unsigned int error_f = 0;
- struct uart_port *uport;
- struct msm_hs_port *msm_uport;
- unsigned int flush = FLUSH_DATA_INVALID;
- struct tty_struct *tty;
- struct sps_event_notify *notify;
- struct msm_hs_rx *rx;
- struct sps_pipe *sps_pipe_handle;
- struct platform_device *pdev;
- const struct msm_serial_hs_platform_data *pdata;
- msm_uport = container_of((struct kthread_work *) work,
- struct msm_hs_port, rx.kwork);
- msm_hs_resource_vote(msm_uport);
- uport = &msm_uport->uport;
- tty = uport->state->port.tty;
- notify = &msm_uport->notify;
- rx = &msm_uport->rx;
- pdev = to_platform_device(uport->dev);
- pdata = pdev->dev.platform_data;
- spin_lock_irqsave(&uport->lock, flags);
- if (!tty || rx->flush == FLUSH_SHUTDOWN) {
- dev_err(uport->dev, "%s:Invalid driver state flush %d\n",
- __func__, rx->flush);
- MSM_HS_ERR("%s:Invalid driver state flush %d\n",
- __func__, rx->flush);
- spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
- return;
- }
- /*
- * Process all pending descs or if nothing is
- * queued - called from termios
- */
- while (!rx->buffer_pending &&
- (rx->pending_flag || !rx->queued_flag)) {
- MSM_HS_DBG("%s(): Loop P 0x%lx Q 0x%lx", __func__,
- rx->pending_flag, rx->queued_flag);
- status = msm_hs_read(uport, UART_DM_SR);
- MSM_HS_DBG("In %s\n", __func__);
- /* overflow is not connect to data in a FIFO */
- if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
- (uport->read_status_mask & CREAD))) {
- retval = tty_insert_flip_char(tty->port,
- 0, TTY_OVERRUN);
- MSM_HS_WARN("%s(): RX Buffer Overrun Detected\n",
- __func__);
- if (!retval)
- msm_uport->rx.buffer_pending |= TTY_OVERRUN;
- uport->icount.buf_overrun++;
- error_f = 1;
- }
- if (!(uport->ignore_status_mask & INPCK))
- status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
- if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
- /* Can not tell diff between parity & frame error */
- MSM_HS_WARN("msm_serial_hs: parity error\n");
- uport->icount.parity++;
- error_f = 1;
- if (!(uport->ignore_status_mask & IGNPAR)) {
- retval = tty_insert_flip_char(tty->port,
- 0, TTY_PARITY);
- if (!retval)
- msm_uport->rx.buffer_pending
- |= TTY_PARITY;
- }
- }
- if (unlikely(status & UARTDM_SR_RX_BREAK_BMSK)) {
- MSM_HS_DBG("msm_serial_hs: Rx break\n");
- uport->icount.brk++;
- error_f = 1;
- if (!(uport->ignore_status_mask & IGNBRK)) {
- retval = tty_insert_flip_char(tty->port,
- 0, TTY_BREAK);
- if (!retval)
- msm_uport->rx.buffer_pending
- |= TTY_BREAK;
- }
- }
- if (error_f)
- msm_hs_write(uport, UART_DM_CR, RESET_ERROR_STATUS);
- flush = msm_uport->rx.flush;
- if (flush == FLUSH_IGNORE)
- if (!msm_uport->rx.buffer_pending) {
- MSM_HS_DBG("%s: calling start_rx_locked\n",
- __func__);
- msm_hs_start_rx_locked(uport);
- }
- if (flush >= FLUSH_DATA_INVALID)
- goto out;
- rx_count = msm_uport->rx.iovec[msm_uport->rx.rx_inx].size;
- hex_dump_ipc(msm_uport, rx->ipc_rx_ctxt, "Rx",
- (msm_uport->rx.buffer +
- (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)),
- msm_uport->rx.iovec[msm_uport->rx.rx_inx].addr,
- rx_count);
- /*
- * We are in a spin locked context, spin lock taken at
- * other places where these flags are updated
- */
- if (0 != (uport->read_status_mask & CREAD)) {
- if (!test_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.pending_flag) &&
- !test_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.queued_flag))
- MSM_HS_ERR("%s: RX INX not set", __func__);
- else if (test_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.pending_flag) &&
- !test_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.queued_flag)) {
- MSM_HS_DBG("%s(): Clear Pending Bit %d",
- __func__, msm_uport->rx.rx_inx);
- retval = tty_insert_flip_string(tty->port,
- msm_uport->rx.buffer +
- (msm_uport->rx.rx_inx *
- UARTDM_RX_BUF_SIZE),
- rx_count);
- if (retval != rx_count) {
- MSM_HS_INFO("%s(): ret %d rx_count %d",
- __func__, retval, rx_count);
- msm_uport->rx.buffer_pending |=
- CHARS_NORMAL | retval << 5 |
- (rx_count - retval) << 16;
- }
- } else
- MSM_HS_ERR("%s: Error in inx %d", __func__,
- msm_uport->rx.rx_inx);
- }
- if (!msm_uport->rx.buffer_pending) {
- msm_uport->rx.flush = FLUSH_NONE;
- msm_uport->rx_bam_inprogress = true;
- sps_pipe_handle = rx->prod.pipe_handle;
- MSM_HS_DBG("Queing bam descriptor\n");
- /* Queue transfer request to SPS */
- clear_bit(msm_uport->rx.rx_inx,
- &msm_uport->rx.pending_flag);
- msm_hs_queue_rx_desc(msm_uport);
- msm_hs_mark_next(msm_uport, msm_uport->rx.rx_inx+1);
- msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
- msm_uport->rx_bam_inprogress = false;
- wake_up(&msm_uport->rx.wait);
- } else
- break;
- }
- out:
- if (msm_uport->rx.buffer_pending) {
- MSM_HS_WARN("%s: tty buffer exhausted. Stalling\n", __func__);
- schedule_delayed_work(&msm_uport->rx.flip_insert_work
- , msecs_to_jiffies(RETRY_TIMEOUT));
- }
- /* tty_flip_buffer_push() might call msm_hs_start(), so unlock */
- spin_unlock_irqrestore(&uport->lock, flags);
- if (flush < FLUSH_DATA_INVALID)
- tty_flip_buffer_push(tty->port);
- msm_hs_resource_unvote(msm_uport);
- }
- static void msm_hs_start_tx_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_tx *tx = &msm_uport->tx;
- /* Bail if transfer in progress */
- if (tx->flush < FLUSH_STOP || tx->dma_in_flight) {
- MSM_HS_INFO("%s(): retry, flush %d, dma_in_flight %d\n",
- __func__, tx->flush, tx->dma_in_flight);
- return;
- }
- if (!tx->dma_in_flight) {
- tx->dma_in_flight = true;
- kthread_queue_work(&msm_uport->tx.kworker,
- &msm_uport->tx.kwork);
- }
- }
- /**
- * Callback notification from SPS driver
- *
- * This callback function gets triggered called from
- * SPS driver when requested SPS data transfer is
- * completed.
- *
- */
- static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
- {
- struct msm_hs_port *msm_uport =
- (struct msm_hs_port *)
- ((struct sps_event_notify *)notify)->user;
- phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
- notify->data.transfer.iovec.addr);
- msm_uport->notify = *notify;
- MSM_HS_INFO("tx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
- &addr, notify->data.transfer.iovec.size,
- notify->data.transfer.iovec.flags);
- del_timer(&msm_uport->tx.tx_timeout_timer);
- MSM_HS_DBG("%s(): Queue kthread work", __func__);
- kthread_queue_work(&msm_uport->tx.kworker, &msm_uport->tx.kwork);
- }
- static void msm_serial_hs_tx_work(struct kthread_work *work)
- {
- unsigned long flags;
- struct msm_hs_port *msm_uport =
- container_of((struct kthread_work *)work,
- struct msm_hs_port, tx.kwork);
- struct uart_port *uport = &msm_uport->uport;
- struct circ_buf *tx_buf = &uport->state->xmit;
- struct msm_hs_tx *tx = &msm_uport->tx;
- /*
- * Do the work buffer related work in BAM
- * mode that is equivalent to legacy mode
- */
- msm_hs_resource_vote(msm_uport);
- if (tx->flush >= FLUSH_STOP) {
- spin_lock_irqsave(&(msm_uport->uport.lock), flags);
- tx->flush = FLUSH_NONE;
- MSM_HS_DBG("%s(): calling submit_tx", __func__);
- msm_hs_submit_tx_locked(uport);
- spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
- msm_hs_resource_unvote(msm_uport);
- return;
- }
- spin_lock_irqsave(&(msm_uport->uport.lock), flags);
- if (!uart_circ_empty(tx_buf))
- tx_buf->tail = (tx_buf->tail +
- tx->tx_count) & ~UART_XMIT_SIZE;
- else
- MSM_HS_DBG("%s:circ buffer is empty\n", __func__);
- wake_up(&msm_uport->tx.wait);
- uport->icount.tx += tx->tx_count;
- /*
- * Calling to send next chunk of data
- * If the circ buffer is empty, we stop
- * If the clock off was requested, the clock
- * off sequence is kicked off
- */
- MSM_HS_DBG("%s(): calling submit_tx", __func__);
- msm_hs_submit_tx_locked(uport);
- if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
- uart_write_wakeup(uport);
- spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
- msm_hs_resource_unvote(msm_uport);
- }
- static void
- msm_hs_mark_proc_rx_desc(struct msm_hs_port *msm_uport,
- struct sps_event_notify *notify)
- {
- struct msm_hs_rx *rx = &msm_uport->rx;
- phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
- notify->data.transfer.iovec.addr);
- /* divide by UARTDM_RX_BUF_SIZE */
- int inx = (addr - rx->rbuffer) >> 9;
- set_bit(inx, &rx->pending_flag);
- clear_bit(inx, &rx->queued_flag);
- rx->iovec[inx] = notify->data.transfer.iovec;
- MSM_HS_DBG("Clear Q, Set P Bit %d, Q 0x%lx P 0x%lx",
- inx, rx->queued_flag, rx->pending_flag);
- }
- /**
- * Callback notification from SPS driver
- *
- * This callback function gets triggered called from
- * SPS driver when requested SPS data transfer is
- * completed.
- *
- */
- static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
- {
- struct msm_hs_port *msm_uport =
- (struct msm_hs_port *)
- ((struct sps_event_notify *)notify)->user;
- struct uart_port *uport;
- unsigned long flags;
- struct msm_hs_rx *rx = &msm_uport->rx;
- phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
- notify->data.transfer.iovec.addr);
- /* divide by UARTDM_RX_BUF_SIZE */
- int inx = (addr - rx->rbuffer) >> 9;
- uport = &(msm_uport->uport);
- msm_uport->notify = *notify;
- MSM_HS_INFO("rx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
- &addr, notify->data.transfer.iovec.size,
- notify->data.transfer.iovec.flags);
- spin_lock_irqsave(&uport->lock, flags);
- msm_hs_mark_proc_rx_desc(msm_uport, notify);
- spin_unlock_irqrestore(&uport->lock, flags);
- if (msm_uport->rx.flush == FLUSH_NONE) {
- /* Test if others are queued */
- if (msm_uport->rx.pending_flag & ~(1 << inx)) {
- MSM_HS_DBG("%s(): inx 0x%x, 0x%lx not processed",
- __func__, inx,
- msm_uport->rx.pending_flag & ~(1<<inx));
- }
- kthread_queue_work(&msm_uport->rx.kworker,
- &msm_uport->rx.kwork);
- MSM_HS_DBG("%s(): Scheduled rx_tlet", __func__);
- }
- }
- /*
- * Standard API, Current states of modem control inputs
- *
- * Since CTS can be handled entirely by HARDWARE we always
- * indicate clear to send and count on the TX FIFO to block when
- * it fills up.
- *
- * - TIOCM_DCD
- * - TIOCM_CTS
- * - TIOCM_DSR
- * - TIOCM_RI
- * (Unsupported) DCD and DSR will return them high. RI will return low.
- */
- static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport)
- {
- return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
- }
- /*
- * Standard API, Set or clear RFR_signal
- *
- * Set RFR high, (Indicate we are not ready for data), we disable auto
- * ready for receiving and then set RFR_N high. To set RFR to low we just turn
- * back auto ready for receiving and it should lower RFR signal
- * when hardware is ready
- */
- void msm_hs_set_mctrl_locked(struct uart_port *uport,
- unsigned int mctrl)
- {
- unsigned int set_rts;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): Clocks are off\n", __func__);
- return;
- }
- /* RTS is active low */
- set_rts = TIOCM_RTS & mctrl ? 0 : 1;
- MSM_HS_INFO("%s: set_rts %d\n", __func__, set_rts);
- if (set_rts)
- msm_hs_disable_flow_control(uport, false);
- else
- msm_hs_enable_flow_control(uport, false);
- }
- void msm_hs_set_mctrl(struct uart_port *uport,
- unsigned int mctrl)
- {
- unsigned long flags;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_hs_resource_vote(msm_uport);
- spin_lock_irqsave(&uport->lock, flags);
- msm_hs_set_mctrl_locked(uport, mctrl);
- spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
- }
- EXPORT_SYMBOL(msm_hs_set_mctrl);
- /* Standard API, Enable modem status (CTS) interrupt */
- static void msm_hs_enable_ms_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): Clocks are off\n", __func__);
- return;
- }
- /* Enable DELTA_CTS Interrupt */
- msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK;
- msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
- /* Ensure register IO completion */
- mb();
- }
- /*
- * Standard API, Break Signal
- *
- * Control the transmission of a break signal. ctl eq 0 => break
- * signal terminate ctl ne 0 => start break signal
- */
- static void msm_hs_break_ctl(struct uart_port *uport, int ctl)
- {
- unsigned long flags;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_hs_resource_vote(msm_uport);
- spin_lock_irqsave(&uport->lock, flags);
- msm_hs_write(uport, UART_DM_CR, ctl ? START_BREAK : STOP_BREAK);
- /* Ensure register IO completion */
- mb();
- spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
- }
- static void msm_hs_config_port(struct uart_port *uport, int cfg_flags)
- {
- if (cfg_flags & UART_CONFIG_TYPE)
- uport->type = PORT_MSM;
- }
- /* Handle CTS changes (Called from interrupt handler) */
- static void msm_hs_handle_delta_cts_locked(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_hs_resource_vote(msm_uport);
- /* clear interrupt */
- msm_hs_write(uport, UART_DM_CR, RESET_CTS);
- /* Calling CLOCK API. Hence mb() requires here. */
- mb();
- uport->icount.cts++;
- /* clear the IOCTL TIOCMIWAIT if called */
- wake_up_interruptible(&uport->state->port.delta_msr_wait);
- msm_hs_resource_unvote(msm_uport);
- }
- static irqreturn_t msm_hs_isr(int irq, void *dev)
- {
- unsigned long flags;
- unsigned int isr_status;
- struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
- struct uart_port *uport = &msm_uport->uport;
- struct circ_buf *tx_buf = &uport->state->xmit;
- struct msm_hs_tx *tx = &msm_uport->tx;
- spin_lock_irqsave(&uport->lock, flags);
- isr_status = msm_hs_read(uport, UART_DM_MISR);
- MSM_HS_INFO("%s: DM_ISR: 0x%x\n", __func__, isr_status);
- dump_uart_hs_registers(msm_uport);
- /* Uart RX starting */
- if (isr_status & UARTDM_ISR_RXLEV_BMSK) {
- MSM_HS_DBG("%s:UARTDM_ISR_RXLEV_BMSK\n", __func__);
- msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK;
- msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
- /* Complete device write for IMR. Hence mb() requires. */
- mb();
- }
- /* Stale rx interrupt */
- if (isr_status & UARTDM_ISR_RXSTALE_BMSK) {
- msm_hs_write(uport, UART_DM_CR, STALE_EVENT_DISABLE);
- msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
- /*
- * Complete device write before calling DMOV API. Hence
- * mb() requires here.
- */
- mb();
- MSM_HS_DBG("%s:Stal Interrupt\n", __func__);
- }
- /* tx ready interrupt */
- if (isr_status & UARTDM_ISR_TX_READY_BMSK) {
- MSM_HS_DBG("%s: ISR_TX_READY Interrupt\n", __func__);
- /* Clear TX Ready */
- msm_hs_write(uport, UART_DM_CR, CLEAR_TX_READY);
- /*
- * Complete both writes before starting new TX.
- * Hence mb() requires here.
- */
- mb();
- /* Complete DMA TX transactions and submit new transactions */
- /* Do not update tx_buf.tail if uart_flush_buffer already
- * called in serial core
- */
- if (!uart_circ_empty(tx_buf))
- tx_buf->tail = (tx_buf->tail +
- tx->tx_count) & ~UART_XMIT_SIZE;
- tx->dma_in_flight = false;
- uport->icount.tx += tx->tx_count;
- if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
- uart_write_wakeup(uport);
- }
- if (isr_status & UARTDM_ISR_TXLEV_BMSK) {
- /* TX FIFO is empty */
- msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK;
- msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
- MSM_HS_DBG("%s: TXLEV Interrupt\n", __func__);
- /*
- * Complete device write before starting clock_off request.
- * Hence mb() requires here.
- */
- mb();
- queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w);
- }
- /* Change in CTS interrupt */
- if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK)
- msm_hs_handle_delta_cts_locked(uport);
- spin_unlock_irqrestore(&uport->lock, flags);
- return IRQ_HANDLED;
- }
- /* The following two functions provide interfaces to get the underlying
- * port structure (struct uart_port or struct msm_hs_port) given
- * the port index. msm_hs_get_uart port is called by clients.
- * The function msm_hs_get_hs_port is for internal use
- */
- struct uart_port *msm_hs_get_uart_port(int port_index)
- {
- struct uart_state *state = msm_hs_driver.state + port_index;
- /* The uart_driver structure stores the states in an array.
- * Thus the corresponding offset from the drv->state returns
- * the state for the uart_port that is requested
- */
- if (port_index == state->uart_port->line)
- return state->uart_port;
- return NULL;
- }
- EXPORT_SYMBOL(msm_hs_get_uart_port);
- static struct msm_hs_port *msm_hs_get_hs_port(int port_index)
- {
- struct uart_port *uport = msm_hs_get_uart_port(port_index);
- if (uport)
- return UARTDM_TO_MSM(uport);
- return NULL;
- }
- void enable_wakeup_interrupt(struct msm_hs_port *msm_uport)
- {
- unsigned long flags;
- struct uart_port *uport = &(msm_uport->uport);
- if (!is_use_low_power_wakeup(msm_uport))
- return;
- if (msm_uport->wakeup.freed)
- return;
- if (!(msm_uport->wakeup.enabled)) {
- spin_lock_irqsave(&uport->lock, flags);
- msm_uport->wakeup.ignore = 1;
- msm_uport->wakeup.enabled = true;
- spin_unlock_irqrestore(&uport->lock, flags);
- disable_irq(uport->irq);
- enable_irq(msm_uport->wakeup.irq);
- } else {
- MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__);
- }
- }
- void disable_wakeup_interrupt(struct msm_hs_port *msm_uport)
- {
- unsigned long flags;
- struct uart_port *uport = &(msm_uport->uport);
- if (!is_use_low_power_wakeup(msm_uport))
- return;
- if (msm_uport->wakeup.freed)
- return;
- if (msm_uport->wakeup.enabled) {
- disable_irq_nosync(msm_uport->wakeup.irq);
- enable_irq(uport->irq);
- spin_lock_irqsave(&uport->lock, flags);
- msm_uport->wakeup.enabled = false;
- spin_unlock_irqrestore(&uport->lock, flags);
- } else {
- MSM_HS_WARN("%s:Wake up IRQ already disabled", __func__);
- }
- }
- void msm_hs_resource_off(struct msm_hs_port *msm_uport)
- {
- struct uart_port *uport = &(msm_uport->uport);
- unsigned int data;
- MSM_HS_DBG("%s(): begin", __func__);
- msm_hs_disable_flow_control(uport, false);
- if (msm_uport->rx.flush == FLUSH_NONE)
- msm_hs_disconnect_rx(uport);
- /* disable dlink */
- if (msm_uport->tx.flush == FLUSH_NONE)
- wait_event_timeout(msm_uport->tx.wait,
- msm_uport->tx.flush == FLUSH_STOP, 500);
- if (msm_uport->tx.flush != FLUSH_SHUTDOWN) {
- data = msm_hs_read(uport, UART_DM_DMEN);
- data &= ~UARTDM_TX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- sps_tx_disconnect(msm_uport);
- }
- if (!atomic_read(&msm_uport->client_req_state))
- msm_hs_enable_flow_control(uport, false);
- }
- void msm_hs_resource_on(struct msm_hs_port *msm_uport)
- {
- struct uart_port *uport = &(msm_uport->uport);
- unsigned int data;
- unsigned long flags;
- if (msm_uport->rx.flush == FLUSH_SHUTDOWN ||
- msm_uport->rx.flush == FLUSH_STOP) {
- msm_hs_write(uport, UART_DM_CR, RESET_RX);
- data = msm_hs_read(uport, UART_DM_DMEN);
- data |= UARTDM_RX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- }
- msm_hs_spsconnect_tx(msm_uport);
- if (msm_uport->rx.flush == FLUSH_SHUTDOWN) {
- msm_hs_spsconnect_rx(uport);
- spin_lock_irqsave(&uport->lock, flags);
- msm_hs_start_rx_locked(uport);
- spin_unlock_irqrestore(&uport->lock, flags);
- }
- }
- /* Request to turn off uart clock once pending TX is flushed */
- int msm_hs_request_clock_off(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- int ret = 0;
- int client_count = 0;
- mutex_lock(&msm_uport->mtx);
- /*
- * If we're in the middle of a system suspend, don't process these
- * userspace/kernel API commands.
- */
- if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
- MSM_HS_WARN("%s:Can't process clk request during suspend",
- __func__);
- ret = -EIO;
- }
- mutex_unlock(&msm_uport->mtx);
- if (ret)
- goto exit_request_clock_off;
- if (atomic_read(&msm_uport->client_count) <= 0) {
- MSM_HS_WARN("%s(): ioctl count -ve, client check voting",
- __func__);
- ret = -EPERM;
- goto exit_request_clock_off;
- }
- /* Set the flag to disable flow control and wakeup irq */
- if (msm_uport->obs)
- atomic_set(&msm_uport->client_req_state, 1);
- msm_hs_resource_unvote(msm_uport);
- atomic_dec(&msm_uport->client_count);
- client_count = atomic_read(&msm_uport->client_count);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s: Client_Count %d\n", __func__,
- client_count);
- exit_request_clock_off:
- return ret;
- }
- EXPORT_SYMBOL(msm_hs_request_clock_off);
- int msm_hs_request_clock_on(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- int client_count;
- int ret = 0;
- mutex_lock(&msm_uport->mtx);
- /*
- * If we're in the middle of a system suspend, don't process these
- * userspace/kernel API commands.
- */
- if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
- MSM_HS_WARN("%s:Can't process clk request during suspend",
- __func__);
- ret = -EIO;
- }
- mutex_unlock(&msm_uport->mtx);
- if (ret)
- goto exit_request_clock_on;
- msm_hs_resource_vote(UARTDM_TO_MSM(uport));
- atomic_inc(&msm_uport->client_count);
- client_count = atomic_read(&msm_uport->client_count);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s: Client_Count %d\n", __func__,
- client_count);
- /* Clear the flag */
- if (msm_uport->obs)
- atomic_set(&msm_uport->client_req_state, 0);
- exit_request_clock_on:
- return ret;
- }
- EXPORT_SYMBOL(msm_hs_request_clock_on);
- static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev)
- {
- unsigned int wakeup = 0;
- unsigned long flags;
- struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
- struct uart_port *uport = &msm_uport->uport;
- struct tty_struct *tty = NULL;
- spin_lock_irqsave(&uport->lock, flags);
- if (msm_uport->wakeup.ignore)
- msm_uport->wakeup.ignore = 0;
- else
- wakeup = 1;
- if (wakeup) {
- /*
- * Port was clocked off during rx, wake up and
- * optionally inject char into tty rx
- */
- if (msm_uport->wakeup.inject_rx) {
- tty = uport->state->port.tty;
- tty_insert_flip_char(tty->port,
- msm_uport->wakeup.rx_to_inject,
- TTY_NORMAL);
- hex_dump_ipc(msm_uport, msm_uport->rx.ipc_rx_ctxt,
- "Rx Inject",
- &msm_uport->wakeup.rx_to_inject, 0, 1);
- MSM_HS_INFO("Wakeup ISR.Ignore%d\n",
- msm_uport->wakeup.ignore);
- }
- }
- spin_unlock_irqrestore(&uport->lock, flags);
- if (wakeup && msm_uport->wakeup.inject_rx)
- tty_flip_buffer_push(tty->port);
- return IRQ_HANDLED;
- }
- static const char *msm_hs_type(struct uart_port *port)
- {
- return "MSM HS UART";
- }
- /**
- * msm_hs_unconfig_uart_gpios: Unconfigures UART GPIOs
- * @uport: uart port
- */
- static void msm_hs_unconfig_uart_gpios(struct uart_port *uport)
- {
- struct platform_device *pdev = to_platform_device(uport->dev);
- const struct msm_serial_hs_platform_data *pdata =
- pdev->dev.platform_data;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- int ret;
- if (msm_uport->use_pinctrl) {
- ret = pinctrl_select_state(msm_uport->pinctrl,
- msm_uport->gpio_state_suspend);
- if (ret)
- MSM_HS_ERR("%s():Failed to pinctrl set_state",
- __func__);
- } else if (pdata) {
- if (gpio_is_valid(pdata->uart_tx_gpio))
- gpio_free(pdata->uart_tx_gpio);
- if (gpio_is_valid(pdata->uart_rx_gpio))
- gpio_free(pdata->uart_rx_gpio);
- if (gpio_is_valid(pdata->uart_cts_gpio))
- gpio_free(pdata->uart_cts_gpio);
- if (gpio_is_valid(pdata->uart_rfr_gpio))
- gpio_free(pdata->uart_rfr_gpio);
- } else
- MSM_HS_ERR("Error:Pdata is NULL.\n");
- }
- /**
- * msm_hs_config_uart_gpios - Configures UART GPIOs
- * @uport: uart port
- */
- static int msm_hs_config_uart_gpios(struct uart_port *uport)
- {
- struct platform_device *pdev = to_platform_device(uport->dev);
- const struct msm_serial_hs_platform_data *pdata =
- pdev->dev.platform_data;
- int ret = 0;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- if (!IS_ERR_OR_NULL(msm_uport->pinctrl)) {
- MSM_HS_DBG("%s(): Using Pinctrl", __func__);
- msm_uport->use_pinctrl = true;
- ret = pinctrl_select_state(msm_uport->pinctrl,
- msm_uport->gpio_state_active);
- if (ret)
- MSM_HS_ERR("%s(): Failed to pinctrl set_state",
- __func__);
- return ret;
- } else if (pdata) {
- /* Fall back to using gpio lib */
- if (gpio_is_valid(pdata->uart_tx_gpio)) {
- ret = gpio_request(pdata->uart_tx_gpio,
- "UART_TX_GPIO");
- if (unlikely(ret)) {
- MSM_HS_ERR("gpio request failed for:%d\n",
- pdata->uart_tx_gpio);
- goto exit_uart_config;
- }
- }
- if (gpio_is_valid(pdata->uart_rx_gpio)) {
- ret = gpio_request(pdata->uart_rx_gpio,
- "UART_RX_GPIO");
- if (unlikely(ret)) {
- MSM_HS_ERR("gpio request failed for:%d\n",
- pdata->uart_rx_gpio);
- goto uart_tx_unconfig;
- }
- }
- if (gpio_is_valid(pdata->uart_cts_gpio)) {
- ret = gpio_request(pdata->uart_cts_gpio,
- "UART_CTS_GPIO");
- if (unlikely(ret)) {
- MSM_HS_ERR("gpio request failed for:%d\n",
- pdata->uart_cts_gpio);
- goto uart_rx_unconfig;
- }
- }
- if (gpio_is_valid(pdata->uart_rfr_gpio)) {
- ret = gpio_request(pdata->uart_rfr_gpio,
- "UART_RFR_GPIO");
- if (unlikely(ret)) {
- MSM_HS_ERR("gpio request failed for:%d\n",
- pdata->uart_rfr_gpio);
- goto uart_cts_unconfig;
- }
- }
- } else {
- MSM_HS_ERR("Pdata is NULL.\n");
- ret = -EINVAL;
- }
- return ret;
- uart_cts_unconfig:
- if (gpio_is_valid(pdata->uart_cts_gpio))
- gpio_free(pdata->uart_cts_gpio);
- uart_rx_unconfig:
- if (gpio_is_valid(pdata->uart_rx_gpio))
- gpio_free(pdata->uart_rx_gpio);
- uart_tx_unconfig:
- if (gpio_is_valid(pdata->uart_tx_gpio))
- gpio_free(pdata->uart_tx_gpio);
- exit_uart_config:
- return ret;
- }
- static void msm_hs_get_pinctrl_configs(struct uart_port *uport)
- {
- struct pinctrl_state *set_state;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_uport->pinctrl = devm_pinctrl_get(uport->dev);
- if (IS_ERR_OR_NULL(msm_uport->pinctrl)) {
- MSM_HS_DBG("%s(): Pinctrl not defined", __func__);
- } else {
- MSM_HS_DBG("%s(): Using Pinctrl", __func__);
- msm_uport->use_pinctrl = true;
- set_state = pinctrl_lookup_state(msm_uport->pinctrl,
- PINCTRL_STATE_DEFAULT);
- if (IS_ERR_OR_NULL(set_state)) {
- dev_err(uport->dev,
- "pinctrl lookup failed for default state");
- goto pinctrl_fail;
- }
- MSM_HS_DBG("%s(): Pinctrl state active %p\n", __func__,
- set_state);
- msm_uport->gpio_state_active = set_state;
- set_state = pinctrl_lookup_state(msm_uport->pinctrl,
- PINCTRL_STATE_SLEEP);
- if (IS_ERR_OR_NULL(set_state)) {
- dev_err(uport->dev,
- "pinctrl lookup failed for sleep state");
- goto pinctrl_fail;
- }
- MSM_HS_DBG("%s(): Pinctrl state sleep %p\n", __func__,
- set_state);
- msm_uport->gpio_state_suspend = set_state;
- return;
- }
- pinctrl_fail:
- msm_uport->pinctrl = NULL;
- }
- /* Called when port is opened */
- static int msm_hs_startup(struct uart_port *uport)
- {
- int ret;
- int rfr_level;
- unsigned long flags;
- unsigned int data;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct circ_buf *tx_buf = &uport->state->xmit;
- struct msm_hs_tx *tx = &msm_uport->tx;
- struct msm_hs_rx *rx = &msm_uport->rx;
- struct sps_pipe *sps_pipe_handle_tx = tx->cons.pipe_handle;
- struct sps_pipe *sps_pipe_handle_rx = rx->prod.pipe_handle;
- rfr_level = uport->fifosize;
- if (rfr_level > 16)
- rfr_level -= 16;
- tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE,
- DMA_TO_DEVICE);
- /* turn on uart clk */
- msm_hs_resource_vote(msm_uport);
- /* Set up Uart Receive */
- msm_hs_write(uport, UART_DM_RFWR, 32);
- /* Write to BADR explicitly to set up FIFO sizes */
- msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
- /* configure the CR Protection to Enable */
- msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
- /*
- * Enable Command register protection before going ahead as this hw
- * configuration makes sure that issued cmd to CR register gets complete
- * before next issued cmd start. Hence mb() requires here.
- */
- mb();
- /*
- * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
- * so any rx_break and character having parity of framing
- * error don't enter inside UART RX FIFO.
- */
- data = msm_hs_read(uport, UART_DM_MR2);
- data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
- UARTDM_MR2_RX_ERROR_CHAR_OFF);
- msm_hs_write(uport, UART_DM_MR2, data);
- /* Ensure register IO completion */
- mb();
- if (is_use_low_power_wakeup(msm_uport)) {
- ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
- msm_hs_wakeup_isr,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "msm_hs_wakeup", msm_uport);
- if (unlikely(ret)) {
- MSM_HS_ERR("%s():Err getting uart wakeup_irq %d\n",
- __func__, ret);
- goto unvote_exit;
- }
- msm_uport->wakeup.freed = false;
- disable_irq(msm_uport->wakeup.irq);
- msm_uport->wakeup.enabled = false;
- ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
- if (unlikely(ret)) {
- MSM_HS_ERR("%s():Err setting wakeup irq\n", __func__);
- goto free_uart_irq;
- }
- }
- ret = msm_hs_config_uart_gpios(uport);
- if (ret) {
- MSM_HS_ERR("Uart GPIO request failed\n");
- goto free_uart_irq;
- }
- msm_hs_write(uport, UART_DM_DMEN, 0);
- /* Connect TX */
- sps_tx_disconnect(msm_uport);
- ret = msm_hs_spsconnect_tx(msm_uport);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: SPS connect failed for TX");
- goto unconfig_uart_gpios;
- }
- /* Connect RX */
- kthread_flush_worker(&msm_uport->rx.kworker);
- if (rx->flush != FLUSH_SHUTDOWN)
- disconnect_rx_endpoint(msm_uport);
- ret = msm_hs_spsconnect_rx(uport);
- if (ret) {
- MSM_HS_ERR("msm_serial_hs: SPS connect failed for RX");
- goto sps_disconnect_tx;
- }
- data = (UARTDM_BCR_TX_BREAK_DISABLE | UARTDM_BCR_STALE_IRQ_EMPTY |
- UARTDM_BCR_RX_DMRX_LOW_EN | UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL |
- UARTDM_BCR_RX_DMRX_1BYTE_RES_EN);
- msm_hs_write(uport, UART_DM_BCR, data);
- /* Set auto RFR Level */
- data = msm_hs_read(uport, UART_DM_MR1);
- data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
- data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK;
- data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2));
- data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level);
- msm_hs_write(uport, UART_DM_MR1, data);
- /* Make sure RXSTALE count is non-zero */
- data = msm_hs_read(uport, UART_DM_IPR);
- if (!data) {
- data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK;
- msm_hs_write(uport, UART_DM_IPR, data);
- }
- /* Assume no flow control, unless termios sets it */
- msm_uport->flow_control = false;
- msm_hs_disable_flow_control(uport, true);
- /* Reset TX */
- msm_hs_write(uport, UART_DM_CR, RESET_TX);
- msm_hs_write(uport, UART_DM_CR, RESET_RX);
- msm_hs_write(uport, UART_DM_CR, RESET_ERROR_STATUS);
- msm_hs_write(uport, UART_DM_CR, RESET_BREAK_INT);
- msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
- msm_hs_write(uport, UART_DM_CR, RESET_CTS);
- msm_hs_write(uport, UART_DM_CR, RFR_LOW);
- /* Turn on Uart Receiver */
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_EN_BMSK);
- /* Turn on Uart Transmitter */
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_EN_BMSK);
- tx->dma_in_flight = false;
- MSM_HS_DBG("%s():desc usage flag 0x%lx", __func__, rx->queued_flag);
- setup_timer(&(tx->tx_timeout_timer),
- tx_timeout_handler,
- (unsigned long) msm_uport);
- /* Enable reading the current CTS, no harm even if CTS is ignored */
- msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK;
- /* TXLEV on empty TX fifo */
- msm_hs_write(uport, UART_DM_TFWR, 4);
- /*
- * Complete all device write related configuration before
- * queuing RX request. Hence mb() requires here.
- */
- mb();
- ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH,
- "msm_hs_uart", msm_uport);
- if (unlikely(ret)) {
- MSM_HS_ERR("%s():Error %d getting uart irq\n", __func__, ret);
- goto sps_disconnect_rx;
- }
- spin_lock_irqsave(&uport->lock, flags);
- atomic_set(&msm_uport->client_count, 0);
- atomic_set(&msm_uport->client_req_state, 0);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s: Client_Count 0\n", __func__);
- msm_hs_start_rx_locked(uport);
- spin_unlock_irqrestore(&uport->lock, flags);
- msm_hs_resource_unvote(msm_uport);
- return 0;
- sps_disconnect_rx:
- sps_disconnect(sps_pipe_handle_rx);
- sps_disconnect_tx:
- sps_disconnect(sps_pipe_handle_tx);
- unconfig_uart_gpios:
- msm_hs_unconfig_uart_gpios(uport);
- free_uart_irq:
- free_irq(uport->irq, msm_uport);
- unvote_exit:
- msm_hs_resource_unvote(msm_uport);
- MSM_HS_ERR("%s(): Error return\n", __func__);
- return ret;
- }
- /* Initialize tx and rx data structures */
- static int uartdm_init_port(struct uart_port *uport)
- {
- int ret = 0;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct msm_hs_tx *tx = &msm_uport->tx;
- struct msm_hs_rx *rx = &msm_uport->rx;
- struct sched_param param = { .sched_priority = 1 };
- init_waitqueue_head(&rx->wait);
- init_waitqueue_head(&tx->wait);
- init_waitqueue_head(&msm_uport->bam_disconnect_wait);
- /* Init kernel threads for tx and rx */
- kthread_init_worker(&rx->kworker);
- rx->task = kthread_run(kthread_worker_fn,
- &rx->kworker, "msm_serial_hs_%d_rx_work", uport->line);
- if (IS_ERR(rx->task)) {
- MSM_HS_ERR("%s(): error creating task", __func__);
- goto exit_lh_init;
- }
- sched_setscheduler(rx->task, SCHED_FIFO, ¶m);
- kthread_init_work(&rx->kwork, msm_serial_hs_rx_work);
- kthread_init_worker(&tx->kworker);
- tx->task = kthread_run(kthread_worker_fn,
- &tx->kworker, "msm_serial_hs_%d_tx_work", uport->line);
- if (IS_ERR(rx->task)) {
- MSM_HS_ERR("%s(): error creating task", __func__);
- goto exit_lh_init;
- }
- sched_setscheduler(tx->task, SCHED_FIFO, ¶m);
- kthread_init_work(&tx->kwork, msm_serial_hs_tx_work);
- rx->buffer = dma_alloc_coherent(uport->dev,
- UART_DMA_DESC_NR * UARTDM_RX_BUF_SIZE,
- &rx->rbuffer, GFP_KERNEL);
- if (!rx->buffer) {
- MSM_HS_ERR("%s(): cannot allocate rx->buffer", __func__);
- ret = -ENOMEM;
- goto exit_lh_init;
- }
- INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
- return ret;
- exit_lh_init:
- kthread_stop(rx->task);
- rx->task = NULL;
- kthread_stop(tx->task);
- tx->task = NULL;
- return ret;
- }
- struct msm_serial_hs_platform_data
- *msm_hs_dt_to_pdata(struct platform_device *pdev)
- {
- struct device_node *node = pdev->dev.of_node;
- struct msm_serial_hs_platform_data *pdata;
- u32 rx_to_inject;
- int ret;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return ERR_PTR(-ENOMEM);
- pdev->id = of_alias_get_id(pdev->dev.of_node, "uart");
- /* UART TX GPIO */
- pdata->uart_tx_gpio = of_get_named_gpio(node,
- "qcom,tx-gpio", 0);
- if (pdata->uart_tx_gpio < 0)
- pr_err("uart_tx_gpio is not available\n");
- /* UART RX GPIO */
- pdata->uart_rx_gpio = of_get_named_gpio(node,
- "qcom,rx-gpio", 0);
- if (pdata->uart_rx_gpio < 0)
- pr_err("uart_rx_gpio is not available\n");
- /* UART CTS GPIO */
- pdata->uart_cts_gpio = of_get_named_gpio(node,
- "qcom,cts-gpio", 0);
- if (pdata->uart_cts_gpio < 0)
- pr_err("uart_cts_gpio is not available\n");
- /* UART RFR GPIO */
- pdata->uart_rfr_gpio = of_get_named_gpio(node,
- "qcom,rfr-gpio", 0);
- if (pdata->uart_rfr_gpio < 0)
- pr_err("uart_rfr_gpio is not available\n");
- pdata->no_suspend_delay = of_property_read_bool(node,
- "qcom,no-suspend-delay");
- pdata->obs = of_property_read_bool(node,
- "qcom,msm-obs");
- if (pdata->obs)
- pr_err("%s:Out of Band sleep flag is set\n", __func__);
- pdata->inject_rx_on_wakeup = of_property_read_bool(node,
- "qcom,inject-rx-on-wakeup");
- if (pdata->inject_rx_on_wakeup) {
- ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
- &rx_to_inject);
- if (ret < 0) {
- pr_err("Error: Rx_char_to_inject not specified.\n");
- return ERR_PTR(ret);
- }
- pdata->rx_to_inject = (u8)rx_to_inject;
- }
- ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
- &pdata->bam_tx_ep_pipe_index);
- if (ret < 0) {
- pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
- return ERR_PTR(ret);
- }
- if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
- pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
- pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
- return ERR_PTR(-EINVAL);
- }
- ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
- &pdata->bam_rx_ep_pipe_index);
- if (ret < 0) {
- pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
- return ERR_PTR(ret);
- }
- if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
- pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
- pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
- return ERR_PTR(-EINVAL);
- }
- pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
- "tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
- pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
- pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
- pdata->uart_rfr_gpio);
- return pdata;
- }
- /**
- * Deallocate UART peripheral's SPS endpoint
- * @msm_uport - Pointer to msm_hs_port structure
- * @ep - Pointer to sps endpoint data structure
- */
- static void msm_hs_exit_ep_conn(struct msm_hs_port *msm_uport,
- struct msm_hs_sps_ep_conn_data *ep)
- {
- struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
- struct sps_connect *sps_config = &ep->config;
- dma_free_coherent(msm_uport->uport.dev,
- sps_config->desc.size,
- &sps_config->desc.phys_base,
- GFP_KERNEL);
- sps_free_endpoint(sps_pipe_handle);
- }
- /**
- * Allocate UART peripheral's SPS endpoint
- *
- * This function allocates endpoint context
- * by calling appropriate SPS driver APIs.
- *
- * @msm_uport - Pointer to msm_hs_port structure
- * @ep - Pointer to sps endpoint data structure
- * @is_produce - 1 means Producer endpoint
- * - 0 means Consumer endpoint
- *
- * @return - 0 if successful else negative value
- */
- static int msm_hs_sps_init_ep_conn(struct msm_hs_port *msm_uport,
- struct msm_hs_sps_ep_conn_data *ep,
- bool is_producer)
- {
- int rc = 0;
- struct sps_pipe *sps_pipe_handle;
- struct sps_connect *sps_config = &ep->config;
- struct sps_register_event *sps_event = &ep->event;
- /* Allocate endpoint context */
- sps_pipe_handle = sps_alloc_endpoint();
- if (!sps_pipe_handle) {
- MSM_HS_ERR("%s(): sps_alloc_endpoint() failed!!\n"
- "is_producer=%d", __func__, is_producer);
- rc = -ENOMEM;
- goto out;
- }
- /* Get default connection configuration for an endpoint */
- rc = sps_get_config(sps_pipe_handle, sps_config);
- if (rc) {
- MSM_HS_ERR("%s(): failed! pipe_handle=0x%p rc=%d",
- __func__, sps_pipe_handle, rc);
- goto get_config_err;
- }
- /* Modify the default connection configuration */
- if (is_producer) {
- /* For UART producer transfer, source is UART peripheral
- * where as destination is system memory
- */
- sps_config->source = msm_uport->bam_handle;
- sps_config->destination = SPS_DEV_HANDLE_MEM;
- sps_config->mode = SPS_MODE_SRC;
- sps_config->src_pipe_index = msm_uport->bam_rx_ep_pipe_index;
- sps_config->dest_pipe_index = 0;
- sps_event->callback = msm_hs_sps_rx_callback;
- } else {
- /* For UART consumer transfer, source is system memory
- * where as destination is UART peripheral
- */
- sps_config->source = SPS_DEV_HANDLE_MEM;
- sps_config->destination = msm_uport->bam_handle;
- sps_config->mode = SPS_MODE_DEST;
- sps_config->src_pipe_index = 0;
- sps_config->dest_pipe_index = msm_uport->bam_tx_ep_pipe_index;
- sps_event->callback = msm_hs_sps_tx_callback;
- }
- sps_config->options = SPS_O_EOT | SPS_O_DESC_DONE | SPS_O_AUTO_ENABLE;
- sps_config->event_thresh = 0x10;
- /* Allocate maximum descriptor fifo size */
- sps_config->desc.size =
- (1 + UART_DMA_DESC_NR) * sizeof(struct sps_iovec);
- sps_config->desc.base = dma_alloc_coherent(msm_uport->uport.dev,
- sps_config->desc.size,
- &sps_config->desc.phys_base,
- GFP_KERNEL);
- if (!sps_config->desc.base) {
- rc = -ENOMEM;
- MSM_HS_ERR("msm_serial_hs: dma_alloc_coherent() failed!!\n");
- goto get_config_err;
- }
- memset(sps_config->desc.base, 0x00, sps_config->desc.size);
- sps_event->mode = SPS_TRIGGER_CALLBACK;
- sps_event->options = SPS_O_DESC_DONE | SPS_O_EOT;
- sps_event->user = (void *)msm_uport;
- /* Now save the sps pipe handle */
- ep->pipe_handle = sps_pipe_handle;
- MSM_HS_DBG("msm_serial_hs: success !! %s: pipe_handle=0x%p\n"
- "desc_fifo.phys_base=0x%pa\n",
- is_producer ? "READ" : "WRITE",
- sps_pipe_handle, &sps_config->desc.phys_base);
- return 0;
- get_config_err:
- sps_free_endpoint(sps_pipe_handle);
- out:
- return rc;
- }
- /**
- * Initialize SPS HW connected with UART core
- *
- * This function register BAM HW resources with
- * SPS driver and then initialize 2 SPS endpoints
- *
- * msm_uport - Pointer to msm_hs_port structure
- *
- * @return - 0 if successful else negative value
- */
- static int msm_hs_sps_init(struct msm_hs_port *msm_uport)
- {
- int rc = 0;
- struct sps_bam_props bam = {0};
- unsigned long bam_handle;
- rc = sps_phy2h(msm_uport->bam_mem, &bam_handle);
- if (rc || !bam_handle) {
- bam.phys_addr = msm_uport->bam_mem;
- bam.virt_addr = msm_uport->bam_base;
- /*
- * This event thresold value is only significant for BAM-to-BAM
- * transfer. It's ignored for BAM-to-System mode transfer.
- */
- bam.event_threshold = 0x10; /* Pipe event threshold */
- bam.summing_threshold = 1; /* BAM event threshold */
- /* SPS driver wll handle the UART BAM IRQ */
- bam.irq = (u32)msm_uport->bam_irq;
- bam.manage = SPS_BAM_MGR_DEVICE_REMOTE;
- MSM_HS_DBG("msm_serial_hs: bam physical base=0x%pa\n",
- &bam.phys_addr);
- MSM_HS_DBG("msm_serial_hs: bam virtual base=0x%p\n",
- bam.virt_addr);
- /* Register UART Peripheral BAM device to SPS driver */
- rc = sps_register_bam_device(&bam, &bam_handle);
- if (rc) {
- MSM_HS_ERR("%s: BAM device register failed\n",
- __func__);
- return rc;
- }
- MSM_HS_DBG("%s:BAM device registered. bam_handle=0x%lx",
- __func__, msm_uport->bam_handle);
- }
- msm_uport->bam_handle = bam_handle;
- rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->rx.prod,
- UART_SPS_PROD_PERIPHERAL);
- if (rc) {
- MSM_HS_ERR("%s: Failed to Init Producer BAM-pipe", __func__);
- goto deregister_bam;
- }
- rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->tx.cons,
- UART_SPS_CONS_PERIPHERAL);
- if (rc) {
- MSM_HS_ERR("%s: Failed to Init Consumer BAM-pipe", __func__);
- goto deinit_ep_conn_prod;
- }
- return 0;
- deinit_ep_conn_prod:
- msm_hs_exit_ep_conn(msm_uport, &msm_uport->rx.prod);
- deregister_bam:
- sps_deregister_bam_device(msm_uport->bam_handle);
- return rc;
- }
- static bool deviceid[UARTDM_NR] = {0};
- /*
- * The mutex synchronizes grabbing next free device number
- * both in case of an alias being used or not. When alias is
- * used, the msm_hs_dt_to_pdata gets it and the boolean array
- * is accordingly updated with device_id_set_used. If no alias
- * is used, then device_id_grab_next_free sets that array.
- */
- static DEFINE_MUTEX(mutex_next_device_id);
- static int device_id_grab_next_free(void)
- {
- int i;
- int ret = -ENODEV;
- mutex_lock(&mutex_next_device_id);
- for (i = 0; i < UARTDM_NR; i++)
- if (!deviceid[i]) {
- ret = i;
- deviceid[i] = true;
- break;
- }
- mutex_unlock(&mutex_next_device_id);
- return ret;
- }
- static int device_id_set_used(int index)
- {
- int ret = 0;
- mutex_lock(&mutex_next_device_id);
- if (deviceid[index])
- ret = -ENODEV;
- else
- deviceid[index] = true;
- mutex_unlock(&mutex_next_device_id);
- return ret;
- }
- static void obs_manage_irq(struct msm_hs_port *msm_uport, bool en)
- {
- struct uart_port *uport = &(msm_uport->uport);
- if (msm_uport->obs) {
- if (en)
- enable_irq(uport->irq);
- else
- disable_irq(uport->irq);
- }
- }
- static void msm_hs_pm_suspend(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- int ret;
- int client_count = 0;
- if (!msm_uport)
- goto err_suspend;
- mutex_lock(&msm_uport->mtx);
- client_count = atomic_read(&msm_uport->client_count);
- msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
- msm_hs_resource_off(msm_uport);
- obs_manage_irq(msm_uport, false);
- msm_hs_clk_bus_unvote(msm_uport);
- /* For OBS, don't use wakeup interrupt, set gpio to suspended state */
- if (msm_uport->obs) {
- ret = pinctrl_select_state(msm_uport->pinctrl,
- msm_uport->gpio_state_suspend);
- if (ret)
- MSM_HS_ERR("%s():Error selecting pinctrl suspend state",
- __func__);
- }
- if (!atomic_read(&msm_uport->client_req_state))
- enable_wakeup_interrupt(msm_uport);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s: PM State Suspended client_count %d\n", __func__,
- client_count);
- mutex_unlock(&msm_uport->mtx);
- return;
- err_suspend:
- pr_err("%s(): invalid uport", __func__);
- }
- static int msm_hs_pm_resume(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- int ret = 0;
- int client_count = 0;
- if (!msm_uport) {
- dev_err(dev, "%s:Invalid uport\n", __func__);
- return -ENODEV;
- }
- mutex_lock(&msm_uport->mtx);
- client_count = atomic_read(&msm_uport->client_count);
- if (msm_uport->pm_state == MSM_HS_PM_ACTIVE)
- goto exit_pm_resume;
- if (!atomic_read(&msm_uport->client_req_state))
- disable_wakeup_interrupt(msm_uport);
- /* For OBS, don't use wakeup interrupt, set gpio to active state */
- if (msm_uport->obs) {
- ret = pinctrl_select_state(msm_uport->pinctrl,
- msm_uport->gpio_state_active);
- if (ret)
- MSM_HS_ERR("%s():Error selecting active state",
- __func__);
- }
- ret = msm_hs_clk_bus_vote(msm_uport);
- if (ret) {
- MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret);
- dev_err(dev, "%s:Failed clock vote %d\n", __func__, ret);
- goto exit_pm_resume;
- }
- obs_manage_irq(msm_uport, true);
- msm_uport->pm_state = MSM_HS_PM_ACTIVE;
- msm_hs_resource_on(msm_uport);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s:PM State:Active client_count %d\n", __func__, client_count);
- exit_pm_resume:
- mutex_unlock(&msm_uport->mtx);
- return ret;
- }
- #ifdef CONFIG_PM
- static int msm_hs_pm_sys_suspend_noirq(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- enum msm_hs_pm_state prev_pwr_state;
- int clk_cnt, client_count, ret = 0;
- if (IS_ERR_OR_NULL(msm_uport))
- return -ENODEV;
- mutex_lock(&msm_uport->mtx);
- /*
- * If there is an active clk request or an impending userspace request
- * fail the suspend callback.
- */
- clk_cnt = atomic_read(&msm_uport->resource_count);
- client_count = atomic_read(&msm_uport->client_count);
- if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d\n",
- __func__, clk_cnt, client_count);
- ret = -EBUSY;
- goto exit_suspend_noirq;
- }
- prev_pwr_state = msm_uport->pm_state;
- msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED;
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s:PM State:Sys-Suspended client_count %d\n", __func__,
- client_count);
- exit_suspend_noirq:
- mutex_unlock(&msm_uport->mtx);
- return ret;
- };
- static int msm_hs_pm_sys_resume_noirq(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
- if (IS_ERR_OR_NULL(msm_uport))
- return -ENODEV;
- /*
- * Note system-pm resume and update the state
- * variable. Resource activation will be done
- * when transfer is requested.
- */
- mutex_lock(&msm_uport->mtx);
- if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED)
- msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s:PM State: Suspended\n", __func__);
- mutex_unlock(&msm_uport->mtx);
- return 0;
- }
- #endif
- #ifdef CONFIG_PM
- static void msm_serial_hs_rt_init(struct uart_port *uport)
- {
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- MSM_HS_INFO("%s(): Enabling runtime pm", __func__);
- pm_runtime_set_suspended(uport->dev);
- pm_runtime_set_autosuspend_delay(uport->dev, 100);
- pm_runtime_use_autosuspend(uport->dev);
- mutex_lock(&msm_uport->mtx);
- msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
- mutex_unlock(&msm_uport->mtx);
- pm_runtime_enable(uport->dev);
- tty_port_set_policy(&uport->state->port, SCHED_FIFO, 1);
- }
- static int msm_hs_runtime_suspend(struct device *dev)
- {
- msm_hs_pm_suspend(dev);
- return 0;
- }
- static int msm_hs_runtime_resume(struct device *dev)
- {
- return msm_hs_pm_resume(dev);
- }
- #else
- static void msm_serial_hs_rt_init(struct uart_port *uport) {}
- static int msm_hs_runtime_suspend(struct device *dev) {}
- static int msm_hs_runtime_resume(struct device *dev) {}
- #endif
- static int msm_hs_probe(struct platform_device *pdev)
- {
- int ret = 0;
- struct uart_port *uport;
- struct msm_hs_port *msm_uport;
- struct resource *core_resource;
- struct resource *bam_resource;
- int core_irqres, bam_irqres, wakeup_irqres;
- struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
- char name[30];
- if (pdev->dev.of_node) {
- dev_dbg(&pdev->dev, "device tree enabled\n");
- pdata = msm_hs_dt_to_pdata(pdev);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- if (pdev->id < 0) {
- pdev->id = device_id_grab_next_free();
- if (pdev->id < 0) {
- dev_err(&pdev->dev,
- "Error grabbing next free device id");
- return pdev->id;
- }
- } else {
- ret = device_id_set_used(pdev->id);
- if (ret < 0) {
- dev_err(&pdev->dev, "%d alias taken",
- pdev->id);
- return ret;
- }
- }
- pdev->dev.platform_data = pdata;
- }
- if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- dev_err(&pdev->dev, "Invalid plaform device ID = %d\n",
- pdev->id);
- return -EINVAL;
- }
- msm_uport = devm_kzalloc(&pdev->dev, sizeof(struct msm_hs_port),
- GFP_KERNEL);
- if (!msm_uport)
- return -ENOMEM;
- msm_uport->uport.type = PORT_UNKNOWN;
- uport = &msm_uport->uport;
- uport->dev = &pdev->dev;
- if (pdev->dev.of_node)
- msm_uport->uart_type = BLSP_HSUART;
- msm_hs_get_pinctrl_configs(uport);
- /* Get required resources for BAM HSUART */
- core_resource = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "core_mem");
- if (!core_resource) {
- dev_err(&pdev->dev, "Invalid core HSUART Resources.\n");
- return -ENXIO;
- }
- bam_resource = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "bam_mem");
- if (!bam_resource) {
- dev_err(&pdev->dev, "Invalid BAM HSUART Resources.\n");
- return -ENXIO;
- }
- core_irqres = platform_get_irq_byname(pdev, "core_irq");
- if (core_irqres < 0) {
- dev_err(&pdev->dev, "Error %d, invalid core irq resources.\n",
- core_irqres);
- return -ENXIO;
- }
- bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
- if (bam_irqres < 0) {
- dev_err(&pdev->dev, "Error %d, invalid bam irq resources.\n",
- bam_irqres);
- return -ENXIO;
- }
- wakeup_irqres = platform_get_irq_byname(pdev, "wakeup_irq");
- if (wakeup_irqres < 0) {
- wakeup_irqres = -1;
- pr_info("Wakeup irq not specified.\n");
- }
- uport->mapbase = core_resource->start;
- uport->membase = ioremap(uport->mapbase,
- resource_size(core_resource));
- if (unlikely(!uport->membase)) {
- dev_err(&pdev->dev, "UART Resource ioremap Failed.\n");
- return -ENOMEM;
- }
- msm_uport->bam_mem = bam_resource->start;
- msm_uport->bam_base = ioremap(msm_uport->bam_mem,
- resource_size(bam_resource));
- if (unlikely(!msm_uport->bam_base)) {
- dev_err(&pdev->dev, "UART BAM Resource ioremap Failed.\n");
- iounmap(uport->membase);
- return -ENOMEM;
- }
- memset(name, 0, sizeof(name));
- scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
- "_state");
- msm_uport->ipc_msm_hs_log_ctxt =
- ipc_log_context_create(IPC_MSM_HS_LOG_STATE_PAGES,
- name, 0);
- if (!msm_uport->ipc_msm_hs_log_ctxt) {
- dev_err(&pdev->dev, "%s: error creating logging context",
- __func__);
- } else {
- msm_uport->ipc_debug_mask = INFO_LEV;
- ret = sysfs_create_file(&pdev->dev.kobj,
- &dev_attr_debug_mask.attr);
- if (unlikely(ret))
- MSM_HS_WARN("%s: Failed to create dev. attr", __func__);
- }
- uport->irq = core_irqres;
- msm_uport->bam_irq = bam_irqres;
- pdata->wakeup_irq = wakeup_irqres;
- msm_uport->bus_scale_table = msm_bus_cl_get_pdata(pdev);
- if (!msm_uport->bus_scale_table) {
- MSM_HS_ERR("BLSP UART: Bus scaling is disabled.\n");
- } else {
- msm_uport->bus_perf_client =
- msm_bus_scale_register_client
- (msm_uport->bus_scale_table);
- if (IS_ERR(&msm_uport->bus_perf_client)) {
- MSM_HS_ERR("%s():Bus client register failed\n",
- __func__);
- ret = -EINVAL;
- goto unmap_memory;
- }
- }
- msm_uport->wakeup.irq = pdata->wakeup_irq;
- msm_uport->wakeup.ignore = 1;
- msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
- msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
- msm_uport->obs = pdata->obs;
- msm_uport->bam_tx_ep_pipe_index =
- pdata->bam_tx_ep_pipe_index;
- msm_uport->bam_rx_ep_pipe_index =
- pdata->bam_rx_ep_pipe_index;
- msm_uport->wakeup.enabled = true;
- uport->iotype = UPIO_MEM;
- uport->fifosize = 64;
- uport->ops = &msm_hs_ops;
- uport->flags = UPF_BOOT_AUTOCONF;
- uport->uartclk = 7372800;
- msm_uport->imr_reg = 0x0;
- msm_uport->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm_uport->clk)) {
- ret = PTR_ERR(msm_uport->clk);
- goto deregister_bus_client;
- }
- msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
- /*
- * Some configurations do not require explicit pclk control so
- * do not flag error on pclk get failure.
- */
- if (IS_ERR(msm_uport->pclk))
- msm_uport->pclk = NULL;
- msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
- WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
- if (!msm_uport->hsuart_wq) {
- MSM_HS_ERR("%s(): Unable to create workqueue hsuart_wq\n",
- __func__);
- ret = -ENOMEM;
- goto put_clk;
- }
- mutex_init(&msm_uport->mtx);
- /* Initialize SPS HW connected with UART core */
- ret = msm_hs_sps_init(msm_uport);
- if (unlikely(ret)) {
- MSM_HS_ERR("SPS Initialization failed ! err=%d", ret);
- goto destroy_mutex;
- }
- msm_uport->tx.flush = FLUSH_SHUTDOWN;
- msm_uport->rx.flush = FLUSH_SHUTDOWN;
- memset(name, 0, sizeof(name));
- scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
- "_tx");
- msm_uport->tx.ipc_tx_ctxt =
- ipc_log_context_create(IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
- if (!msm_uport->tx.ipc_tx_ctxt)
- dev_err(&pdev->dev, "%s: error creating tx logging context",
- __func__);
- memset(name, 0, sizeof(name));
- scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
- "_rx");
- msm_uport->rx.ipc_rx_ctxt = ipc_log_context_create(
- IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
- if (!msm_uport->rx.ipc_rx_ctxt)
- dev_err(&pdev->dev, "%s: error creating rx logging context",
- __func__);
- memset(name, 0, sizeof(name));
- scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
- "_pwr");
- msm_uport->ipc_msm_hs_pwr_ctxt = ipc_log_context_create(
- IPC_MSM_HS_LOG_USER_PAGES, name, 0);
- if (!msm_uport->ipc_msm_hs_pwr_ctxt)
- dev_err(&pdev->dev, "%s: error creating usr logging context",
- __func__);
- uport->irq = core_irqres;
- msm_uport->bam_irq = bam_irqres;
- clk_set_rate(msm_uport->clk, msm_uport->uport.uartclk);
- msm_hs_clk_bus_vote(msm_uport);
- ret = uartdm_init_port(uport);
- if (unlikely(ret))
- goto err_clock;
- ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
- if (unlikely(ret)) {
- MSM_HS_ERR("Probe Failed as sysfs failed\n");
- goto err_clock;
- }
- msm_serial_debugfs_init(msm_uport, pdev->id);
- msm_hs_unconfig_uart_gpios(uport);
- uport->line = pdev->id;
- if (pdata->userid && pdata->userid <= UARTDM_NR)
- uport->line = pdata->userid;
- ret = uart_add_one_port(&msm_hs_driver, uport);
- if (!ret) {
- msm_hs_clk_bus_unvote(msm_uport);
- msm_serial_hs_rt_init(uport);
- return ret;
- }
- err_clock:
- msm_hs_clk_bus_unvote(msm_uport);
- destroy_mutex:
- mutex_destroy(&msm_uport->mtx);
- destroy_workqueue(msm_uport->hsuart_wq);
- put_clk:
- if (msm_uport->pclk)
- clk_put(msm_uport->pclk);
- if (msm_uport->clk)
- clk_put(msm_uport->clk);
- deregister_bus_client:
- msm_bus_scale_unregister_client(msm_uport->bus_perf_client);
- unmap_memory:
- iounmap(uport->membase);
- iounmap(msm_uport->bam_base);
- return ret;
- }
- static int __init msm_serial_hs_init(void)
- {
- int ret;
- ret = uart_register_driver(&msm_hs_driver);
- if (unlikely(ret)) {
- pr_err("%s failed to load\n", __func__);
- return ret;
- }
- debug_base = debugfs_create_dir("msm_serial_hs", NULL);
- if (IS_ERR_OR_NULL(debug_base))
- pr_err("msm_serial_hs: Cannot create debugfs dir\n");
- ret = platform_driver_register(&msm_serial_hs_platform_driver);
- if (ret) {
- pr_err("%s failed to load\n", __func__);
- debugfs_remove_recursive(debug_base);
- uart_unregister_driver(&msm_hs_driver);
- return ret;
- }
- pr_info("msm_serial_hs module loaded\n");
- return ret;
- }
- /*
- * Called by the upper layer when port is closed.
- * - Disables the port
- * - Unhook the ISR
- */
- static void msm_hs_shutdown(struct uart_port *uport)
- {
- int ret, rc;
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- struct circ_buf *tx_buf = &uport->state->xmit;
- int data;
- unsigned long flags;
- if (is_use_low_power_wakeup(msm_uport))
- irq_set_irq_wake(msm_uport->wakeup.irq, 0);
- if (msm_uport->wakeup.enabled)
- disable_irq(msm_uport->wakeup.irq);
- else
- disable_irq(uport->irq);
- spin_lock_irqsave(&uport->lock, flags);
- msm_uport->wakeup.enabled = false;
- msm_uport->wakeup.ignore = 1;
- spin_unlock_irqrestore(&uport->lock, flags);
- /* Free the interrupt */
- free_irq(uport->irq, msm_uport);
- if (is_use_low_power_wakeup(msm_uport)) {
- free_irq(msm_uport->wakeup.irq, msm_uport);
- MSM_HS_DBG("%s(): wakeup irq freed", __func__);
- }
- msm_uport->wakeup.freed = true;
- /* make sure tx lh finishes */
- kthread_flush_worker(&msm_uport->tx.kworker);
- ret = wait_event_timeout(msm_uport->tx.wait,
- uart_circ_empty(tx_buf), 500);
- if (!ret)
- MSM_HS_WARN("Shutdown called when tx buff not empty");
- msm_hs_resource_vote(msm_uport);
- /* Stop remote side from sending data */
- msm_hs_disable_flow_control(uport, false);
- /* make sure rx lh finishes */
- kthread_flush_worker(&msm_uport->rx.kworker);
- if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
- /* disable and disconnect rx */
- ret = wait_event_timeout(msm_uport->rx.wait,
- !msm_uport->rx.pending_flag, 500);
- if (!ret)
- MSM_HS_WARN("%s(): rx disconnect not complete",
- __func__);
- msm_hs_disconnect_rx(uport);
- }
- cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
- flush_workqueue(msm_uport->hsuart_wq);
- /* BAM Disconnect for TX */
- data = msm_hs_read(uport, UART_DM_DMEN);
- data &= ~UARTDM_TX_BAM_ENABLE_BMSK;
- msm_hs_write(uport, UART_DM_DMEN, data);
- ret = sps_tx_disconnect(msm_uport);
- if (ret)
- MSM_HS_ERR("%s(): sps_disconnect failed\n",
- __func__);
- msm_uport->tx.flush = FLUSH_SHUTDOWN;
- /* Disable the transmitter */
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_DISABLE_BMSK);
- /* Disable the receiver */
- msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_DISABLE_BMSK);
- msm_uport->imr_reg = 0;
- msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
- /*
- * Complete all device write before actually disabling uartclk.
- * Hence mb() requires here.
- */
- mb();
- msm_uport->rx.buffer_pending = NONE_PENDING;
- MSM_HS_DBG("%s(): tx, rx events complete", __func__);
- dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
- msm_hs_resource_unvote(msm_uport);
- rc = atomic_read(&msm_uport->resource_count);
- if (rc) {
- atomic_set(&msm_uport->resource_count, 1);
- MSM_HS_WARN("%s(): removing extra vote\n", __func__);
- msm_hs_resource_unvote(msm_uport);
- }
- if (atomic_read(&msm_uport->client_req_state)) {
- MSM_HS_WARN("%s: Client clock vote imbalance\n", __func__);
- atomic_set(&msm_uport->client_req_state, 0);
- }
- if (atomic_read(&msm_uport->client_count)) {
- MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__);
- atomic_set(&msm_uport->client_count, 0);
- LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
- "%s: Client_Count 0\n", __func__);
- }
- msm_hs_unconfig_uart_gpios(uport);
- MSM_HS_INFO("%s:UART port closed successfully\n", __func__);
- }
- static void __exit msm_serial_hs_exit(void)
- {
- pr_info("msm_serial_hs module removed\n");
- debugfs_remove_recursive(debug_base);
- platform_driver_unregister(&msm_serial_hs_platform_driver);
- uart_unregister_driver(&msm_hs_driver);
- }
- static const struct dev_pm_ops msm_hs_dev_pm_ops = {
- .runtime_suspend = msm_hs_runtime_suspend,
- .runtime_resume = msm_hs_runtime_resume,
- .runtime_idle = NULL,
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(msm_hs_pm_sys_suspend_noirq,
- msm_hs_pm_sys_resume_noirq)
- };
- static struct platform_driver msm_serial_hs_platform_driver = {
- .probe = msm_hs_probe,
- .remove = msm_hs_remove,
- .driver = {
- .name = "msm_serial_hs",
- .pm = &msm_hs_dev_pm_ops,
- .of_match_table = msm_hs_match_table,
- },
- };
- static struct uart_driver msm_hs_driver = {
- .owner = THIS_MODULE,
- .driver_name = "msm_serial_hs",
- .dev_name = "ttyHS",
- .nr = UARTDM_NR,
- .cons = 0,
- };
- static const struct uart_ops msm_hs_ops = {
- .tx_empty = msm_hs_tx_empty,
- .set_mctrl = msm_hs_set_mctrl_locked,
- .get_mctrl = msm_hs_get_mctrl_locked,
- .stop_tx = msm_hs_stop_tx_locked,
- .start_tx = msm_hs_start_tx_locked,
- .stop_rx = msm_hs_stop_rx_locked,
- .enable_ms = msm_hs_enable_ms_locked,
- .break_ctl = msm_hs_break_ctl,
- .startup = msm_hs_startup,
- .shutdown = msm_hs_shutdown,
- .set_termios = msm_hs_set_termios,
- .type = msm_hs_type,
- .config_port = msm_hs_config_port,
- .flush_buffer = NULL,
- .ioctl = msm_hs_ioctl,
- };
- module_init(msm_serial_hs_init);
- module_exit(msm_serial_hs_exit);
- MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset");
- MODULE_LICENSE("GPL v2");
|