1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013 |
- /* Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h>
- #include <linux/firmware.h>
- #include <linux/slab.h>
- #include <linux/err.h>
- #include <linux/platform_device.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/wcnss_wlan.h>
- #include <linux/platform_data/qcom_wcnss_device.h>
- #include <linux/workqueue.h>
- #include <linux/jiffies.h>
- #include <linux/gpio.h>
- #include <linux/wakelock.h>
- #include <linux/delay.h>
- #include <linux/of.h>
- #include <linux/of_gpio.h>
- #include <linux/clk.h>
- #include <linux/ratelimit.h>
- #include <linux/kthread.h>
- #include <linux/wait.h>
- #include <linux/uaccess.h>
- #include <linux/suspend.h>
- #include <linux/rwsem.h>
- #include <linux/mfd/pm8xxx/misc.h>
- #include <linux/qpnp/qpnp-adc.h>
- #include <linux/pm_qos.h>
- #include <mach/board.h>
- #include <mach/msm_smd.h>
- #include <mach/msm_iomap.h>
- #include <mach/subsystem_restart.h>
- #include <mach/subsystem_notif.h>
- #ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
- #include "wcnss_prealloc.h"
- #endif
- #define DEVICE "wcnss_wlan"
- #define CTRL_DEVICE "wcnss_ctrl"
- #define VERSION "1.01"
- #define WCNSS_PIL_DEVICE "wcnss"
- #define WCNSS_DISABLE_PC_LATENCY 100
- #define WCNSS_ENABLE_PC_LATENCY PM_QOS_DEFAULT_VALUE
- #define WCNSS_PM_QOS_TIMEOUT 15000
- #define WAIT_FOR_CBC_IND 2
- /* module params */
- #define WCNSS_CONFIG_UNSPECIFIED (-1)
- #define UINT32_MAX (0xFFFFFFFFU)
- static int has_48mhz_xo = WCNSS_CONFIG_UNSPECIFIED;
- module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
- MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
- static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
- module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
- MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
- static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
- module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
- MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
- static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
- module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
- MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
- static DEFINE_SPINLOCK(reg_spinlock);
- #define MSM_RIVA_PHYS 0x03204000
- #define MSM_PRONTO_PHYS 0xfb21b000
- #define RIVA_SPARE_OFFSET 0x0b4
- #define RIVA_SUSPEND_BIT BIT(24)
- #define MSM_RIVA_CCU_BASE 0x03200800
- #define CCU_RIVA_INVALID_ADDR_OFFSET 0x100
- #define CCU_RIVA_LAST_ADDR0_OFFSET 0x104
- #define CCU_RIVA_LAST_ADDR1_OFFSET 0x108
- #define CCU_RIVA_LAST_ADDR2_OFFSET 0x10c
- #define PRONTO_PMU_SPARE_OFFSET 0x1088
- #define PRONTO_PMU_COM_GDSCR_OFFSET 0x0024
- #define PRONTO_PMU_COM_GDSCR_SW_COLLAPSE BIT(0)
- #define PRONTO_PMU_COM_GDSCR_HW_CTRL BIT(1)
- #define PRONTO_PMU_WLAN_BCR_OFFSET 0x0050
- #define PRONTO_PMU_WLAN_BCR_BLK_ARES BIT(0)
- #define PRONTO_PMU_WLAN_GDSCR_OFFSET 0x0054
- #define PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE BIT(0)
- #define PRONTO_PMU_CBCR_OFFSET 0x0008
- #define PRONTO_PMU_CBCR_CLK_EN BIT(0)
- #define PRONTO_PMU_COM_CPU_CBCR_OFFSET 0x0030
- #define PRONTO_PMU_COM_AHB_CBCR_OFFSET 0x0034
- #define PRONTO_PMU_WLAN_AHB_CBCR_OFFSET 0x0074
- #define PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN BIT(0)
- #define PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF BIT(31)
- #define PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET 0x0120
- #define PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN BIT(1)
- #define PRONTO_PMU_CFG_OFFSET 0x1004
- #define PRONTO_PMU_COM_CSR_OFFSET 0x1040
- #define PRONTO_PMU_SOFT_RESET_OFFSET 0x104C
- #define MSM_PRONTO_A2XB_BASE 0xfb100400
- #define A2XB_CFG_OFFSET 0x00
- #define A2XB_INT_SRC_OFFSET 0x0c
- #define A2XB_TSTBUS_CTRL_OFFSET 0x14
- #define A2XB_TSTBUS_OFFSET 0x18
- #define A2XB_ERR_INFO_OFFSET 0x1c
- #define A2XB_FIFO_FILL_OFFSET 0x07
- #define A2XB_READ_FIFO_FILL_MASK 0x3F
- #define A2XB_CMD_FIFO_FILL_MASK 0x0F
- #define A2XB_WRITE_FIFO_FILL_MASK 0x1F
- #define A2XB_FIFO_EMPTY 0x2
- #define A2XB_FIFO_COUNTER 0xA
- #define WCNSS_TSTBUS_CTRL_EN BIT(0)
- #define WCNSS_TSTBUS_CTRL_AXIM (0x02 << 1)
- #define WCNSS_TSTBUS_CTRL_CMDFIFO (0x03 << 1)
- #define WCNSS_TSTBUS_CTRL_WRFIFO (0x04 << 1)
- #define WCNSS_TSTBUS_CTRL_RDFIFO (0x05 << 1)
- #define WCNSS_TSTBUS_CTRL_CTRL (0x07 << 1)
- #define WCNSS_TSTBUS_CTRL_AXIM_CFG0 (0x00 << 8)
- #define WCNSS_TSTBUS_CTRL_AXIM_CFG1 (0x01 << 8)
- #define WCNSS_TSTBUS_CTRL_CTRL_CFG0 (0x00 << 28)
- #define WCNSS_TSTBUS_CTRL_CTRL_CFG1 (0x01 << 28)
- #define MSM_PRONTO_CCPU_BASE 0xfb205050
- #define CCU_PRONTO_INVALID_ADDR_OFFSET 0x08
- #define CCU_PRONTO_LAST_ADDR0_OFFSET 0x0c
- #define CCU_PRONTO_LAST_ADDR1_OFFSET 0x10
- #define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
- #define MSM_PRONTO_SAW2_BASE 0xfb219000
- #define PRONTO_SAW2_SPM_STS_OFFSET 0x0c
- #define MSM_PRONTO_PLL_BASE 0xfb21b1c0
- #define PRONTO_PLL_STATUS_OFFSET 0x1c
- #define MSM_PRONTO_MCU_BASE 0xfb080c00
- #define MCU_APB2PHY_STATUS_OFFSET 0xec
- #define MCU_CBR_CCAHB_ERR_OFFSET 0x380
- #define MCU_CBR_CAHB_ERR_OFFSET 0x384
- #define MCU_CBR_CCAHB_TIMEOUT_OFFSET 0x388
- #define MCU_CBR_CAHB_TIMEOUT_OFFSET 0x38c
- #define MCU_DBR_CDAHB_ERR_OFFSET 0x390
- #define MCU_DBR_DAHB_ERR_OFFSET 0x394
- #define MCU_DBR_CDAHB_TIMEOUT_OFFSET 0x398
- #define MCU_DBR_DAHB_TIMEOUT_OFFSET 0x39c
- #define MCU_FDBR_CDAHB_ERR_OFFSET 0x3a0
- #define MCU_FDBR_FDAHB_ERR_OFFSET 0x3a4
- #define MCU_FDBR_CDAHB_TIMEOUT_OFFSET 0x3a8
- #define MCU_FDBR_FDAHB_TIMEOUT_OFFSET 0x3ac
- #define MSM_PRONTO_TXP_STATUS 0xfb08040c
- #define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
- #define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
- #define MSM_PRONTO_ALARMS_TXCTL 0xfb0120a8
- #define MSM_PRONTO_ALARMS_TACTL 0xfb012448
- #define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
- #define WCNSS_VBATT_THRESHOLD 3500000
- #define WCNSS_VBATT_GUARD 20000
- #define WCNSS_VBATT_HIGH 3700000
- #define WCNSS_VBATT_LOW 3300000
- #define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
- #define WCNSS_MAX_FRAME_SIZE (4*1024)
- #define WCNSS_VERSION_LEN 30
- #define WCNSS_MAX_BUILD_VER_LEN 256
- #define WCNSS_MAX_CMD_LEN (128)
- #define WCNSS_MIN_CMD_LEN (3)
- #define WCNSS_MIN_SERIAL_LEN (6)
- /* control messages from userspace */
- #define WCNSS_USR_CTRL_MSG_START 0x00000000
- #define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
- #define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
- #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
- #define MAC_ADDRESS_STR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
- /* message types */
- #define WCNSS_CTRL_MSG_START 0x01000000
- #define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
- #define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
- #define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
- #define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
- #define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
- #define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
- #define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
- #define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
- #define WCNSS_VBATT_LEVEL_IND (WCNSS_CTRL_MSG_START + 8)
- #define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9)
- #define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10)
- #define WCNSS_PM_CONFIG_REQ (WCNSS_CTRL_MSG_START + 11)
- #define WCNSS_CBC_COMPLETE_IND (WCNSS_CTRL_MSG_START + 12)
- /* max 20mhz channel count */
- #define WCNSS_MAX_CH_NUM 45
- #define WCNSS_MAX_PIL_RETRY 2
- #define VALID_VERSION(version) \
- ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
- #define FW_CALDATA_CAPABLE() \
- ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
- struct smd_msg_hdr {
- unsigned int msg_type;
- unsigned int msg_len;
- };
- struct wcnss_version {
- struct smd_msg_hdr hdr;
- unsigned char major;
- unsigned char minor;
- unsigned char version;
- unsigned char revision;
- };
- struct wcnss_pmic_dump {
- char reg_name[10];
- u16 reg_addr;
- };
- static struct wcnss_pmic_dump wcnss_pmic_reg_dump[] = {
- {"S2", 0x1D8},
- {"L4", 0xB4},
- {"L10", 0xC0},
- {"LVS2", 0x62},
- {"S4", 0x1E8},
- {"LVS7", 0x06C},
- {"LVS1", 0x060},
- };
- static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle);
- static struct notifier_block wnb = {
- .notifier_call = wcnss_notif_cb,
- };
- #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
- /*
- * On SMD channel 4K of maximum data can be transferred, including message
- * header, so NV fragment size as next multiple of 1Kb is 3Kb.
- */
- #define NV_FRAGMENT_SIZE 3072
- #define MAX_CALIBRATED_DATA_SIZE (64*1024)
- #define LAST_FRAGMENT (1 << 0)
- #define MESSAGE_TO_FOLLOW (1 << 1)
- #define CAN_RECEIVE_CALDATA (1 << 15)
- #define WCNSS_RESP_SUCCESS 1
- #define WCNSS_RESP_FAIL 0
- /* Macro to find the total number fragments of the NV bin Image */
- #define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
- (x / NV_FRAGMENT_SIZE) : ((x / NV_FRAGMENT_SIZE) + 1))
- struct nvbin_dnld_req_params {
- /*
- * Fragment sequence number of the NV bin Image. NV Bin Image
- * might not fit into one message due to size limitation of
- * the SMD channel FIFO so entire NV blob is chopped into
- * multiple fragments starting with seqeunce number 0. The
- * last fragment is indicated by marking is_last_fragment field
- * to 1. At receiving side, NV blobs would be concatenated
- * together without any padding bytes in between.
- */
- unsigned short frag_number;
- /*
- * bit 0: When set to 1 it indicates that no more fragments will
- * be sent.
- * bit 1: When set, a new message will be followed by this message
- * bit 2- bit 14: Reserved
- * bit 15: when set, it indicates that the sender is capable of
- * receiving Calibrated data.
- */
- unsigned short msg_flags;
- /* NV Image size (number of bytes) */
- unsigned int nvbin_buffer_size;
- /*
- * Following the 'nvbin_buffer_size', there should be
- * nvbin_buffer_size bytes of NV bin Image i.e.
- * uint8[nvbin_buffer_size].
- */
- };
- struct nvbin_dnld_req_msg {
- /*
- * Note: The length specified in nvbin_dnld_req_msg messages
- * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
- * nvbin_buffer_size.
- */
- struct smd_msg_hdr hdr;
- struct nvbin_dnld_req_params dnld_req_params;
- };
- struct cal_data_params {
- /* The total size of the calibrated data, including all the
- * fragments.
- */
- unsigned int total_size;
- unsigned short frag_number;
- /*
- * bit 0: When set to 1 it indicates that no more fragments will
- * be sent.
- * bit 1: When set, a new message will be followed by this message
- * bit 2- bit 15: Reserved
- */
- unsigned short msg_flags;
- /*
- * fragment size
- */
- unsigned int frag_size;
- /*
- * Following the frag_size, frag_size of fragmented
- * data will be followed.
- */
- };
- struct cal_data_msg {
- /*
- * The length specified in cal_data_msg should be
- * hdr.msg_len = sizeof(cal_data_msg) + frag_size
- */
- struct smd_msg_hdr hdr;
- struct cal_data_params cal_params;
- };
- struct vbatt_level {
- u32 curr_volt;
- u32 threshold;
- };
- struct vbatt_message {
- struct smd_msg_hdr hdr;
- struct vbatt_level vbatt;
- };
- static struct {
- struct platform_device *pdev;
- void *pil;
- struct resource *mmio_res;
- struct resource *tx_irq_res;
- struct resource *rx_irq_res;
- struct resource *gpios_5wire;
- const struct dev_pm_ops *pm_ops;
- int triggered;
- int smd_channel_ready;
- u32 wlan_rx_buff_count;
- smd_channel_t *smd_ch;
- unsigned char wcnss_version[WCNSS_VERSION_LEN];
- unsigned char fw_major;
- unsigned char fw_minor;
- unsigned int serial_number;
- int thermal_mitigation;
- enum wcnss_hw_type wcnss_hw_type;
- void (*tm_notify)(struct device *, int);
- struct wcnss_wlan_config wlan_config;
- struct delayed_work wcnss_work;
- struct delayed_work vbatt_work;
- struct work_struct wcnssctrl_version_work;
- struct work_struct wcnss_pm_config_work;
- struct work_struct wcnssctrl_nvbin_dnld_work;
- struct work_struct wcnssctrl_rx_work;
- struct wake_lock wcnss_wake_lock;
- void __iomem *msm_wcnss_base;
- void __iomem *riva_ccu_base;
- void __iomem *pronto_a2xb_base;
- void __iomem *pronto_ccpu_base;
- void __iomem *pronto_saw2_base;
- void __iomem *pronto_pll_base;
- void __iomem *pronto_mcu_base;
- void __iomem *wlan_tx_status;
- void __iomem *wlan_tx_phy_aborts;
- void __iomem *wlan_brdg_err_source;
- void __iomem *alarms_txctl;
- void __iomem *alarms_tactl;
- void __iomem *fiq_reg;
- int nv_downloaded;
- int is_cbc_done;
- unsigned char *fw_cal_data;
- unsigned char *user_cal_data;
- int fw_cal_rcvd;
- int fw_cal_exp_frag;
- int fw_cal_available;
- int user_cal_read;
- int user_cal_available;
- u32 user_cal_rcvd;
- u32 user_cal_exp_size;
- int iris_xo_mode_set;
- int fw_vbatt_state;
- int ctrl_device_opened;
- char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
- struct mutex dev_lock;
- struct mutex ctrl_lock;
- wait_queue_head_t read_wait;
- struct qpnp_adc_tm_btm_param vbat_monitor_params;
- struct qpnp_adc_tm_chip *adc_tm_dev;
- struct mutex vbat_monitor_mutex;
- u16 unsafe_ch_count;
- u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
- void *wcnss_notif_hdle;
- u8 is_shutdown;
- struct pm_qos_request wcnss_pm_qos_request;
- int pc_disabled;
- struct delayed_work wcnss_pm_qos_del_req;
- struct mutex pm_qos_mutex;
- } *penv = NULL;
- static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- char macAddr[WLAN_MAC_ADDR_SIZE];
- if (!penv)
- return -ENODEV;
- pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
- if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
- &macAddr[0], &macAddr[1],
- &macAddr[2], &macAddr[3],
- &macAddr[4], &macAddr[5])) {
- pr_err("%s: Failed to Copy MAC\n", __func__);
- return -EINVAL;
- }
- memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
- pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
- penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
- penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
- penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
- return count;
- }
- static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- if (!penv)
- return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
- penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
- penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
- penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
- }
- static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR,
- wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store);
- static ssize_t wcnss_serial_number_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- if (!penv)
- return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, "%08X\n", penv->serial_number);
- }
- static ssize_t wcnss_serial_number_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int value;
- if (!penv)
- return -ENODEV;
- if (sscanf(buf, "%08X", &value) != 1)
- return -EINVAL;
- penv->serial_number = value;
- return count;
- }
- static DEVICE_ATTR(serial_number, S_IRUSR | S_IWUSR,
- wcnss_serial_number_show, wcnss_serial_number_store);
- static ssize_t wcnss_thermal_mitigation_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- if (!penv)
- return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, "%u\n", penv->thermal_mitigation);
- }
- static ssize_t wcnss_thermal_mitigation_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- int value;
- if (!penv)
- return -ENODEV;
- if (sscanf(buf, "%d", &value) != 1)
- return -EINVAL;
- penv->thermal_mitigation = value;
- if (penv->tm_notify)
- (penv->tm_notify)(dev, value);
- return count;
- }
- static DEVICE_ATTR(thermal_mitigation, S_IRUSR | S_IWUSR,
- wcnss_thermal_mitigation_show, wcnss_thermal_mitigation_store);
- static ssize_t wcnss_version_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- if (!penv)
- return -ENODEV;
- return scnprintf(buf, PAGE_SIZE, "%s", penv->wcnss_version);
- }
- static DEVICE_ATTR(wcnss_version, S_IRUSR,
- wcnss_version_show, NULL);
- void wcnss_riva_dump_pmic_regs(void)
- {
- int i, rc;
- u8 val;
- for (i = 0; i < ARRAY_SIZE(wcnss_pmic_reg_dump); i++) {
- val = 0;
- rc = pm8xxx_read_register(wcnss_pmic_reg_dump[i].reg_addr,
- &val);
- if (rc)
- pr_err("PMIC READ: Failed to read addr = %d\n",
- wcnss_pmic_reg_dump[i].reg_addr);
- else
- pr_info_ratelimited("PMIC READ: %s addr = %x, value = %x\n",
- wcnss_pmic_reg_dump[i].reg_name,
- wcnss_pmic_reg_dump[i].reg_addr, val);
- }
- }
- /* wcnss_reset_intr() is invoked when host drivers fails to
- * communicate with WCNSS over SMD; so logging these registers
- * helps to know WCNSS failure reason
- */
- void wcnss_riva_log_debug_regs(void)
- {
- void __iomem *ccu_reg;
- u32 reg = 0;
- ccu_reg = penv->riva_ccu_base + CCU_RIVA_INVALID_ADDR_OFFSET;
- reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR0_OFFSET;
- reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR1_OFFSET;
- reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
- ccu_reg = penv->riva_ccu_base + CCU_RIVA_LAST_ADDR2_OFFSET;
- reg = readl_relaxed(ccu_reg);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
- wcnss_riva_dump_pmic_regs();
- }
- EXPORT_SYMBOL(wcnss_riva_log_debug_regs);
- void wcnss_pronto_is_a2xb_bus_stall(void *tst_addr, u32 fifo_mask, char *type)
- {
- u32 iter = 0, reg = 0;
- u32 axi_fifo_count = 0, axi_fifo_count_last = 0;
- reg = readl_relaxed(tst_addr);
- axi_fifo_count = (reg >> A2XB_FIFO_FILL_OFFSET) & fifo_mask;
- while ((++iter < A2XB_FIFO_COUNTER) && axi_fifo_count) {
- axi_fifo_count_last = axi_fifo_count;
- reg = readl_relaxed(tst_addr);
- axi_fifo_count = (reg >> A2XB_FIFO_FILL_OFFSET) & fifo_mask;
- if (axi_fifo_count < axi_fifo_count_last)
- break;
- }
- if (iter == A2XB_FIFO_COUNTER) {
- pr_err("%s data FIFO testbus possibly stalled reg%08x\n",
- type, reg);
- } else {
- pr_err("%s data FIFO tstbus not stalled reg%08x\n",
- type, reg);
- }
- }
- /* Log pronto debug registers before sending reset interrupt */
- void wcnss_pronto_log_debug_regs(void)
- {
- void __iomem *reg_addr, *tst_addr, *tst_ctrl_addr;
- u32 reg = 0, reg2 = 0, reg3 = 0, reg4 = 0;
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_SPARE %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_CFG %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
- reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
- reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PLL_STATUS %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
- reg4 = readl_relaxed(reg_addr);
- pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
- reg >>= 31;
- if (!reg) {
- pr_err("Cannot log, Pronto common SS is power collapsed\n");
- return;
- }
- reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
- | PRONTO_PMU_COM_GDSCR_HW_CTRL);
- writel_relaxed(reg, reg_addr);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CBCR_OFFSET;
- reg = readl_relaxed(reg_addr);
- reg |= PRONTO_PMU_CBCR_CLK_EN;
- writel_relaxed(reg, reg_addr);
- reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("A2XB_CFG_OFFSET %08x\n", reg);
- reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
- reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
- reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
- reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
- reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
- reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
- tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
- tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
- /* read data FIFO */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- if (!(reg & A2XB_FIFO_EMPTY)) {
- wcnss_pronto_is_a2xb_bus_stall(tst_addr,
- A2XB_READ_FIFO_FILL_MASK, "Read");
- } else {
- pr_err("Read data FIFO testbus %08x\n", reg);
- }
- /* command FIFO */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- if (!(reg & A2XB_FIFO_EMPTY)) {
- wcnss_pronto_is_a2xb_bus_stall(tst_addr,
- A2XB_CMD_FIFO_FILL_MASK, "Cmd");
- } else {
- pr_err("Command FIFO testbus %08x\n", reg);
- }
- /* write data FIFO */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- if (!(reg & A2XB_FIFO_EMPTY)) {
- wcnss_pronto_is_a2xb_bus_stall(tst_addr,
- A2XB_WRITE_FIFO_FILL_MASK, "Write");
- } else {
- pr_err("Write data FIFO testbus %08x\n", reg);
- }
- /* AXIM SEL CFG0 */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
- WCNSS_TSTBUS_CTRL_AXIM_CFG0;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
- /* AXIM SEL CFG1 */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_AXIM |
- WCNSS_TSTBUS_CTRL_AXIM_CFG1;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
- /* CTRL SEL CFG0 */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
- WCNSS_TSTBUS_CTRL_CTRL_CFG0;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
- /* CTRL SEL CFG1 */
- reg = 0;
- reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CTRL |
- WCNSS_TSTBUS_CTRL_CTRL_CFG1;
- writel_relaxed(reg, tst_ctrl_addr);
- reg = readl_relaxed(tst_addr);
- pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
- reg = readl_relaxed(reg_addr);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_GDSCR_OFFSET;
- reg2 = readl_relaxed(reg_addr);
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
- reg3 = readl_relaxed(reg_addr);
- pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
- if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
- (reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
- (!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
- (reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
- (!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
- pr_err("Cannot log, wlan domain is power collapsed\n");
- return;
- }
- msleep(50);
- reg = readl_relaxed(penv->wlan_tx_phy_aborts);
- pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_APB2PHY_STATUS_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_APB2PHY_STATUS %08x\n", reg);
-
- reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_CDAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_DAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_CDAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_ERR_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_FDAHB_ERR %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
- reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_TIMEOUT_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_err("MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
- reg = readl_relaxed(penv->wlan_brdg_err_source);
- pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
- reg = readl_relaxed(penv->wlan_tx_status);
- pr_err("WLAN_TXP_STATUS %08x\n", reg);
- reg = readl_relaxed(penv->alarms_txctl);
- pr_err("ALARMS_TXCTL %08x\n", reg);
- reg = readl_relaxed(penv->alarms_tactl);
- pr_err("ALARMS_TACTL %08x\n", reg);
- }
- EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
- #ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
- static void wcnss_log_iris_regs(void)
- {
- int i;
- u32 reg_val;
- u32 regs_array[] = {
- 0x04, 0x05, 0x11, 0x1e, 0x40, 0x48,
- 0x49, 0x4b, 0x00, 0x01, 0x4d};
- pr_info("IRIS Registers [address] : value\n");
- for (i = 0; i < ARRAY_SIZE(regs_array); i++) {
- reg_val = wcnss_rf_read_reg(regs_array[i]);
- pr_info("[0x%08x] : 0x%08x\n", regs_array[i], reg_val);
- }
- }
- int wcnss_get_mux_control(void)
- {
- void __iomem *pmu_conf_reg;
- u32 reg = 0;
- if (NULL == penv)
- return 0;
- pmu_conf_reg = penv->msm_wcnss_base + PRONTO_PMU_OFFSET;
- reg = readl_relaxed(pmu_conf_reg);
- reg |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP;
- writel_relaxed(reg, pmu_conf_reg);
- return 1;
- }
- void wcnss_log_debug_regs_on_bite(void)
- {
- struct platform_device *pdev = wcnss_get_platform_device();
- struct clk *measure;
- struct clk *wcnss_debug_mux;
- unsigned long clk_rate;
- if (wcnss_hardware_type() != WCNSS_PRONTO_HW)
- return;
- measure = clk_get(&pdev->dev, "measure");
- wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug");
- if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
- if (clk_set_parent(measure, wcnss_debug_mux))
- return;
- clk_rate = clk_get_rate(measure);
- pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
- if (clk_rate) {
- wcnss_pronto_log_debug_regs();
- if (wcnss_get_mux_control())
- wcnss_log_iris_regs();
- } else {
- pr_err("clock frequency is zero, cannot access PMU or other registers\n");
- wcnss_log_iris_regs();
- }
- }
- }
- #endif
- /* interface to reset wcnss by sending the reset interrupt */
- void wcnss_reset_intr(void)
- {
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
- wcnss_pronto_log_debug_regs();
- if (wcnss_get_mux_control())
- wcnss_log_iris_regs();
- wmb();
- __raw_writel(1 << 16, penv->fiq_reg);
- } else {
- wcnss_riva_log_debug_regs();
- wmb();
- __raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
- }
- }
- EXPORT_SYMBOL(wcnss_reset_intr);
- void wcnss_reset_fiq(bool clk_chk_en)
- {
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
- if (clk_chk_en) {
- wcnss_log_debug_regs_on_bite();
- } else {
- wcnss_pronto_log_debug_regs();
- if (wcnss_get_mux_control())
- wcnss_log_iris_regs();
- }
- /* Insert memory barrier before writing fiq register */
- wmb();
- __raw_writel(1 << 16, penv->fiq_reg);
- } else {
- wcnss_riva_log_debug_regs();
- }
- }
- EXPORT_SYMBOL(wcnss_reset_fiq);
- static int wcnss_create_sysfs(struct device *dev)
- {
- int ret;
- if (!dev)
- return -ENODEV;
- ret = device_create_file(dev, &dev_attr_serial_number);
- if (ret)
- return ret;
- ret = device_create_file(dev, &dev_attr_thermal_mitigation);
- if (ret)
- goto remove_serial;
- ret = device_create_file(dev, &dev_attr_wcnss_version);
- if (ret)
- goto remove_thermal;
- ret = device_create_file(dev, &dev_attr_wcnss_mac_addr);
- if (ret)
- goto remove_version;
- return 0;
- remove_version:
- device_remove_file(dev, &dev_attr_wcnss_version);
- remove_thermal:
- device_remove_file(dev, &dev_attr_thermal_mitigation);
- remove_serial:
- device_remove_file(dev, &dev_attr_serial_number);
- return ret;
- }
- static void wcnss_remove_sysfs(struct device *dev)
- {
- if (dev) {
- device_remove_file(dev, &dev_attr_serial_number);
- device_remove_file(dev, &dev_attr_thermal_mitigation);
- device_remove_file(dev, &dev_attr_wcnss_version);
- device_remove_file(dev, &dev_attr_wcnss_mac_addr);
- }
- }
- static void wcnss_pm_qos_add_request(void)
- {
- pr_info("%s: add request\n", __func__);
- pm_qos_add_request(&penv->wcnss_pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
- }
- static void wcnss_pm_qos_remove_request(void)
- {
- pr_info("%s: remove request\n", __func__);
- pm_qos_remove_request(&penv->wcnss_pm_qos_request);
- }
- void wcnss_pm_qos_update_request(int val)
- {
- pr_info("%s: update request %d\n", __func__, val);
- pm_qos_update_request(&penv->wcnss_pm_qos_request, val);
- }
- void wcnss_disable_pc_remove_req(void)
- {
- mutex_lock(&penv->pm_qos_mutex);
- if (penv->pc_disabled) {
- penv->pc_disabled = 0;
- wcnss_pm_qos_update_request(WCNSS_ENABLE_PC_LATENCY);
- wcnss_pm_qos_remove_request();
- wcnss_allow_suspend();
- }
- mutex_unlock(&penv->pm_qos_mutex);
- }
- void wcnss_disable_pc_add_req(void)
- {
- mutex_lock(&penv->pm_qos_mutex);
- if (!penv->pc_disabled) {
- wcnss_pm_qos_add_request();
- wcnss_prevent_suspend();
- wcnss_pm_qos_update_request(WCNSS_DISABLE_PC_LATENCY);
- penv->pc_disabled = 1;
- }
- mutex_unlock(&penv->pm_qos_mutex);
- }
- static void wcnss_smd_notify_event(void *data, unsigned int event)
- {
- int len = 0;
- if (penv != data) {
- pr_err("wcnss: invalid env pointer in smd callback\n");
- return;
- }
- switch (event) {
- case SMD_EVENT_DATA:
- len = smd_read_avail(penv->smd_ch);
- if (len < 0) {
- pr_err("wcnss: failed to read from smd %d\n", len);
- return;
- }
- schedule_work(&penv->wcnssctrl_rx_work);
- break;
- case SMD_EVENT_OPEN:
- pr_debug("wcnss: opening WCNSS SMD channel :%s",
- WCNSS_CTRL_CHANNEL);
- schedule_work(&penv->wcnssctrl_version_work);
- schedule_work(&penv->wcnss_pm_config_work);
- __cancel_delayed_work(&penv->wcnss_pm_qos_del_req);
- schedule_delayed_work(&penv->wcnss_pm_qos_del_req, 0);
- break;
- case SMD_EVENT_CLOSE:
- pr_debug("wcnss: closing WCNSS SMD channel :%s",
- WCNSS_CTRL_CHANNEL);
- penv->nv_downloaded = 0;
- penv->is_cbc_done = 0;
- break;
- default:
- break;
- }
- }
- static void wcnss_post_bootup(struct work_struct *work)
- {
- if (do_not_cancel_vote == 1) {
- pr_info("%s: Keeping APPS vote for Iris & WCNSS\n", __func__);
- return;
- }
- pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);
- /* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs */
- wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
- WCNSS_WLAN_SWITCH_OFF, NULL);
- }
- static int
- wcnss_pronto_gpios_config(struct device *dev, bool enable)
- {
- int rc = 0;
- int i, j;
- int WCNSS_WLAN_NUM_GPIOS = 5;
- for (i = 0; i < WCNSS_WLAN_NUM_GPIOS; i++) {
- int gpio = of_get_gpio(dev->of_node, i);
- if (enable) {
- rc = gpio_request(gpio, "wcnss_wlan");
- if (rc) {
- pr_err("WCNSS gpio_request %d err %d\n",
- gpio, rc);
- goto fail;
- }
- } else
- gpio_free(gpio);
- }
- return rc;
- fail:
- for (j = WCNSS_WLAN_NUM_GPIOS-1; j >= 0; j--) {
- int gpio = of_get_gpio(dev->of_node, i);
- gpio_free(gpio);
- }
- return rc;
- }
- static int
- wcnss_gpios_config(struct resource *gpios_5wire, bool enable)
- {
- int i, j;
- int rc = 0;
- for (i = gpios_5wire->start; i <= gpios_5wire->end; i++) {
- if (enable) {
- rc = gpio_request(i, gpios_5wire->name);
- if (rc) {
- pr_err("WCNSS gpio_request %d err %d\n", i, rc);
- goto fail;
- }
- } else
- gpio_free(i);
- }
- return rc;
- fail:
- for (j = i-1; j >= gpios_5wire->start; j--)
- gpio_free(j);
- return rc;
- }
- static int __devinit
- wcnss_wlan_ctrl_probe(struct platform_device *pdev)
- {
- if (!penv || !penv->triggered)
- return -ENODEV;
- penv->smd_channel_ready = 1;
- pr_info("%s: SMD ctrl channel up\n", __func__);
- /* Schedule a work to do any post boot up activity */
- INIT_DELAYED_WORK(&penv->wcnss_work, wcnss_post_bootup);
- schedule_delayed_work(&penv->wcnss_work, msecs_to_jiffies(10000));
- return 0;
- }
- void wcnss_flush_delayed_boot_votes()
- {
- flush_delayed_work(&penv->wcnss_work);
- }
- EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
- static int __devexit
- wcnss_wlan_ctrl_remove(struct platform_device *pdev)
- {
- if (penv)
- penv->smd_channel_ready = 0;
- pr_info("%s: SMD ctrl channel down\n", __func__);
- return 0;
- }
- static struct platform_driver wcnss_wlan_ctrl_driver = {
- .driver = {
- .name = "WLAN_CTRL",
- .owner = THIS_MODULE,
- },
- .probe = wcnss_wlan_ctrl_probe,
- .remove = __devexit_p(wcnss_wlan_ctrl_remove),
- };
- static int __devexit
- wcnss_ctrl_remove(struct platform_device *pdev)
- {
- if (penv && penv->smd_ch)
- smd_close(penv->smd_ch);
- return 0;
- }
- static int __devinit
- wcnss_ctrl_probe(struct platform_device *pdev)
- {
- int ret = 0;
- if (!penv || !penv->triggered)
- return -ENODEV;
- ret = smd_named_open_on_edge(WCNSS_CTRL_CHANNEL, SMD_APPS_WCNSS,
- &penv->smd_ch, penv, wcnss_smd_notify_event);
- if (ret < 0) {
- pr_err("wcnss: cannot open the smd command channel %s: %d\n",
- WCNSS_CTRL_CHANNEL, ret);
- return -ENODEV;
- }
- smd_disable_read_intr(penv->smd_ch);
- return 0;
- }
- /* platform device for WCNSS_CTRL SMD channel */
- static struct platform_driver wcnss_ctrl_driver = {
- .driver = {
- .name = "WCNSS_CTRL",
- .owner = THIS_MODULE,
- },
- .probe = wcnss_ctrl_probe,
- .remove = __devexit_p(wcnss_ctrl_remove),
- };
- void wcnss_get_monotonic_boottime(struct timespec *ts)
- {
- get_monotonic_boottime(ts);
- }
- EXPORT_SYMBOL(wcnss_get_monotonic_boottime);
- struct device *wcnss_wlan_get_device(void)
- {
- if (penv && penv->pdev && penv->smd_channel_ready)
- return &penv->pdev->dev;
- return NULL;
- }
- EXPORT_SYMBOL(wcnss_wlan_get_device);
- struct platform_device *wcnss_get_platform_device(void)
- {
- if (penv && penv->pdev)
- return penv->pdev;
- return NULL;
- }
- EXPORT_SYMBOL(wcnss_get_platform_device);
- struct wcnss_wlan_config *wcnss_get_wlan_config(void)
- {
- if (penv && penv->pdev)
- return &penv->wlan_config;
- return NULL;
- }
- EXPORT_SYMBOL(wcnss_get_wlan_config);
- int wcnss_is_hw_pronto_ver3(void)
- {
- if (penv && penv->pdev)
- return penv->wlan_config.is_pronto_v3;
- return 0;
- }
- EXPORT_SYMBOL(wcnss_is_hw_pronto_ver3);
- int wcnss_device_ready(void)
- {
- if (penv && penv->pdev && penv->nv_downloaded &&
- !wcnss_device_is_shutdown())
- return 1;
- return 0;
- }
- EXPORT_SYMBOL(wcnss_device_ready);
- bool wcnss_cbc_complete(void)
- {
- if (penv && penv->pdev && penv->is_cbc_done &&
- !wcnss_device_is_shutdown())
- return true;
- return false;
- }
- EXPORT_SYMBOL(wcnss_cbc_complete);
- int wcnss_device_is_shutdown(void)
- {
- if (penv && penv->is_shutdown)
- return 1;
- return 0;
- }
- EXPORT_SYMBOL(wcnss_device_is_shutdown);
- struct resource *wcnss_wlan_get_memory_map(struct device *dev)
- {
- if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
- return penv->mmio_res;
- return NULL;
- }
- EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
- int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
- {
- if (penv && dev && (dev == &penv->pdev->dev) &&
- penv->tx_irq_res && penv->smd_channel_ready)
- return penv->tx_irq_res->start;
- return WCNSS_WLAN_IRQ_INVALID;
- }
- EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
- int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
- {
- if (penv && dev && (dev == &penv->pdev->dev) &&
- penv->rx_irq_res && penv->smd_channel_ready)
- return penv->rx_irq_res->start;
- return WCNSS_WLAN_IRQ_INVALID;
- }
- EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
- void wcnss_wlan_register_pm_ops(struct device *dev,
- const struct dev_pm_ops *pm_ops)
- {
- if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
- penv->pm_ops = pm_ops;
- }
- EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
- void wcnss_wlan_unregister_pm_ops(struct device *dev,
- const struct dev_pm_ops *pm_ops)
- {
- if (penv && dev && (dev == &penv->pdev->dev) && pm_ops) {
- if (penv->pm_ops == NULL) {
- pr_err("%s: pm_ops is already unregistered.\n",
- __func__);
- return;
- }
- if (pm_ops->suspend != penv->pm_ops->suspend ||
- pm_ops->resume != penv->pm_ops->resume)
- pr_err("PM APIs dont match with registered APIs\n");
- penv->pm_ops = NULL;
- }
- }
- EXPORT_SYMBOL(wcnss_wlan_unregister_pm_ops);
- void wcnss_register_thermal_mitigation(struct device *dev,
- void (*tm_notify)(struct device *, int))
- {
- if (penv && dev && tm_notify)
- penv->tm_notify = tm_notify;
- }
- EXPORT_SYMBOL(wcnss_register_thermal_mitigation);
- void wcnss_unregister_thermal_mitigation(
- void (*tm_notify)(struct device *, int))
- {
- if (penv && tm_notify) {
- if (tm_notify != penv->tm_notify)
- pr_err("tm_notify doesn't match registered\n");
- penv->tm_notify = NULL;
- }
- }
- EXPORT_SYMBOL(wcnss_unregister_thermal_mitigation);
- unsigned int wcnss_get_serial_number(void)
- {
- if (penv)
- return penv->serial_number;
- return 0;
- }
- EXPORT_SYMBOL(wcnss_get_serial_number);
- int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE])
- {
- if (!penv)
- return -ENODEV;
- memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE);
- pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
- penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
- penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
- penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
- return 0;
- }
- EXPORT_SYMBOL(wcnss_get_wlan_mac_address);
- static int enable_wcnss_suspend_notify;
- static int enable_wcnss_suspend_notify_set(const char *val,
- struct kernel_param *kp)
- {
- int ret;
- ret = param_set_int(val, kp);
- if (ret)
- return ret;
- if (enable_wcnss_suspend_notify)
- pr_debug("Suspend notification activated for wcnss\n");
- return 0;
- }
- module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
- param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
- int wcnss_xo_auto_detect_enabled(void)
- {
- return (has_autodetect_xo == 1 ? 1 : 0);
- }
- int wcnss_wlan_iris_xo_mode(void)
- {
- if (penv && penv->pdev && penv->smd_channel_ready)
- return penv->iris_xo_mode_set;
- return -ENODEV;
- }
- EXPORT_SYMBOL(wcnss_wlan_iris_xo_mode);
- void wcnss_suspend_notify(void)
- {
- void __iomem *pmu_spare_reg;
- u32 reg = 0;
- unsigned long flags;
- if (!enable_wcnss_suspend_notify)
- return;
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
- return;
- /* For Riva */
- pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
- spin_lock_irqsave(®_spinlock, flags);
- reg = readl_relaxed(pmu_spare_reg);
- reg |= RIVA_SUSPEND_BIT;
- writel_relaxed(reg, pmu_spare_reg);
- spin_unlock_irqrestore(®_spinlock, flags);
- }
- EXPORT_SYMBOL(wcnss_suspend_notify);
- void wcnss_resume_notify(void)
- {
- void __iomem *pmu_spare_reg;
- u32 reg = 0;
- unsigned long flags;
- if (!enable_wcnss_suspend_notify)
- return;
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
- return;
- /* For Riva */
- pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
- spin_lock_irqsave(®_spinlock, flags);
- reg = readl_relaxed(pmu_spare_reg);
- reg &= ~RIVA_SUSPEND_BIT;
- writel_relaxed(reg, pmu_spare_reg);
- spin_unlock_irqrestore(®_spinlock, flags);
- }
- EXPORT_SYMBOL(wcnss_resume_notify);
- static int wcnss_wlan_suspend(struct device *dev)
- {
- if (penv && dev && (dev == &penv->pdev->dev) &&
- penv->smd_channel_ready &&
- penv->pm_ops && penv->pm_ops->suspend)
- return penv->pm_ops->suspend(dev);
- return 0;
- }
- static int wcnss_wlan_resume(struct device *dev)
- {
- if (penv && dev && (dev == &penv->pdev->dev) &&
- penv->smd_channel_ready &&
- penv->pm_ops && penv->pm_ops->resume)
- return penv->pm_ops->resume(dev);
- return 0;
- }
- void wcnss_prevent_suspend()
- {
- if (penv)
- wake_lock(&penv->wcnss_wake_lock);
- }
- EXPORT_SYMBOL(wcnss_prevent_suspend);
- void wcnss_allow_suspend()
- {
- if (penv)
- wake_unlock(&penv->wcnss_wake_lock);
- }
- EXPORT_SYMBOL(wcnss_allow_suspend);
- int wcnss_hardware_type(void)
- {
- if (penv)
- return penv->wcnss_hw_type;
- else
- return -ENODEV;
- }
- EXPORT_SYMBOL(wcnss_hardware_type);
- int fw_cal_data_available(void)
- {
- if (penv)
- return penv->fw_cal_available;
- else
- return -ENODEV;
- }
- u32 wcnss_get_wlan_rx_buff_count(void)
- {
- if (penv)
- return penv->wlan_rx_buff_count;
- else
- return WCNSS_DEF_WLAN_RX_BUFF_COUNT;
- }
- EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
- int wcnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
- {
- if (penv && unsafe_ch_list &&
- (ch_count <= WCNSS_MAX_CH_NUM)) {
- memcpy((char *)penv->unsafe_ch_list,
- (char *)unsafe_ch_list, ch_count * sizeof(u16));
- penv->unsafe_ch_count = ch_count;
- return 0;
- } else
- return -ENODEV;
- }
- EXPORT_SYMBOL(wcnss_set_wlan_unsafe_channel);
- int wcnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 buffer_size,
- u16 *ch_count)
- {
- if (penv) {
- if (buffer_size < penv->unsafe_ch_count * sizeof(u16))
- return -ENODEV;
- memcpy((char *)unsafe_ch_list,
- (char *)penv->unsafe_ch_list,
- penv->unsafe_ch_count * sizeof(u16));
- *ch_count = penv->unsafe_ch_count;
- return 0;
- } else
- return -ENODEV;
- }
- EXPORT_SYMBOL(wcnss_get_wlan_unsafe_channel);
- static int wcnss_smd_tx(void *data, int len)
- {
- int ret = 0;
- ret = smd_write_avail(penv->smd_ch);
- if (ret < len) {
- pr_err("wcnss: no space available for smd frame\n");
- return -ENOSPC;
- }
- ret = smd_write(penv->smd_ch, data, len);
- if (ret < len) {
- pr_err("wcnss: failed to write Command %d", len);
- ret = -ENODEV;
- }
- return ret;
- }
- static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx)
- {
- mutex_lock(&penv->vbat_monitor_mutex);
- cancel_delayed_work_sync(&penv->vbatt_work);
- if (state == ADC_TM_LOW_STATE) {
- pr_debug("wcnss: low voltage notification triggered\n");
- penv->vbat_monitor_params.state_request =
- ADC_TM_HIGH_THR_ENABLE;
- penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD +
- WCNSS_VBATT_GUARD;
- penv->vbat_monitor_params.low_thr = 0;
- } else if (state == ADC_TM_HIGH_STATE) {
- penv->vbat_monitor_params.state_request =
- ADC_TM_LOW_THR_ENABLE;
- penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD -
- WCNSS_VBATT_GUARD;
- penv->vbat_monitor_params.high_thr = 0;
- pr_debug("wcnss: high voltage notification triggered\n");
- } else {
- pr_debug("wcnss: unknown voltage notification state: %d\n",
- state);
- mutex_unlock(&penv->vbat_monitor_mutex);
- return;
- }
- pr_debug("wcnss: set low thr to %d and high to %d\n",
- penv->vbat_monitor_params.low_thr,
- penv->vbat_monitor_params.high_thr);
- qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
- &penv->vbat_monitor_params);
- schedule_delayed_work(&penv->vbatt_work, msecs_to_jiffies(2000));
- mutex_unlock(&penv->vbat_monitor_mutex);
- }
- static int wcnss_setup_vbat_monitoring(void)
- {
- int rc = -1;
- if (!penv->adc_tm_dev) {
- pr_err("wcnss: not setting up vbatt\n");
- return rc;
- }
- penv->vbat_monitor_params.low_thr = WCNSS_VBATT_THRESHOLD;
- penv->vbat_monitor_params.high_thr = WCNSS_VBATT_THRESHOLD;
- penv->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
- penv->vbat_monitor_params.channel = VBAT_SNS;
- penv->vbat_monitor_params.btm_ctx = (void *)penv;
- penv->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
- penv->vbat_monitor_params.threshold_notification = &wcnss_notify_vbat;
- pr_debug("wcnss: set low thr to %d and high to %d\n",
- penv->vbat_monitor_params.low_thr,
- penv->vbat_monitor_params.high_thr);
- rc = qpnp_adc_tm_channel_measure(penv->adc_tm_dev,
- &penv->vbat_monitor_params);
- if (rc)
- pr_err("wcnss: tm setup failed: %d\n", rc);
- return rc;
- }
- static void wcnss_update_vbatt(struct work_struct *work)
- {
- struct vbatt_message vbatt_msg;
- int ret = 0;
- vbatt_msg.hdr.msg_type = WCNSS_VBATT_LEVEL_IND;
- vbatt_msg.hdr.msg_len = sizeof(struct vbatt_message);
- vbatt_msg.vbatt.threshold = WCNSS_VBATT_THRESHOLD;
- mutex_lock(&penv->vbat_monitor_mutex);
- if (penv->vbat_monitor_params.low_thr &&
- (penv->fw_vbatt_state == WCNSS_VBATT_LOW ||
- penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)) {
- vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_HIGH;
- penv->fw_vbatt_state = WCNSS_VBATT_HIGH;
- pr_debug("wcnss: send HIGH BATT to FW\n");
- } else if (!penv->vbat_monitor_params.low_thr &&
- (penv->fw_vbatt_state == WCNSS_VBATT_HIGH ||
- penv->fw_vbatt_state == WCNSS_CONFIG_UNSPECIFIED)){
- vbatt_msg.vbatt.curr_volt = WCNSS_VBATT_LOW;
- penv->fw_vbatt_state = WCNSS_VBATT_LOW;
- pr_debug("wcnss: send LOW BATT to FW\n");
- } else {
- mutex_unlock(&penv->vbat_monitor_mutex);
- return;
- }
- mutex_unlock(&penv->vbat_monitor_mutex);
- ret = wcnss_smd_tx(&vbatt_msg, vbatt_msg.hdr.msg_len);
- if (ret < 0)
- pr_err("wcnss: smd tx failed\n");
- return;
- }
- static unsigned char wcnss_fw_status(void)
- {
- int len = 0;
- int rc = 0;
- unsigned char fw_status = 0xFF;
- len = smd_read_avail(penv->smd_ch);
- if (len < 1) {
- pr_err("%s: invalid firmware status", __func__);
- return fw_status;
- }
- rc = smd_read(penv->smd_ch, &fw_status, 1);
- if (rc < 0) {
- pr_err("%s: incomplete data read from smd\n", __func__);
- return fw_status;
- }
- return fw_status;
- }
- static void wcnss_send_cal_rsp(unsigned char fw_status)
- {
- struct smd_msg_hdr *rsphdr;
- unsigned char *msg = NULL;
- int rc;
- msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
- if (NULL == msg) {
- pr_err("wcnss: %s: failed to get memory\n", __func__);
- return;
- }
- rsphdr = (struct smd_msg_hdr *)msg;
- rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
- rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
- memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
- rc = wcnss_smd_tx(msg, rsphdr->msg_len);
- if (rc < 0)
- pr_err("wcnss: smd tx failed\n");
- kfree(msg);
- }
- /* Collect calibrated data from WCNSS */
- void extract_cal_data(int len)
- {
- int rc;
- struct cal_data_params calhdr;
- unsigned char fw_status = WCNSS_RESP_FAIL;
- if (len < sizeof(struct cal_data_params)) {
- pr_err("wcnss: incomplete cal header length\n");
- return;
- }
- rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
- sizeof(struct cal_data_params));
- if (rc < sizeof(struct cal_data_params)) {
- pr_err("wcnss: incomplete cal header read from smd\n");
- return;
- }
- if (penv->fw_cal_exp_frag != calhdr.frag_number) {
- pr_err("wcnss: Invalid frgament");
- goto exit;
- }
- if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
- pr_err("wcnss: Invalid fragment size");
- goto exit;
- }
- if (penv->fw_cal_available) {
- /* ignore cal upload from SSR */
- smd_read(penv->smd_ch, NULL, calhdr.frag_size);
- penv->fw_cal_exp_frag++;
- if (calhdr.msg_flags & LAST_FRAGMENT) {
- penv->fw_cal_exp_frag = 0;
- goto exit;
- }
- return;
- }
- if (0 == calhdr.frag_number) {
- if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err("wcnss: Invalid cal data size %d",
- calhdr.total_size);
- goto exit;
- }
- kfree(penv->fw_cal_data);
- penv->fw_cal_rcvd = 0;
- penv->fw_cal_data = kmalloc(calhdr.total_size,
- GFP_KERNEL);
- if (penv->fw_cal_data == NULL) {
- smd_read(penv->smd_ch, NULL, calhdr.frag_size);
- goto exit;
- }
- }
- mutex_lock(&penv->dev_lock);
- if (penv->fw_cal_rcvd + calhdr.frag_size >
- MAX_CALIBRATED_DATA_SIZE) {
- pr_err("calibrated data size is more than expected %d",
- penv->fw_cal_rcvd + calhdr.frag_size);
- penv->fw_cal_exp_frag = 0;
- penv->fw_cal_rcvd = 0;
- smd_read(penv->smd_ch, NULL, calhdr.frag_size);
- goto unlock_exit;
- }
- rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
- calhdr.frag_size);
- if (rc < calhdr.frag_size)
- goto unlock_exit;
- penv->fw_cal_exp_frag++;
- penv->fw_cal_rcvd += calhdr.frag_size;
- if (calhdr.msg_flags & LAST_FRAGMENT) {
- penv->fw_cal_exp_frag = 0;
- penv->fw_cal_available = true;
- pr_info("wcnss: cal data collection completed\n");
- }
- mutex_unlock(&penv->dev_lock);
- wake_up(&penv->read_wait);
- if (penv->fw_cal_available) {
- fw_status = WCNSS_RESP_SUCCESS;
- wcnss_send_cal_rsp(fw_status);
- }
- return;
- unlock_exit:
- mutex_unlock(&penv->dev_lock);
- exit:
- wcnss_send_cal_rsp(fw_status);
- return;
- }
- static void wcnssctrl_rx_handler(struct work_struct *worker)
- {
- int len = 0;
- int rc = 0;
- unsigned char buf[sizeof(struct wcnss_version)];
- unsigned char build[WCNSS_MAX_BUILD_VER_LEN+1];
- struct smd_msg_hdr *phdr;
- struct smd_msg_hdr smd_msg;
- struct wcnss_version *pversion;
- int hw_type;
- unsigned char fw_status = 0;
- len = smd_read_avail(penv->smd_ch);
- if (len > WCNSS_MAX_FRAME_SIZE) {
- pr_err("wcnss: frame larger than the allowed size\n");
- smd_read(penv->smd_ch, NULL, len);
- return;
- }
- if (len < sizeof(struct smd_msg_hdr)) {
- pr_err("wcnss: incomplete header available len = %d\n", len);
- return;
- }
- rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
- if (rc < sizeof(struct smd_msg_hdr)) {
- pr_err("wcnss: incomplete header read from smd\n");
- return;
- }
- len -= sizeof(struct smd_msg_hdr);
- phdr = (struct smd_msg_hdr *)buf;
- switch (phdr->msg_type) {
- case WCNSS_VERSION_RSP:
- if (len != sizeof(struct wcnss_version)
- - sizeof(struct smd_msg_hdr)) {
- pr_err("wcnss: invalid version data from wcnss %d\n",
- len);
- return;
- }
- rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
- len);
- if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
- return;
- }
- pversion = (struct wcnss_version *)buf;
- penv->fw_major = pversion->major;
- penv->fw_minor = pversion->minor;
- snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
- "%02x%02x%02x%02x", pversion->major, pversion->minor,
- pversion->version, pversion->revision);
- pr_info("wcnss: version %s\n", penv->wcnss_version);
- /* schedule work to download nvbin to ccpu */
- hw_type = wcnss_hardware_type();
- switch (hw_type) {
- case WCNSS_RIVA_HW:
- /* supported only if riva major >= 1 and minor >= 4 */
- if ((pversion->major >= 1) && (pversion->minor >= 4)) {
- pr_info("wcnss: schedule dnld work for riva\n");
- schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
- }
- break;
- case WCNSS_PRONTO_HW:
- smd_msg.msg_type = WCNSS_BUILD_VER_REQ;
- smd_msg.msg_len = sizeof(smd_msg);
- rc = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
- if (rc < 0)
- pr_err("wcnss: smd tx failed: %s\n", __func__);
- /* supported only if pronto major >= 1 and minor >= 4 */
- if ((pversion->major >= 1) && (pversion->minor >= 4)) {
- pr_info("wcnss: schedule dnld work for pronto\n");
- schedule_work(&penv->wcnssctrl_nvbin_dnld_work);
- }
- break;
- default:
- pr_info("wcnss: unknown hw type (%d), will not schedule dnld work\n",
- hw_type);
- break;
- }
- break;
- case WCNSS_BUILD_VER_RSP:
- if (len > WCNSS_MAX_BUILD_VER_LEN) {
- pr_err("wcnss: invalid build version data from wcnss %d\n",
- len);
- return;
- }
- rc = smd_read(penv->smd_ch, build, len);
- if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
- return;
- }
- build[len] = 0;
- pr_info("wcnss: build version %s\n", build);
- break;
- case WCNSS_NVBIN_DNLD_RSP:
- penv->nv_downloaded = true;
- fw_status = wcnss_fw_status();
- pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
- fw_status);
- if (fw_status != WAIT_FOR_CBC_IND)
- penv->is_cbc_done = 1;
- wcnss_setup_vbat_monitoring();
- break;
- case WCNSS_CALDATA_DNLD_RSP:
- penv->nv_downloaded = true;
- fw_status = wcnss_fw_status();
- pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
- fw_status);
- break;
- case WCNSS_CBC_COMPLETE_IND:
- penv->is_cbc_done = 1;
- pr_debug("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n");
- break;
-
- case WCNSS_CBC_COMPLETE_IND:
- penv->is_cbc_done = 1;
- pr_debug("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n");
- break;
-
- case WCNSS_CALDATA_UPLD_REQ:
- extract_cal_data(len);
- break;
- default:
- pr_err("wcnss: invalid message type %d\n", phdr->msg_type);
- }
- return;
- }
- static void wcnss_send_version_req(struct work_struct *worker)
- {
- struct smd_msg_hdr smd_msg;
- int ret = 0;
- smd_msg.msg_type = WCNSS_VERSION_REQ;
- smd_msg.msg_len = sizeof(smd_msg);
- ret = wcnss_smd_tx(&smd_msg, smd_msg.msg_len);
- if (ret < 0)
- pr_err("wcnss: smd tx failed\n");
- return;
- }
- static void wcnss_send_pm_config(struct work_struct *worker)
- {
- struct smd_msg_hdr *hdr;
- unsigned char *msg = NULL;
- int rc, prop_len;
- u32 *payload;
- if (!of_find_property(penv->pdev->dev.of_node,
- "qcom,wcnss-pm", &prop_len))
- return;
- msg = kmalloc((sizeof(struct smd_msg_hdr) + prop_len), GFP_KERNEL);
- if (NULL == msg) {
- pr_err("wcnss: %s: failed to allocate memory\n", __func__);
- return;
- }
- payload = (u32 *)(msg + sizeof(struct smd_msg_hdr));
- prop_len /= sizeof(int);
- rc = of_property_read_u32_array(penv->pdev->dev.of_node,
- "qcom,wcnss-pm", payload, prop_len);
- if (rc < 0) {
- pr_err("wcnss: property read failed\n");
- kfree(msg);
- return;
- }
- pr_debug("%s:size=%d: <%d, %d, %d, %d, %d %d>\n", __func__,
- prop_len, *payload, *(payload+1), *(payload+2),
- *(payload+3), *(payload+4), *(payload+5));
- hdr = (struct smd_msg_hdr *)msg;
- hdr->msg_type = WCNSS_PM_CONFIG_REQ;
- hdr->msg_len = sizeof(struct smd_msg_hdr) + (prop_len * sizeof(int));
- rc = wcnss_smd_tx(msg, hdr->msg_len);
- if (rc < 0)
- pr_err("wcnss: smd tx failed\n");
- kfree(msg);
- return;
- }
- static DECLARE_RWSEM(wcnss_pm_sem);
- static void wcnss_nvbin_dnld(void)
- {
- int ret = 0;
- struct nvbin_dnld_req_msg *dnld_req_msg;
- unsigned short total_fragments = 0;
- unsigned short count = 0;
- unsigned short retry_count = 0;
- unsigned short cur_frag_size = 0;
- unsigned char *outbuffer = NULL;
- const void *nv_blob_addr = NULL;
- unsigned int nv_blob_size = 0;
- const struct firmware *nv = NULL;
- struct device *dev = &penv->pdev->dev;
- down_read(&wcnss_pm_sem);
- ret = request_firmware(&nv, NVBIN_FILE, dev);
- if (ret || !nv || !nv->data || !nv->size) {
- pr_err("wcnss: %s: request_firmware failed for %s(ret = %d)\n",
- __func__, NVBIN_FILE, ret);
- goto out;
- }
- /*
- * First 4 bytes in nv blob is validity bitmap.
- * We cannot validate nv, so skip those 4 bytes.
- */
- nv_blob_addr = nv->data + 4;
- nv_blob_size = nv->size - 4;
- total_fragments = TOTALFRAGMENTS(nv_blob_size);
- pr_info("wcnss: NV bin size: %d, total_fragments: %d\n",
- nv_blob_size, total_fragments);
- /* get buffer for nv bin dnld req message */
- outbuffer = kmalloc((sizeof(struct nvbin_dnld_req_msg) +
- NV_FRAGMENT_SIZE), GFP_KERNEL);
- if (NULL == outbuffer) {
- pr_err("wcnss: %s: failed to get buffer\n", __func__);
- goto err_free_nv;
- }
- dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
- dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
- dnld_req_msg->dnld_req_params.msg_flags = 0;
- for (count = 0; count < total_fragments; count++) {
- dnld_req_msg->dnld_req_params.frag_number = count;
- if (count == (total_fragments - 1)) {
- /* last fragment, take care of boundry condition */
- cur_frag_size = nv_blob_size % NV_FRAGMENT_SIZE;
- if (!cur_frag_size)
- cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.msg_flags |=
- LAST_FRAGMENT;
- dnld_req_msg->dnld_req_params.msg_flags |=
- CAN_RECEIVE_CALDATA;
- } else {
- cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.msg_flags &=
- ~LAST_FRAGMENT;
- }
- dnld_req_msg->dnld_req_params.nvbin_buffer_size =
- cur_frag_size;
- dnld_req_msg->hdr.msg_len =
- sizeof(struct nvbin_dnld_req_msg) + cur_frag_size;
- /* copy NV fragment */
- memcpy((outbuffer + sizeof(struct nvbin_dnld_req_msg)),
- (nv_blob_addr + count * NV_FRAGMENT_SIZE),
- cur_frag_size);
- ret = wcnss_smd_tx(outbuffer, dnld_req_msg->hdr.msg_len);
- retry_count = 0;
- while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
- __func__);
- pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, dnld_req_msg->hdr.msg_len,
- total_fragments, retry_count);
- /* wait and try again */
- msleep(20);
- retry_count++;
- ret = wcnss_smd_tx(outbuffer,
- dnld_req_msg->hdr.msg_len);
- }
- if (ret < 0) {
- pr_err("wcnss: %s: smd tx failed\n", __func__);
- pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, dnld_req_msg->hdr.msg_len,
- total_fragments, retry_count);
- goto err_dnld;
- }
- }
- err_dnld:
- /* free buffer */
- kfree(outbuffer);
- err_free_nv:
- /* release firmware */
- release_firmware(nv);
- out:
- up_read(&wcnss_pm_sem);
- return;
- }
- static void wcnss_pm_qos_enable_pc(struct work_struct *worker)
- {
- wcnss_disable_pc_remove_req();
- return;
- }
- static void wcnss_caldata_dnld(const void *cal_data,
- unsigned int cal_data_size, bool msg_to_follow)
- {
- int ret = 0;
- struct cal_data_msg *cal_msg;
- unsigned short total_fragments = 0;
- unsigned short count = 0;
- unsigned short retry_count = 0;
- unsigned short cur_frag_size = 0;
- unsigned char *outbuffer = NULL;
- total_fragments = TOTALFRAGMENTS(cal_data_size);
- outbuffer = kmalloc((sizeof(struct cal_data_msg) +
- NV_FRAGMENT_SIZE), GFP_KERNEL);
- if (NULL == outbuffer) {
- pr_err("wcnss: %s: failed to get buffer\n", __func__);
- return;
- }
- cal_msg = (struct cal_data_msg *)outbuffer;
- cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
- cal_msg->cal_params.msg_flags = 0;
- for (count = 0; count < total_fragments; count++) {
- cal_msg->cal_params.frag_number = count;
- if (count == (total_fragments - 1)) {
- cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
- if (!cur_frag_size)
- cur_frag_size = NV_FRAGMENT_SIZE;
- cal_msg->cal_params.msg_flags
- |= LAST_FRAGMENT;
- if (msg_to_follow)
- cal_msg->cal_params.msg_flags |=
- MESSAGE_TO_FOLLOW;
- } else {
- cur_frag_size = NV_FRAGMENT_SIZE;
- cal_msg->cal_params.msg_flags &=
- ~LAST_FRAGMENT;
- }
- cal_msg->cal_params.total_size = cal_data_size;
- cal_msg->cal_params.frag_size =
- cur_frag_size;
- cal_msg->hdr.msg_len =
- sizeof(struct cal_data_msg) + cur_frag_size;
- memcpy((outbuffer + sizeof(struct cal_data_msg)),
- (cal_data + count * NV_FRAGMENT_SIZE),
- cur_frag_size);
- ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
- retry_count = 0;
- while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
- __func__);
- pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, cal_msg->hdr.msg_len,
- total_fragments, retry_count);
- /* wait and try again */
- msleep(20);
- retry_count++;
- ret = wcnss_smd_tx(outbuffer,
- cal_msg->hdr.msg_len);
- }
- if (ret < 0) {
- pr_err("wcnss: %s: smd tx failed\n", __func__);
- pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
- count, cal_msg->hdr.msg_len,
- total_fragments, retry_count);
- goto err_dnld;
- }
- }
- err_dnld:
- /* free buffer */
- kfree(outbuffer);
- return;
- }
- static void wcnss_nvbin_dnld_main(struct work_struct *worker)
- {
- int retry = 0;
- if (!FW_CALDATA_CAPABLE())
- goto nv_download;
- if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
- != has_calibrated_data && !penv->user_cal_available) {
- while (!penv->user_cal_available && retry++ < 5)
- msleep(500);
- }
- if (penv->fw_cal_available) {
- pr_info_ratelimited("wcnss: cal download, using fw cal");
- wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, true);
- } else if (penv->user_cal_available) {
- pr_info_ratelimited("wcnss: cal download, using user cal");
- wcnss_caldata_dnld(penv->user_cal_data,
- penv->user_cal_rcvd, true);
- }
- nv_download:
- pr_info_ratelimited("wcnss: NV download");
- wcnss_nvbin_dnld();
- return;
- }
- static int wcnss_pm_notify(struct notifier_block *b,
- unsigned long event, void *p)
- {
- switch (event) {
- case PM_SUSPEND_PREPARE:
- down_write(&wcnss_pm_sem);
- break;
- case PM_POST_SUSPEND:
- up_write(&wcnss_pm_sem);
- break;
- }
- return NOTIFY_DONE;
- }
- static struct notifier_block wcnss_pm_notifier = {
- .notifier_call = wcnss_pm_notify,
- };
- static int wcnss_ctrl_open(struct inode *inode, struct file *file)
- {
- int rc = 0;
- if (!penv || penv->ctrl_device_opened)
- return -EFAULT;
- penv->ctrl_device_opened = 1;
- return rc;
- }
- void process_usr_ctrl_cmd(u8 *buf, size_t len)
- {
- u16 cmd = buf[0] << 8 | buf[1];
- switch (cmd) {
- case WCNSS_USR_SERIAL_NUM:
- if (WCNSS_MIN_SERIAL_LEN > len) {
- pr_err("%s: Invalid serial number\n", __func__);
- return;
- }
- penv->serial_number = buf[2] << 24 | buf[3] << 16
- | buf[4] << 8 | buf[5];
- break;
- case WCNSS_USR_HAS_CAL_DATA:
- if (1 < buf[2])
- pr_err("%s: Invalid data for cal %d\n", __func__,
- buf[2]);
- has_calibrated_data = buf[2];
- break;
- case WCNSS_USR_WLAN_MAC_ADDR:
- memcpy(&penv->wlan_nv_macAddr, &buf[2],
- sizeof(penv->wlan_nv_macAddr));
- pr_debug("%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
- penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
- penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
- penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
- break;
- default:
- pr_err("%s: Invalid command %d\n", __func__, cmd);
- break;
- }
- }
- static ssize_t wcnss_ctrl_write(struct file *fp, const char __user
- *user_buffer, size_t count, loff_t *position)
- {
- int rc = 0;
- u8 buf[WCNSS_MAX_CMD_LEN];
- if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count
- || WCNSS_MIN_CMD_LEN > count)
- return -EFAULT;
- mutex_lock(&penv->ctrl_lock);
- rc = copy_from_user(buf, user_buffer, count);
- if (0 == rc)
- process_usr_ctrl_cmd(buf, count);
- mutex_unlock(&penv->ctrl_lock);
- return rc;
- }
- static const struct file_operations wcnss_ctrl_fops = {
- .owner = THIS_MODULE,
- .open = wcnss_ctrl_open,
- .write = wcnss_ctrl_write,
- };
- static struct miscdevice wcnss_usr_ctrl = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = CTRL_DEVICE,
- .fops = &wcnss_ctrl_fops,
- };
- static int
- wcnss_trigger_config(struct platform_device *pdev)
- {
- int ret;
- struct qcom_wcnss_opts *pdata;
- unsigned long wcnss_phys_addr;
- int size = 0;
- struct resource *res;
- int is_pronto_v3;
- int pil_retry = 0;
- int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-pronto-hw");
- is_pronto_v3 = of_property_read_bool(pdev->dev.of_node,
- "qcom,is-pronto-v3");
- if (of_property_read_u32(pdev->dev.of_node,
- "qcom,wlan-rx-buff-count", &penv->wlan_rx_buff_count)) {
- penv->wlan_rx_buff_count = WCNSS_DEF_WLAN_RX_BUFF_COUNT;
- }
- /* make sure we are only triggered once */
- if (penv->triggered)
- return 0;
- penv->triggered = 1;
- /* initialize the WCNSS device configuration */
- pdata = pdev->dev.platform_data;
- if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
- if (has_pronto_hw) {
- has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-48mhz-xo");
- } else {
- has_48mhz_xo = pdata->has_48mhz_xo;
- }
- }
- penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
- penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
- penv->wlan_config.is_pronto_v3 = is_pronto_v3;
- if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
- has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
- "qcom,has-autodetect-xo");
- }
- penv->thermal_mitigation = 0;
- strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
- /* Configure 5 wire GPIOs */
- if (!has_pronto_hw) {
- penv->gpios_5wire = platform_get_resource_byname(pdev,
- IORESOURCE_IO, "wcnss_gpios_5wire");
- /* allocate 5-wire GPIO resources */
- if (!penv->gpios_5wire) {
- dev_err(&pdev->dev, "insufficient IO resources\n");
- ret = -ENOENT;
- goto fail_gpio_res;
- }
- ret = wcnss_gpios_config(penv->gpios_5wire, true);
- } else
- ret = wcnss_pronto_gpios_config(&pdev->dev, true);
- if (ret) {
- dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
- goto fail_gpio_res;
- }
- /* power up the WCNSS */
- ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
- WCNSS_WLAN_SWITCH_ON,
- &penv->iris_xo_mode_set);
- if (ret) {
- dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
- goto fail_power;
- }
- /* allocate resources */
- penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "wcnss_mmio");
- penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlantx_irq");
- penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlanrx_irq");
- if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
- dev_err(&pdev->dev, "insufficient resources\n");
- ret = -ENOENT;
- goto fail_res;
- }
- INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
- INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
- INIT_WORK(&penv->wcnss_pm_config_work, wcnss_send_pm_config);
- INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
- INIT_DELAYED_WORK(&penv->wcnss_pm_qos_del_req, wcnss_pm_qos_enable_pc);
- wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
- /* Add pm_qos request to disable power collapse for DDR */
- wcnss_disable_pc_add_req();
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
- size = 0x3000;
- wcnss_phys_addr = MSM_PRONTO_PHYS;
- } else {
- wcnss_phys_addr = MSM_RIVA_PHYS;
- size = SZ_256;
- }
- penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
- if (!penv->msm_wcnss_base) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap;
- }
- if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
- penv->riva_ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
- if (!penv->riva_ccu_base) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap2;
- }
- } else {
- penv->pronto_a2xb_base = ioremap(MSM_PRONTO_A2XB_BASE, SZ_512);
- if (!penv->pronto_a2xb_base) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap2;
- }
- penv->pronto_ccpu_base = ioremap(MSM_PRONTO_CCPU_BASE, SZ_512);
- if (!penv->pronto_ccpu_base) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical failed\n", __func__);
- goto fail_ioremap3;
- }
- /* for reset FIQ */
- res = platform_get_resource_byname(penv->pdev,
- IORESOURCE_MEM, "wcnss_fiq");
- if (!res) {
- dev_err(&pdev->dev, "insufficient irq mem resources\n");
- ret = -ENOENT;
- goto fail_ioremap4;
- }
- penv->fiq_reg = ioremap_nocache(res->start, resource_size(res));
- if (!penv->fiq_reg) {
- pr_err("wcnss: %s: ioremap_nocache() failed fiq_reg addr:%pr\n",
- __func__, &res->start);
- ret = -ENOMEM;
- goto fail_ioremap4;
- }
- penv->pronto_saw2_base = ioremap_nocache(MSM_PRONTO_SAW2_BASE,
- SZ_32);
- if (!penv->pronto_saw2_base) {
- pr_err("%s: ioremap wcnss physical(saw2) failed\n",
- __func__);
- ret = -ENOMEM;
- goto fail_ioremap5;
- }
- penv->pronto_pll_base = ioremap_nocache(MSM_PRONTO_PLL_BASE,
- SZ_64);
- if (!penv->pronto_pll_base) {
- pr_err("%s: ioremap wcnss physical(pll) failed\n",
- __func__);
- ret = -ENOMEM;
- goto fail_ioremap6;
- }
- penv->wlan_tx_phy_aborts = ioremap(MSM_PRONTO_TXP_PHY_ABORT,
- SZ_8);
- if (!penv->wlan_tx_phy_aborts) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wlan TX PHY failed\n", __func__);
- goto fail_ioremap7;
- }
- penv->wlan_brdg_err_source = ioremap(MSM_PRONTO_BRDG_ERR_SRC,
- SZ_8);
- if (!penv->wlan_brdg_err_source) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wlan BRDG ERR failed\n", __func__);
- goto fail_ioremap8;
- }
- penv->wlan_tx_status = ioremap(MSM_PRONTO_TXP_STATUS, SZ_8);
- if (!penv->wlan_tx_status) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
- goto fail_ioremap9;
- }
- penv->alarms_txctl = ioremap(MSM_PRONTO_ALARMS_TXCTL, SZ_8);
- if (!penv->alarms_txctl) {
- ret = -ENOMEM;
- pr_err("%s: ioremap alarms TXCTL failed\n", __func__);
- goto fail_ioremap10;
- }
- penv->alarms_tactl = ioremap(MSM_PRONTO_ALARMS_TACTL, SZ_8);
- if (!penv->alarms_tactl) {
- ret = -ENOMEM;
- pr_err("%s: ioremap alarms TACTL failed\n", __func__);
- goto fail_ioremap11;
- }
- penv->pronto_mcu_base = ioremap(MSM_PRONTO_MCU_BASE, SZ_1K);
- if (!penv->pronto_mcu_base) {
- ret = -ENOMEM;
- pr_err("%s: ioremap wcnss physical(mcu) failed\n",
- __func__);
- goto fail_ioremap12;
- }
- }
- penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
- if (IS_ERR(penv->adc_tm_dev)) {
- pr_err("%s: adc get failed\n", __func__);
- penv->adc_tm_dev = NULL;
- } else {
- INIT_DELAYED_WORK(&penv->vbatt_work, wcnss_update_vbatt);
- penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
- }
- do {
- /* trigger initialization of the WCNSS */
- penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
- if (IS_ERR(penv->pil)) {
- dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
- ret = PTR_ERR(penv->pil);
- wcnss_disable_pc_add_req();
- wcnss_pronto_log_debug_regs();
- }
- } while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
- if (IS_ERR(penv->pil)) {
- wcnss_reset_intr();
- if (penv->wcnss_notif_hdle)
- subsys_notif_unregister_notifier(penv->wcnss_notif_hdle,
- &wnb);
- penv->pil = NULL;
- goto fail_pil;
- }
- /* Remove pm_qos request */
- wcnss_disable_pc_remove_req();
- return 0;
- fail_pil:
- if (penv->riva_ccu_base)
- iounmap(penv->riva_ccu_base);
- if (penv->pronto_mcu_base)
- iounmap(penv->pronto_mcu_base);
- fail_ioremap12:
- if (penv->alarms_tactl)
- iounmap(penv->alarms_tactl);
- fail_ioremap11:
- if (penv->alarms_txctl)
- iounmap(penv->alarms_txctl);
- fail_ioremap10:
- if (penv->wlan_tx_status)
- iounmap(penv->wlan_tx_status);
- fail_ioremap9:
- if (penv->wlan_brdg_err_source)
- iounmap(penv->wlan_brdg_err_source);
- fail_ioremap8:
- if (penv->wlan_tx_phy_aborts)
- iounmap(penv->wlan_tx_phy_aborts);
- fail_ioremap7:
- if (penv->pronto_pll_base)
- iounmap(penv->pronto_pll_base);
- fail_ioremap6:
- if (penv->pronto_saw2_base)
- iounmap(penv->pronto_saw2_base);
- fail_ioremap5:
- if (penv->fiq_reg)
- iounmap(penv->fiq_reg);
- fail_ioremap4:
- if (penv->pronto_ccpu_base)
- iounmap(penv->pronto_ccpu_base);
- fail_ioremap3:
- if (penv->pronto_a2xb_base)
- iounmap(penv->pronto_a2xb_base);
- fail_ioremap2:
- if (penv->msm_wcnss_base)
- iounmap(penv->msm_wcnss_base);
- fail_ioremap:
- wake_lock_destroy(&penv->wcnss_wake_lock);
- fail_res:
- wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
- WCNSS_WLAN_SWITCH_OFF, NULL);
- fail_power:
- if (has_pronto_hw)
- wcnss_pronto_gpios_config(&pdev->dev, false);
- else
- wcnss_gpios_config(penv->gpios_5wire, false);
- fail_gpio_res:
- wcnss_disable_pc_remove_req();
- penv = NULL;
- return ret;
- }
- /* wlan prop driver cannot invoke cancel_work_sync
- * function directly, so to invoke this function it
- * call wcnss_flush_work function
- */
- void wcnss_flush_work(struct work_struct *work)
- {
- struct work_struct *cnss_work = work;
- if (cnss_work != NULL)
- cancel_work_sync(cnss_work);
- }
- EXPORT_SYMBOL(wcnss_flush_work);
- /* wlan prop driver cannot invoke show_stack
- * function directly, so to invoke this function it
- * call wcnss_dump_stack function
- */
- void wcnss_dump_stack(struct task_struct *task)
- {
- show_stack(task, NULL);
- }
- EXPORT_SYMBOL(wcnss_dump_stack);
- /* wlan prop driver cannot invoke cancel_delayed_work_sync
- * function directly, so to invoke this function it call
- * wcnss_flush_delayed_work function
- */
- void wcnss_flush_delayed_work(struct delayed_work *dwork)
- {
- struct delayed_work *cnss_dwork = dwork;
- if (cnss_dwork != NULL)
- cancel_delayed_work_sync(cnss_dwork);
- }
- EXPORT_SYMBOL(wcnss_flush_delayed_work);
- static int wcnss_node_open(struct inode *inode, struct file *file)
- {
- struct platform_device *pdev;
- int rc = 0;
- if (!penv)
- return -EFAULT;
- if (!penv->triggered) {
- pr_info(DEVICE " triggered by userspace\n");
- pdev = penv->pdev;
- rc = wcnss_trigger_config(pdev);
- if (rc)
- return -EFAULT;
- }
- return rc;
- }
- static ssize_t wcnss_wlan_read(struct file *fp, char __user
- *buffer, size_t count, loff_t *position)
- {
- int rc = 0;
- if (!penv)
- return -EFAULT;
- rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
- > penv->user_cal_read || penv->fw_cal_available);
- if (rc < 0)
- return rc;
- mutex_lock(&penv->dev_lock);
- if (penv->fw_cal_available && penv->fw_cal_rcvd
- == penv->user_cal_read) {
- rc = 0;
- goto exit;
- }
- if (count > penv->fw_cal_rcvd - penv->user_cal_read)
- count = penv->fw_cal_rcvd - penv->user_cal_read;
- rc = copy_to_user(buffer, penv->fw_cal_data +
- penv->user_cal_read, count);
- if (rc == 0) {
- penv->user_cal_read += count;
- rc = count;
- }
- exit:
- mutex_unlock(&penv->dev_lock);
- return rc;
- }
- /* first (valid) write to this device should be 4 bytes cal file size */
- static ssize_t wcnss_wlan_write(struct file *fp, const char __user
- *user_buffer, size_t count, loff_t *position)
- {
- int rc = 0;
- char *cal_data = NULL;
- if (!penv || penv->user_cal_available)
- return -EFAULT;
- if (!penv->user_cal_rcvd && count >= 4 && !penv->user_cal_exp_size) {
- mutex_lock(&penv->dev_lock);
- rc = copy_from_user((void *)&penv->user_cal_exp_size,
- user_buffer, 4);
- if (!penv->user_cal_exp_size ||
- penv->user_cal_exp_size > MAX_CALIBRATED_DATA_SIZE) {
- pr_err(DEVICE " invalid size to write %d\n",
- penv->user_cal_exp_size);
- penv->user_cal_exp_size = 0;
- mutex_unlock(&penv->dev_lock);
- return -EFAULT;
- }
- mutex_unlock(&penv->dev_lock);
- return count;
- } else if (!penv->user_cal_rcvd && count < 4) {
- return -EFAULT;
- }
- mutex_lock(&penv->dev_lock);
- if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
- (penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
- pr_err(DEVICE " invalid size to write %zu\n", count +
- penv->user_cal_rcvd);
- mutex_unlock(&penv->dev_lock);
- return -ENOMEM;
- }
- cal_data = kmalloc(count, GFP_KERNEL);
- if (!cal_data) {
- mutex_unlock(&penv->dev_lock);
- return -ENOMEM;
- }
- rc = copy_from_user(cal_data, user_buffer, count);
- if (!rc) {
- memcpy(penv->user_cal_data + penv->user_cal_rcvd,
- cal_data, count);
- penv->user_cal_rcvd += count;
- rc += count;
- }
- kfree(cal_data);
- if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
- penv->user_cal_available = true;
- pr_info_ratelimited("wcnss: user cal written");
- }
- mutex_unlock(&penv->dev_lock);
- return rc;
- }
- static int wcnss_node_release(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
- void *ss_handle)
- {
- pr_info("%s: wcnss notification event: %lu\n", __func__, code);
- if (code == SUBSYS_BEFORE_SHUTDOWN) {
- penv->is_shutdown = 1;
- wcnss_disable_pc_add_req();
- schedule_delayed_work(&penv->wcnss_pm_qos_del_req,
- msecs_to_jiffies(WCNSS_PM_QOS_TIMEOUT));
- } else if (code == SUBSYS_POWERUP_FAILURE) {
- wcnss_pronto_log_debug_regs();
- wcnss_disable_pc_remove_req();
- } else if (SUBSYS_AFTER_POWERUP == code)
- penv->is_shutdown = 0;
- return NOTIFY_DONE;
- }
- static const struct file_operations wcnss_node_fops = {
- .owner = THIS_MODULE,
- .open = wcnss_node_open,
- .read = wcnss_wlan_read,
- .write = wcnss_wlan_write,
- .release = wcnss_node_release,
- };
- static struct miscdevice wcnss_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE,
- .fops = &wcnss_node_fops,
- };
- static int __devinit
- wcnss_wlan_probe(struct platform_device *pdev)
- {
- int ret = 0;
- /* verify we haven't been called more than once */
- if (penv) {
- dev_err(&pdev->dev, "cannot handle multiple devices.\n");
- return -ENODEV;
- }
- /* create an environment to track the device */
- penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
- if (!penv) {
- dev_err(&pdev->dev, "cannot allocate device memory.\n");
- return -ENOMEM;
- }
- penv->pdev = pdev;
- penv->user_cal_data =
- devm_kzalloc(&pdev->dev, MAX_CALIBRATED_DATA_SIZE, GFP_KERNEL);
- if (!penv->user_cal_data) {
- dev_err(&pdev->dev, "Failed to alloc memory for cal data.\n");
- return -ENOMEM;
- }
- /* register sysfs entries */
- ret = wcnss_create_sysfs(&pdev->dev);
- if (ret) {
- penv = NULL;
- return -ENOENT;
- }
- /* register wcnss event notification */
- penv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
- if (IS_ERR(penv->wcnss_notif_hdle)) {
- pr_err("wcnss: register event notification failed!\n");
- return PTR_ERR(penv->wcnss_notif_hdle);
- }
- mutex_init(&penv->dev_lock);
- mutex_init(&penv->ctrl_lock);
- mutex_init(&penv->vbat_monitor_mutex);
- mutex_init(&penv->pm_qos_mutex);
- init_waitqueue_head(&penv->read_wait);
- penv->user_cal_rcvd = 0;
- penv->user_cal_read = 0;
- penv->user_cal_exp_size = 0;
- penv->user_cal_available = false;
- /* Since we were built into the kernel we'll be called as part
- * of kernel initialization. We don't know if userspace
- * applications are available to service PIL at this time
- * (they probably are not), so we simply create a device node
- * here. When userspace is available it should touch the
- * device so that we know that WCNSS configuration can take
- * place
- */
- pr_info(DEVICE " probed in built-in mode\n");
- misc_register(&wcnss_usr_ctrl);
- return misc_register(&wcnss_misc);
- }
- static int __devexit
- wcnss_wlan_remove(struct platform_device *pdev)
- {
- if (penv->wcnss_notif_hdle)
- subsys_notif_unregister_notifier(penv->wcnss_notif_hdle, &wnb);
- wcnss_remove_sysfs(&pdev->dev);
- penv = NULL;
- return 0;
- }
- static const struct dev_pm_ops wcnss_wlan_pm_ops = {
- .suspend = wcnss_wlan_suspend,
- .resume = wcnss_wlan_resume,
- };
- #ifdef CONFIG_WCNSS_CORE_PRONTO
- static struct of_device_id msm_wcnss_pronto_match[] = {
- {.compatible = "qcom,wcnss_wlan"},
- {}
- };
- #endif
- static struct platform_driver wcnss_wlan_driver = {
- .driver = {
- .name = DEVICE,
- .owner = THIS_MODULE,
- .pm = &wcnss_wlan_pm_ops,
- #ifdef CONFIG_WCNSS_CORE_PRONTO
- .of_match_table = msm_wcnss_pronto_match,
- #endif
- },
- .probe = wcnss_wlan_probe,
- .remove = __devexit_p(wcnss_wlan_remove),
- };
- static int __init wcnss_wlan_init(void)
- {
- int ret = 0;
- platform_driver_register(&wcnss_wlan_driver);
- platform_driver_register(&wcnss_wlan_ctrl_driver);
- platform_driver_register(&wcnss_ctrl_driver);
- register_pm_notifier(&wcnss_pm_notifier);
- #ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
- ret = wcnss_prealloc_init();
- if (ret < 0)
- pr_err("wcnss: pre-allocation failed\n");
- #endif
- return ret;
- }
- static void __exit wcnss_wlan_exit(void)
- {
- if (penv) {
- if (penv->pil)
- subsystem_put(penv->pil);
- penv = NULL;
- }
- #ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
- wcnss_prealloc_deinit();
- #endif
- unregister_pm_notifier(&wcnss_pm_notifier);
- platform_driver_unregister(&wcnss_ctrl_driver);
- platform_driver_unregister(&wcnss_wlan_ctrl_driver);
- platform_driver_unregister(&wcnss_wlan_driver);
- }
- module_init(wcnss_wlan_init);
- module_exit(wcnss_wlan_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_VERSION(VERSION);
- MODULE_DESCRIPTION(DEVICE "Driver");
|