| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * Bluetooth support for Intel PCIe devices
- *
- * Copyright (C) 2024 Intel Corporation
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/firmware.h>
- #include <linux/pci.h>
- #include <linux/wait.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/unaligned.h>
- #include <linux/devcoredump.h>
- #include <net/bluetooth/bluetooth.h>
- #include <net/bluetooth/hci_core.h>
- #include <net/bluetooth/hci_drv.h>
- #include "btintel.h"
- #include "btintel_pcie.h"
- #define VERSION "0.1"
- #define BTINTEL_PCI_DEVICE(dev, subdev) \
- .vendor = PCI_VENDOR_ID_INTEL, \
- .device = (dev), \
- .subvendor = PCI_ANY_ID, \
- .subdevice = (subdev), \
- .driver_data = 0
- #define POLL_INTERVAL_US 10
- /* Intel Bluetooth PCIe device id table */
- static const struct pci_device_id btintel_pcie_table[] = {
- /* BlazarI, Wildcat Lake */
- { BTINTEL_PCI_DEVICE(0x4D76, PCI_ANY_ID) },
- /* BlazarI, Lunar Lake */
- { BTINTEL_PCI_DEVICE(0xA876, PCI_ANY_ID) },
- /* Scorpious, Panther Lake-H484 */
- { BTINTEL_PCI_DEVICE(0xE376, PCI_ANY_ID) },
- /* Scorpious, Panther Lake-H404 */
- { BTINTEL_PCI_DEVICE(0xE476, PCI_ANY_ID) },
- { 0 }
- };
- MODULE_DEVICE_TABLE(pci, btintel_pcie_table);
- struct btintel_pcie_dev_recovery {
- struct list_head list;
- u8 count;
- time64_t last_error;
- char name[];
- };
- /* Intel PCIe uses 4 bytes of HCI type instead of 1 byte BT SIG HCI type */
- #define BTINTEL_PCIE_HCI_TYPE_LEN 4
- #define BTINTEL_PCIE_HCI_CMD_PKT 0x00000001
- #define BTINTEL_PCIE_HCI_ACL_PKT 0x00000002
- #define BTINTEL_PCIE_HCI_SCO_PKT 0x00000003
- #define BTINTEL_PCIE_HCI_EVT_PKT 0x00000004
- #define BTINTEL_PCIE_HCI_ISO_PKT 0x00000005
- #define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
- #define BTINTEL_PCIE_BLZR_HWEXP_SIZE 1024
- #define BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR 0xB00A7C00
- #define BTINTEL_PCIE_SCP_HWEXP_SIZE 4096
- #define BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR 0xB030F800
- #define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
- #define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER 0x17A2
- #define BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT 0x1E61
- #define BTINTEL_PCIE_RESET_WINDOW_SECS 5
- #define BTINTEL_PCIE_FLR_MAX_RETRY 1
- /* Alive interrupt context */
- enum {
- BTINTEL_PCIE_ROM,
- BTINTEL_PCIE_FW_DL,
- BTINTEL_PCIE_HCI_RESET,
- BTINTEL_PCIE_INTEL_HCI_RESET1,
- BTINTEL_PCIE_INTEL_HCI_RESET2,
- BTINTEL_PCIE_D0,
- BTINTEL_PCIE_D3
- };
- /* Structure for dbgc fragment buffer
- * @buf_addr_lsb: LSB of the buffer's physical address
- * @buf_addr_msb: MSB of the buffer's physical address
- * @buf_size: Total size of the buffer
- */
- struct btintel_pcie_dbgc_ctxt_buf {
- u32 buf_addr_lsb;
- u32 buf_addr_msb;
- u32 buf_size;
- };
- /* Structure for dbgc fragment
- * @magic_num: 0XA5A5A5A5
- * @ver: For Driver-FW compatibility
- * @total_size: Total size of the payload debug info
- * @num_buf: Num of allocated debug bufs
- * @bufs: All buffer's addresses and sizes
- */
- struct btintel_pcie_dbgc_ctxt {
- u32 magic_num;
- u32 ver;
- u32 total_size;
- u32 num_buf;
- struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
- };
- struct btintel_pcie_removal {
- struct pci_dev *pdev;
- struct work_struct work;
- };
- static LIST_HEAD(btintel_pcie_recovery_list);
- static DEFINE_SPINLOCK(btintel_pcie_recovery_lock);
- static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
- {
- switch (alive_intr_ctxt) {
- case BTINTEL_PCIE_ROM:
- return "rom";
- case BTINTEL_PCIE_FW_DL:
- return "fw_dl";
- case BTINTEL_PCIE_D0:
- return "d0";
- case BTINTEL_PCIE_D3:
- return "d3";
- case BTINTEL_PCIE_HCI_RESET:
- return "hci_reset";
- case BTINTEL_PCIE_INTEL_HCI_RESET1:
- return "intel_reset1";
- case BTINTEL_PCIE_INTEL_HCI_RESET2:
- return "intel_reset2";
- default:
- return "unknown";
- }
- }
- /* This function initializes the memory for DBGC buffers and formats the
- * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
- * size as the payload
- */
- static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
- {
- struct btintel_pcie_dbgc_ctxt db_frag;
- struct data_buf *buf;
- int i;
- data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
- data->dbgc.bufs = devm_kcalloc(&data->pdev->dev, data->dbgc.count,
- sizeof(*buf), GFP_KERNEL);
- if (!data->dbgc.bufs)
- return -ENOMEM;
- data->dbgc.buf_v_addr = dmam_alloc_coherent(&data->pdev->dev,
- data->dbgc.count *
- BTINTEL_PCIE_DBGC_BUFFER_SIZE,
- &data->dbgc.buf_p_addr,
- GFP_KERNEL | __GFP_NOWARN);
- if (!data->dbgc.buf_v_addr)
- return -ENOMEM;
- data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
- sizeof(struct btintel_pcie_dbgc_ctxt),
- &data->dbgc.frag_p_addr,
- GFP_KERNEL | __GFP_NOWARN);
- if (!data->dbgc.frag_v_addr)
- return -ENOMEM;
- data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
- db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
- db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
- db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
- db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
- for (i = 0; i < data->dbgc.count; i++) {
- buf = &data->dbgc.bufs[i];
- buf->data_p_addr = data->dbgc.buf_p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
- buf->data = data->dbgc.buf_v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
- db_frag.bufs[i].buf_addr_lsb = lower_32_bits(buf->data_p_addr);
- db_frag.bufs[i].buf_addr_msb = upper_32_bits(buf->data_p_addr);
- db_frag.bufs[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
- }
- memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
- return 0;
- }
- static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
- u16 queue_num)
- {
- bt_dev_dbg(hdev, "IA: %s: tr-h:%02u tr-t:%02u cr-h:%02u cr-t:%02u",
- queue_num == BTINTEL_PCIE_TXQ_NUM ? "TXQ" : "RXQ",
- ia->tr_hia[queue_num], ia->tr_tia[queue_num],
- ia->cr_hia[queue_num], ia->cr_tia[queue_num]);
- }
- static inline void ipc_print_urbd1(struct hci_dev *hdev, struct urbd1 *urbd1,
- u16 index)
- {
- bt_dev_dbg(hdev, "RXQ:urbd1(%u) frbd_tag:%u status: 0x%x fixed:0x%x",
- index, urbd1->frbd_tag, urbd1->status, urbd1->fixed);
- }
- static struct btintel_pcie_data *btintel_pcie_get_data(struct msix_entry *entry)
- {
- u8 queue = entry->entry;
- struct msix_entry *entries = entry - queue;
- return container_of(entries, struct btintel_pcie_data, msix_entries[0]);
- }
- /* Set the doorbell for TXQ to notify the device that @index (actually index-1)
- * of the TFD is updated and ready to transmit.
- */
- static void btintel_pcie_set_tx_db(struct btintel_pcie_data *data, u16 index)
- {
- u32 val;
- val = index;
- val |= (BTINTEL_PCIE_TX_DB_VEC << 16);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR, val);
- }
- /* Copy the data to next(@tfd_index) data buffer and update the TFD(transfer
- * descriptor) with the data length and the DMA address of the data buffer.
- */
- static void btintel_pcie_prepare_tx(struct txq *txq, u16 tfd_index,
- struct sk_buff *skb)
- {
- struct data_buf *buf;
- struct tfd *tfd;
- tfd = &txq->tfds[tfd_index];
- memset(tfd, 0, sizeof(*tfd));
- buf = &txq->bufs[tfd_index];
- tfd->size = skb->len;
- tfd->addr = buf->data_p_addr;
- /* Copy the outgoing data to DMA buffer */
- memcpy(buf->data, skb->data, tfd->size);
- }
- static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev)
- {
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- u16 cr_hia, cr_tia;
- u32 reg, mbox_reg;
- struct sk_buff *skb;
- u8 buf[80];
- skb = alloc_skb(1024, GFP_ATOMIC);
- if (!skb)
- return;
- snprintf(buf, sizeof(buf), "%s", "---- Dump of debug registers ---");
- bt_dev_dbg(hdev, "%s", buf);
- skb_put_data(skb, buf, strlen(buf));
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
- snprintf(buf, sizeof(buf), "boot stage: 0x%8.8x", reg);
- bt_dev_dbg(hdev, "%s", buf);
- skb_put_data(skb, buf, strlen(buf));
- data->boot_stage_cache = reg;
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG);
- snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_CONTROL_REG);
- snprintf(buf, sizeof(buf), "ipc control: 0x%8.8x", reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG);
- snprintf(buf, sizeof(buf), "ipc sleep control: 0x%8.8x", reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- /*Read the Mail box status and registers*/
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MBOX_STATUS_REG);
- snprintf(buf, sizeof(buf), "mbox status: 0x%8.8x", reg);
- skb_put_data(skb, buf, strlen(buf));
- if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1) {
- mbox_reg = btintel_pcie_rd_reg32(data,
- BTINTEL_PCIE_CSR_MBOX_1_REG);
- snprintf(buf, sizeof(buf), "mbox_1: 0x%8.8x", mbox_reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- }
- if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2) {
- mbox_reg = btintel_pcie_rd_reg32(data,
- BTINTEL_PCIE_CSR_MBOX_2_REG);
- snprintf(buf, sizeof(buf), "mbox_2: 0x%8.8x", mbox_reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- }
- if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3) {
- mbox_reg = btintel_pcie_rd_reg32(data,
- BTINTEL_PCIE_CSR_MBOX_3_REG);
- snprintf(buf, sizeof(buf), "mbox_3: 0x%8.8x", mbox_reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- }
- if (reg & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4) {
- mbox_reg = btintel_pcie_rd_reg32(data,
- BTINTEL_PCIE_CSR_MBOX_4_REG);
- snprintf(buf, sizeof(buf), "mbox_4: 0x%8.8x", mbox_reg);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- }
- cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
- cr_tia = data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
- snprintf(buf, sizeof(buf), "rxq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
- cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
- snprintf(buf, sizeof(buf), "txq: cr_tia: %u cr_hia: %u", cr_tia, cr_hia);
- skb_put_data(skb, buf, strlen(buf));
- bt_dev_dbg(hdev, "%s", buf);
- snprintf(buf, sizeof(buf), "--------------------------------");
- bt_dev_dbg(hdev, "%s", buf);
- hci_recv_diag(hdev, skb);
- }
- static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
- struct sk_buff *skb, u32 pkt_type, u16 opcode)
- {
- int ret;
- u16 tfd_index;
- u32 old_ctxt;
- bool wait_on_alive = false;
- struct hci_dev *hdev = data->hdev;
- struct txq *txq = &data->txq;
- tfd_index = data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM];
- if (tfd_index > txq->count)
- return -ERANGE;
- /* Firmware raises alive interrupt on HCI_OP_RESET or
- * BTINTEL_HCI_OP_RESET
- */
- wait_on_alive = (pkt_type == BTINTEL_PCIE_HCI_CMD_PKT &&
- (opcode == BTINTEL_HCI_OP_RESET || opcode == HCI_OP_RESET));
- if (wait_on_alive) {
- data->gp0_received = false;
- old_ctxt = data->alive_intr_ctxt;
- data->alive_intr_ctxt =
- (opcode == BTINTEL_HCI_OP_RESET ? BTINTEL_PCIE_INTEL_HCI_RESET1 :
- BTINTEL_PCIE_HCI_RESET);
- bt_dev_dbg(data->hdev, "sending cmd: 0x%4.4x alive context changed: %s -> %s",
- opcode, btintel_pcie_alivectxt_state2str(old_ctxt),
- btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
- }
- memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &pkt_type,
- BTINTEL_PCIE_HCI_TYPE_LEN);
- /* Prepare for TX. It updates the TFD with the length of data and
- * address of the DMA buffer, and copy the data to the DMA buffer
- */
- btintel_pcie_prepare_tx(txq, tfd_index, skb);
- tfd_index = (tfd_index + 1) % txq->count;
- data->ia.tr_hia[BTINTEL_PCIE_TXQ_NUM] = tfd_index;
- /* Arm wait event condition */
- data->tx_wait_done = false;
- /* Set the doorbell to notify the device */
- btintel_pcie_set_tx_db(data, tfd_index);
- /* Wait for the complete interrupt - URBD0 */
- ret = wait_event_timeout(data->tx_wait_q, data->tx_wait_done,
- msecs_to_jiffies(BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS));
- if (!ret) {
- bt_dev_err(data->hdev, "Timeout (%u ms) on tx completion",
- BTINTEL_PCIE_TX_WAIT_TIMEOUT_MS);
- btintel_pcie_dump_debug_registers(data->hdev);
- return -ETIME;
- }
- if (wait_on_alive) {
- ret = wait_event_timeout(data->gp0_wait_q,
- data->gp0_received,
- msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
- if (!ret) {
- hdev->stat.err_tx++;
- bt_dev_err(hdev, "Timeout (%u ms) on alive interrupt, alive context: %s",
- BTINTEL_DEFAULT_INTR_TIMEOUT_MS,
- btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
- return -ETIME;
- }
- }
- return 0;
- }
- /* Set the doorbell for RXQ to notify the device that @index (actually index-1)
- * is available to receive the data
- */
- static void btintel_pcie_set_rx_db(struct btintel_pcie_data *data, u16 index)
- {
- u32 val;
- val = index;
- val |= (BTINTEL_PCIE_RX_DB_VEC << 16);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR, val);
- }
- /* Update the FRBD (free buffer descriptor) with the @frbd_index and the
- * DMA address of the free buffer.
- */
- static void btintel_pcie_prepare_rx(struct rxq *rxq, u16 frbd_index)
- {
- struct data_buf *buf;
- struct frbd *frbd;
- /* Get the buffer of the FRBD for DMA */
- buf = &rxq->bufs[frbd_index];
- frbd = &rxq->frbds[frbd_index];
- memset(frbd, 0, sizeof(*frbd));
- /* Update FRBD */
- frbd->tag = frbd_index;
- frbd->addr = buf->data_p_addr;
- }
- static int btintel_pcie_submit_rx(struct btintel_pcie_data *data)
- {
- u16 frbd_index;
- struct rxq *rxq = &data->rxq;
- frbd_index = data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM];
- if (frbd_index > rxq->count)
- return -ERANGE;
- /* Prepare for RX submit. It updates the FRBD with the address of DMA
- * buffer
- */
- btintel_pcie_prepare_rx(rxq, frbd_index);
- frbd_index = (frbd_index + 1) % rxq->count;
- data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM] = frbd_index;
- ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_RXQ_NUM);
- /* Set the doorbell to notify the device */
- btintel_pcie_set_rx_db(data, frbd_index);
- return 0;
- }
- static int btintel_pcie_start_rx(struct btintel_pcie_data *data)
- {
- int i, ret;
- struct rxq *rxq = &data->rxq;
- /* Post (BTINTEL_PCIE_RX_DESCS_COUNT - 3) buffers to overcome the
- * hardware issues leading to race condition at the firmware.
- */
- for (i = 0; i < rxq->count - 3; i++) {
- ret = btintel_pcie_submit_rx(data);
- if (ret)
- return ret;
- }
- return 0;
- }
- static void btintel_pcie_reset_ia(struct btintel_pcie_data *data)
- {
- memset(data->ia.tr_hia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
- memset(data->ia.tr_tia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
- memset(data->ia.cr_hia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
- memset(data->ia.cr_tia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
- }
- static int btintel_pcie_reset_bt(struct btintel_pcie_data *data)
- {
- u32 reg;
- int retry = 3;
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
- BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
- BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON;
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- do {
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS)
- break;
- usleep_range(10000, 12000);
- } while (--retry > 0);
- usleep_range(10000, 12000);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
- BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
- BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET;
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- usleep_range(10000, 12000);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- bt_dev_dbg(data->hdev, "csr register after reset: 0x%8.8x", reg);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
- /* If shared hardware reset is success then boot stage register shall be
- * set to 0
- */
- return reg == 0 ? 0 : -ENODEV;
- }
- static void btintel_pcie_mac_init(struct btintel_pcie_data *data)
- {
- u32 reg;
- /* Set MAC_INIT bit to start primary bootloader */
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
- BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON |
- BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
- reg |= (BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
- BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- }
- static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data)
- {
- u32 reg;
- int retry = 15;
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS;
- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ;
- if ((reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS) == 0)
- reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ;
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- do {
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS)
- return 0;
- /* Need delay here for Target Access harwdware to settle down*/
- usleep_range(1000, 1200);
- } while (--retry > 0);
- return -ETIME;
- }
- static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
- {
- u32 reg;
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ)
- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ;
- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS)
- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS;
- if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ)
- reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ;
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- }
- static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
- void *data, size_t size)
- {
- struct intel_tlv *tlv;
- tlv = dest;
- tlv->type = type;
- tlv->len = size;
- memcpy(tlv->val, data, tlv->len);
- return dest + sizeof(*tlv) + size;
- }
- static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
- {
- u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
- struct btintel_pcie_dbgc *dbgc = &data->dbgc;
- struct hci_dev *hdev = data->hdev;
- u8 *pdata, *p, buf_idx;
- struct intel_tlv *tlv;
- struct timespec64 now;
- struct tm tm_now;
- char fw_build[128];
- char ts[128];
- char vendor[64];
- char driver[64];
- if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
- return -EOPNOTSUPP;
- wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
- offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
- buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
- if (buf_idx > dbgc->count) {
- bt_dev_warn(hdev, "Buffer index is invalid");
- return -EINVAL;
- }
- prev_size = buf_idx * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
- if (prev_size + offset >= prev_size)
- data->dmp_hdr.write_ptr = prev_size + offset;
- else
- return -EINVAL;
- snprintf(vendor, sizeof(vendor), "Vendor: Intel\n");
- snprintf(driver, sizeof(driver), "Driver: %s\n",
- data->dmp_hdr.driver_name);
- ktime_get_real_ts64(&now);
- time64_to_tm(now.tv_sec, 0, &tm_now);
- snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
- tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
- tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
- snprintf(fw_build, sizeof(fw_build),
- "Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
- 2000 + (data->dmp_hdr.fw_timestamp >> 8),
- data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
- data->dmp_hdr.fw_build_num);
- data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.fw_git_sha1) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
- sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
- sizeof(*tlv) + strlen(ts) +
- sizeof(*tlv) + strlen(fw_build) +
- sizeof(*tlv) + strlen(vendor) +
- sizeof(*tlv) + strlen(driver);
- /*
- * sizeof(u32) - signature
- * sizeof(data_len) - to store tlv data size
- * data_len - TLV data
- */
- dump_size = sizeof(u32) + sizeof(data_len) + data_len;
- /* Add debug buffers data length to dump size */
- dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
- pdata = vmalloc(dump_size);
- if (!pdata)
- return -ENOMEM;
- p = pdata;
- *(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
- p += sizeof(u32);
- *(u32 *)p = data_len;
- p += sizeof(u32);
- p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
- p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
- p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
- p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
- strlen(fw_build));
- p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
- sizeof(data->dmp_hdr.cnvi_bt));
- p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
- sizeof(data->dmp_hdr.write_ptr));
- p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
- sizeof(data->dmp_hdr.wrap_ctr));
- data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data,
- BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
- p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
- sizeof(data->dmp_hdr.trigger_reason));
- p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1,
- sizeof(data->dmp_hdr.fw_git_sha1));
- p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
- sizeof(data->dmp_hdr.cnvr_top));
- p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
- sizeof(data->dmp_hdr.cnvi_top));
- memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
- dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
- return 0;
- }
- static void btintel_pcie_dump_traces(struct hci_dev *hdev)
- {
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- int ret = 0;
- ret = btintel_pcie_get_mac_access(data);
- if (ret) {
- bt_dev_err(hdev, "Failed to get mac access: (%d)", ret);
- return;
- }
- ret = btintel_pcie_read_dram_buffers(data);
- btintel_pcie_release_mac_access(data);
- if (ret)
- bt_dev_err(hdev, "Failed to dump traces: (%d)", ret);
- }
- /* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
- * BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with
- * BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0.
- * Then the host reads firmware version from BTINTEL_CSR_F2D_MBX and the boot stage
- * from BTINTEL_PCIE_CSR_BOOT_STAGE_REG.
- */
- static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
- {
- int err;
- u32 reg;
- data->gp0_received = false;
- /* Update the DMA address of CI struct to CSR */
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_LSB_REG,
- data->ci_p_addr & 0xffffffff);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG,
- (u64)data->ci_p_addr >> 32);
- /* Reset the cached value of boot stage. it is updated by the MSI-X
- * gp0 interrupt handler.
- */
- data->boot_stage_cache = 0x0;
- /* Set MAC_INIT bit to start primary bootloader */
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
- BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON |
- BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
- reg |= (BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
- BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
- /* MAC is ready. Enable BT FUNC */
- btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
- BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
- btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
- /* wait for interrupt from the device after booting up to primary
- * bootloader.
- */
- data->alive_intr_ctxt = BTINTEL_PCIE_ROM;
- err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
- msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
- if (!err)
- return -ETIME;
- /* Check cached boot stage is BTINTEL_PCIE_CSR_BOOT_STAGE_ROM(BIT(0)) */
- if (~data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ROM)
- return -ENODEV;
- return 0;
- }
- static inline bool btintel_pcie_in_op(struct btintel_pcie_data *data)
- {
- return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW;
- }
- static inline bool btintel_pcie_in_iml(struct btintel_pcie_data *data)
- {
- return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_IML &&
- !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW);
- }
- static inline bool btintel_pcie_in_d3(struct btintel_pcie_data *data)
- {
- return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY;
- }
- static inline bool btintel_pcie_in_d0(struct btintel_pcie_data *data)
- {
- return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY);
- }
- static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data)
- {
- return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED;
- }
- static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data,
- u32 dxstate)
- {
- bt_dev_dbg(data->hdev, "writing sleep_ctl_reg: 0x%8.8x", dxstate);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_IPC_SLEEP_CTL_REG, dxstate);
- }
- static int btintel_pcie_read_device_mem(struct btintel_pcie_data *data,
- void *buf, u32 dev_addr, int len)
- {
- int err;
- u32 *val = buf;
- /* Get device mac access */
- err = btintel_pcie_get_mac_access(data);
- if (err) {
- bt_dev_err(data->hdev, "Failed to get mac access %d", err);
- return err;
- }
- for (; len > 0; len -= 4, dev_addr += 4, val++)
- *val = btintel_pcie_rd_dev_mem(data, dev_addr);
- btintel_pcie_release_mac_access(data);
- return 0;
- }
- static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data)
- {
- return (data->boot_stage_cache &
- BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN) ||
- (data->boot_stage_cache &
- BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN);
- }
- static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
- {
- return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) ||
- (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER);
- }
- static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
- {
- bt_dev_err(data->hdev, "Received gp1 mailbox interrupt");
- btintel_pcie_dump_debug_registers(data->hdev);
- }
- /* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
- * BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES) which is sent for boot stage and image response.
- */
- static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
- {
- bool submit_rx, signal_waitq;
- u32 reg, old_ctxt;
- /* This interrupt is for three different causes and it is not easy to
- * know what causes the interrupt. So, it compares each register value
- * with cached value and update it before it wake up the queue.
- */
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
- if (reg != data->boot_stage_cache)
- data->boot_stage_cache = reg;
- bt_dev_dbg(data->hdev, "Alive context: %s old_boot_stage: 0x%8.8x new_boot_stage: 0x%8.8x",
- btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt),
- data->boot_stage_cache, reg);
- reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IMG_RESPONSE_REG);
- if (reg != data->img_resp_cache)
- data->img_resp_cache = reg;
- if (btintel_pcie_in_error(data)) {
- bt_dev_err(data->hdev, "Controller in error state");
- btintel_pcie_dump_debug_registers(data->hdev);
- return;
- }
- if (btintel_pcie_in_lockdown(data)) {
- bt_dev_err(data->hdev, "Controller in lockdown state");
- btintel_pcie_dump_debug_registers(data->hdev);
- return;
- }
- data->gp0_received = true;
- old_ctxt = data->alive_intr_ctxt;
- submit_rx = false;
- signal_waitq = false;
- switch (data->alive_intr_ctxt) {
- case BTINTEL_PCIE_ROM:
- data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL;
- signal_waitq = true;
- break;
- case BTINTEL_PCIE_FW_DL:
- /* Error case is already handled. Ideally control shall not
- * reach here
- */
- break;
- case BTINTEL_PCIE_INTEL_HCI_RESET1:
- if (btintel_pcie_in_op(data)) {
- submit_rx = true;
- signal_waitq = true;
- break;
- }
- if (btintel_pcie_in_iml(data)) {
- submit_rx = true;
- signal_waitq = true;
- data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL;
- break;
- }
- break;
- case BTINTEL_PCIE_INTEL_HCI_RESET2:
- if (btintel_test_and_clear_flag(data->hdev, INTEL_WAIT_FOR_D0)) {
- btintel_wake_up_flag(data->hdev, INTEL_WAIT_FOR_D0);
- data->alive_intr_ctxt = BTINTEL_PCIE_D0;
- }
- break;
- case BTINTEL_PCIE_D0:
- if (btintel_pcie_in_d3(data)) {
- data->alive_intr_ctxt = BTINTEL_PCIE_D3;
- signal_waitq = true;
- break;
- }
- break;
- case BTINTEL_PCIE_D3:
- if (btintel_pcie_in_d0(data)) {
- data->alive_intr_ctxt = BTINTEL_PCIE_D0;
- submit_rx = true;
- signal_waitq = true;
- break;
- }
- break;
- case BTINTEL_PCIE_HCI_RESET:
- data->alive_intr_ctxt = BTINTEL_PCIE_D0;
- submit_rx = true;
- signal_waitq = true;
- break;
- default:
- bt_dev_err(data->hdev, "Unknown state: 0x%2.2x",
- data->alive_intr_ctxt);
- break;
- }
- if (submit_rx) {
- btintel_pcie_reset_ia(data);
- btintel_pcie_start_rx(data);
- }
- if (signal_waitq) {
- bt_dev_dbg(data->hdev, "wake up gp0 wait_q");
- wake_up(&data->gp0_wait_q);
- }
- if (old_ctxt != data->alive_intr_ctxt)
- bt_dev_dbg(data->hdev, "alive context changed: %s -> %s",
- btintel_pcie_alivectxt_state2str(old_ctxt),
- btintel_pcie_alivectxt_state2str(data->alive_intr_ctxt));
- }
- /* This function handles the MSX-X interrupt for rx queue 0 which is for TX
- */
- static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
- {
- u16 cr_tia, cr_hia;
- struct txq *txq;
- struct urbd0 *urbd0;
- cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
- cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
- if (cr_tia == cr_hia)
- return;
- txq = &data->txq;
- while (cr_tia != cr_hia) {
- data->tx_wait_done = true;
- wake_up(&data->tx_wait_q);
- urbd0 = &txq->urbd0s[cr_tia];
- if (urbd0->tfd_index > txq->count)
- return;
- cr_tia = (cr_tia + 1) % txq->count;
- data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] = cr_tia;
- ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_TXQ_NUM);
- }
- }
- static int btintel_pcie_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
- {
- struct hci_event_hdr *hdr = (void *)skb->data;
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
- hdr->plen > 0) {
- const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
- unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
- if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
- switch (skb->data[2]) {
- case 0x02:
- /* When switching to the operational firmware
- * the device sends a vendor specific event
- * indicating that the bootup completed.
- */
- btintel_bootup(hdev, ptr, len);
- /* If bootup event is from operational image,
- * driver needs to write sleep control register to
- * move into D0 state
- */
- if (btintel_pcie_in_op(data)) {
- btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
- data->alive_intr_ctxt = BTINTEL_PCIE_INTEL_HCI_RESET2;
- kfree_skb(skb);
- return 0;
- }
- if (btintel_pcie_in_iml(data)) {
- /* In case of IML, there is no concept
- * of D0 transition. Just mimic as if
- * IML moved to D0 by clearing INTEL_WAIT_FOR_D0
- * bit and waking up the task waiting on
- * INTEL_WAIT_FOR_D0. This is required
- * as intel_boot() is common function for
- * both IML and OP image loading.
- */
- if (btintel_test_and_clear_flag(data->hdev,
- INTEL_WAIT_FOR_D0))
- btintel_wake_up_flag(data->hdev,
- INTEL_WAIT_FOR_D0);
- }
- kfree_skb(skb);
- return 0;
- case 0x06:
- /* When the firmware loading completes the
- * device sends out a vendor specific event
- * indicating the result of the firmware
- * loading.
- */
- btintel_secure_send_result(hdev, ptr, len);
- kfree_skb(skb);
- return 0;
- }
- }
- /* This is a debug event that comes from IML and OP image when it
- * starts execution. There is no need pass this event to stack.
- */
- if (skb->data[2] == 0x97) {
- hci_recv_diag(hdev, skb);
- return 0;
- }
- }
- return hci_recv_frame(hdev, skb);
- }
- /* Process the received rx data
- * It check the frame header to identify the data type and create skb
- * and calling HCI API
- */
- static int btintel_pcie_recv_frame(struct btintel_pcie_data *data,
- struct sk_buff *skb)
- {
- int ret;
- u8 pkt_type;
- u16 plen;
- u32 pcie_pkt_type;
- void *pdata;
- struct hci_dev *hdev = data->hdev;
- spin_lock(&data->hci_rx_lock);
- /* The first 4 bytes indicates the Intel PCIe specific packet type */
- pdata = skb_pull_data(skb, BTINTEL_PCIE_HCI_TYPE_LEN);
- if (!pdata) {
- bt_dev_err(hdev, "Corrupted packet received");
- ret = -EILSEQ;
- goto exit_error;
- }
- pcie_pkt_type = get_unaligned_le32(pdata);
- switch (pcie_pkt_type) {
- case BTINTEL_PCIE_HCI_ACL_PKT:
- if (skb->len >= HCI_ACL_HDR_SIZE) {
- plen = HCI_ACL_HDR_SIZE + __le16_to_cpu(hci_acl_hdr(skb)->dlen);
- pkt_type = HCI_ACLDATA_PKT;
- } else {
- bt_dev_err(hdev, "ACL packet is too short");
- ret = -EILSEQ;
- goto exit_error;
- }
- break;
- case BTINTEL_PCIE_HCI_SCO_PKT:
- if (skb->len >= HCI_SCO_HDR_SIZE) {
- plen = HCI_SCO_HDR_SIZE + hci_sco_hdr(skb)->dlen;
- pkt_type = HCI_SCODATA_PKT;
- } else {
- bt_dev_err(hdev, "SCO packet is too short");
- ret = -EILSEQ;
- goto exit_error;
- }
- break;
- case BTINTEL_PCIE_HCI_EVT_PKT:
- if (skb->len >= HCI_EVENT_HDR_SIZE) {
- plen = HCI_EVENT_HDR_SIZE + hci_event_hdr(skb)->plen;
- pkt_type = HCI_EVENT_PKT;
- } else {
- bt_dev_err(hdev, "Event packet is too short");
- ret = -EILSEQ;
- goto exit_error;
- }
- break;
- case BTINTEL_PCIE_HCI_ISO_PKT:
- if (skb->len >= HCI_ISO_HDR_SIZE) {
- plen = HCI_ISO_HDR_SIZE + __le16_to_cpu(hci_iso_hdr(skb)->dlen);
- pkt_type = HCI_ISODATA_PKT;
- } else {
- bt_dev_err(hdev, "ISO packet is too short");
- ret = -EILSEQ;
- goto exit_error;
- }
- break;
- default:
- bt_dev_err(hdev, "Invalid packet type received: 0x%4.4x",
- pcie_pkt_type);
- ret = -EINVAL;
- goto exit_error;
- }
- if (skb->len < plen) {
- bt_dev_err(hdev, "Received corrupted packet. type: 0x%2.2x",
- pkt_type);
- ret = -EILSEQ;
- goto exit_error;
- }
- bt_dev_dbg(hdev, "pkt_type: 0x%2.2x len: %u", pkt_type, plen);
- hci_skb_pkt_type(skb) = pkt_type;
- hdev->stat.byte_rx += plen;
- skb_trim(skb, plen);
- if (pcie_pkt_type == BTINTEL_PCIE_HCI_EVT_PKT)
- ret = btintel_pcie_recv_event(hdev, skb);
- else
- ret = hci_recv_frame(hdev, skb);
- skb = NULL; /* skb is freed in the callee */
- exit_error:
- kfree_skb(skb);
- if (ret)
- hdev->stat.err_rx++;
- spin_unlock(&data->hci_rx_lock);
- return ret;
- }
- static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data)
- {
- int len, err, offset, pending;
- struct sk_buff *skb;
- u8 *buf, prefix[64];
- u32 addr, val;
- u16 pkt_len;
- struct tlv {
- u8 type;
- __le16 len;
- u8 val[];
- } __packed;
- struct tlv *tlv;
- switch (data->dmp_hdr.cnvi_top & 0xfff) {
- case BTINTEL_CNVI_BLAZARI:
- case BTINTEL_CNVI_BLAZARIW:
- /* only from step B0 onwards */
- if (INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top) != 0x01)
- return;
- len = BTINTEL_PCIE_BLZR_HWEXP_SIZE; /* exception data length */
- addr = BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR;
- break;
- case BTINTEL_CNVI_SCP:
- len = BTINTEL_PCIE_SCP_HWEXP_SIZE;
- addr = BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR;
- break;
- default:
- bt_dev_err(data->hdev, "Unsupported cnvi 0x%8.8x", data->dmp_hdr.cnvi_top);
- return;
- }
- buf = kzalloc(len, GFP_KERNEL);
- if (!buf)
- goto exit_on_error;
- btintel_pcie_mac_init(data);
- err = btintel_pcie_read_device_mem(data, buf, addr, len);
- if (err)
- goto exit_on_error;
- val = get_unaligned_le32(buf);
- if (val != BTINTEL_PCIE_MAGIC_NUM) {
- bt_dev_err(data->hdev, "Invalid exception dump signature: 0x%8.8x",
- val);
- goto exit_on_error;
- }
- snprintf(prefix, sizeof(prefix), "Bluetooth: %s: ", bt_dev_name(data->hdev));
- offset = 4;
- do {
- pending = len - offset;
- if (pending < sizeof(*tlv))
- break;
- tlv = (struct tlv *)(buf + offset);
- /* If type == 0, then there are no more TLVs to be parsed */
- if (!tlv->type) {
- bt_dev_dbg(data->hdev, "Invalid TLV type 0");
- break;
- }
- pkt_len = le16_to_cpu(tlv->len);
- offset += sizeof(*tlv);
- pending = len - offset;
- if (pkt_len > pending)
- break;
- offset += pkt_len;
- /* Only TLVs of type == 1 are HCI events, no need to process other
- * TLVs
- */
- if (tlv->type != 1)
- continue;
- bt_dev_dbg(data->hdev, "TLV packet length: %u", pkt_len);
- if (pkt_len > HCI_MAX_EVENT_SIZE)
- break;
- skb = bt_skb_alloc(pkt_len, GFP_KERNEL);
- if (!skb)
- goto exit_on_error;
- hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
- skb_put_data(skb, tlv->val, pkt_len);
- /* copy Intel specific pcie packet type */
- val = BTINTEL_PCIE_HCI_EVT_PKT;
- memcpy(skb_push(skb, BTINTEL_PCIE_HCI_TYPE_LEN), &val,
- BTINTEL_PCIE_HCI_TYPE_LEN);
- print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_OFFSET, 16, 1,
- tlv->val, pkt_len, false);
- btintel_pcie_recv_frame(data, skb);
- } while (offset < len);
- exit_on_error:
- kfree(buf);
- }
- static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
- {
- bt_dev_err(data->hdev, "Received hw exception interrupt");
- if (test_and_set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
- return;
- if (test_and_set_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags))
- return;
- /* Trigger device core dump when there is HW exception */
- if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
- data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
- queue_work(data->workqueue, &data->rx_work);
- }
- static void btintel_pcie_rx_work(struct work_struct *work)
- {
- struct btintel_pcie_data *data = container_of(work,
- struct btintel_pcie_data, rx_work);
- struct sk_buff *skb;
- if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
- btintel_pcie_dump_traces(data->hdev);
- clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
- }
- if (test_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags)) {
- /* Unlike usb products, controller will not send hardware
- * exception event on exception. Instead controller writes the
- * hardware event to device memory along with optional debug
- * events, raises MSIX and halts. Driver shall read the
- * exception event from device memory and passes it stack for
- * further processing.
- */
- btintel_pcie_read_hwexp(data);
- clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
- }
- /* Process the sk_buf in queue and send to the HCI layer */
- while ((skb = skb_dequeue(&data->rx_skb_q))) {
- btintel_pcie_recv_frame(data, skb);
- }
- }
- /* create sk_buff with data and save it to queue and start RX work */
- static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status,
- void *buf)
- {
- int ret, len;
- struct rfh_hdr *rfh_hdr;
- struct sk_buff *skb;
- rfh_hdr = buf;
- len = rfh_hdr->packet_len;
- if (len <= 0) {
- ret = -EINVAL;
- goto resubmit;
- }
- /* Remove RFH header */
- buf += sizeof(*rfh_hdr);
- skb = alloc_skb(len, GFP_ATOMIC);
- if (!skb)
- goto resubmit;
- skb_put_data(skb, buf, len);
- skb_queue_tail(&data->rx_skb_q, skb);
- queue_work(data->workqueue, &data->rx_work);
- resubmit:
- ret = btintel_pcie_submit_rx(data);
- return ret;
- }
- /* Handles the MSI-X interrupt for rx queue 1 which is for RX */
- static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data)
- {
- u16 cr_hia, cr_tia;
- struct rxq *rxq;
- struct urbd1 *urbd1;
- struct data_buf *buf;
- int ret;
- struct hci_dev *hdev = data->hdev;
- cr_hia = data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM];
- cr_tia = data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
- bt_dev_dbg(hdev, "RXQ: cr_hia: %u cr_tia: %u", cr_hia, cr_tia);
- /* Check CR_TIA and CR_HIA for change */
- if (cr_tia == cr_hia)
- return;
- rxq = &data->rxq;
- /* The firmware sends multiple CD in a single MSI-X and it needs to
- * process all received CDs in this interrupt.
- */
- while (cr_tia != cr_hia) {
- urbd1 = &rxq->urbd1s[cr_tia];
- ipc_print_urbd1(data->hdev, urbd1, cr_tia);
- buf = &rxq->bufs[urbd1->frbd_tag];
- if (!buf) {
- bt_dev_err(hdev, "RXQ: failed to get the DMA buffer for %d",
- urbd1->frbd_tag);
- return;
- }
- ret = btintel_pcie_submit_rx_work(data, urbd1->status,
- buf->data);
- if (ret) {
- bt_dev_err(hdev, "RXQ: failed to submit rx request");
- return;
- }
- cr_tia = (cr_tia + 1) % rxq->count;
- data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM] = cr_tia;
- ipc_print_ia_ring(data->hdev, &data->ia, BTINTEL_PCIE_RXQ_NUM);
- }
- }
- static inline bool btintel_pcie_is_rxq_empty(struct btintel_pcie_data *data)
- {
- return data->ia.cr_hia[BTINTEL_PCIE_RXQ_NUM] == data->ia.cr_tia[BTINTEL_PCIE_RXQ_NUM];
- }
- static inline bool btintel_pcie_is_txackq_empty(struct btintel_pcie_data *data)
- {
- return data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] == data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
- }
- static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
- {
- struct msix_entry *entry = dev_id;
- struct btintel_pcie_data *data = btintel_pcie_get_data(entry);
- u32 intr_fh, intr_hw;
- spin_lock(&data->irq_lock);
- intr_fh = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES);
- intr_hw = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES);
- /* Clear causes registers to avoid being handling the same cause */
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_CAUSES, intr_fh);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, intr_hw);
- spin_unlock(&data->irq_lock);
- if (unlikely(!(intr_fh | intr_hw))) {
- /* Ignore interrupt, inta == 0 */
- return IRQ_NONE;
- }
- /* This interrupt is raised when there is an hardware exception */
- if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP)
- btintel_pcie_msix_hw_exp_handler(data);
- if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
- btintel_pcie_msix_gp1_handler(data);
- /* For TX */
- if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0) {
- btintel_pcie_msix_tx_handle(data);
- if (!btintel_pcie_is_rxq_empty(data))
- btintel_pcie_msix_rx_handle(data);
- }
- /* For RX */
- if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1) {
- btintel_pcie_msix_rx_handle(data);
- if (!btintel_pcie_is_txackq_empty(data))
- btintel_pcie_msix_tx_handle(data);
- }
- /* This interrupt is triggered by the firmware after updating
- * boot_stage register and image_response register
- */
- if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0)
- btintel_pcie_msix_gp0_handler(data);
- /*
- * Before sending the interrupt the HW disables it to prevent a nested
- * interrupt. This is done by writing 1 to the corresponding bit in
- * the mask register. After handling the interrupt, it should be
- * re-enabled by clearing this bit. This register is defined as write 1
- * clear (W1C) register, meaning that it's cleared by writing 1
- * to the bit.
- */
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
- BIT(entry->entry));
- return IRQ_HANDLED;
- }
- /* This function requests the irq for MSI-X and registers the handlers per irq.
- * Currently, it requests only 1 irq for all interrupt causes.
- */
- static int btintel_pcie_setup_irq(struct btintel_pcie_data *data)
- {
- int err;
- int num_irqs, i;
- for (i = 0; i < BTINTEL_PCIE_MSIX_VEC_MAX; i++)
- data->msix_entries[i].entry = i;
- num_irqs = pci_alloc_irq_vectors(data->pdev, BTINTEL_PCIE_MSIX_VEC_MIN,
- BTINTEL_PCIE_MSIX_VEC_MAX, PCI_IRQ_MSIX);
- if (num_irqs < 0)
- return num_irqs;
- data->alloc_vecs = num_irqs;
- data->msix_enabled = 1;
- data->def_irq = 0;
- /* setup irq handler */
- for (i = 0; i < data->alloc_vecs; i++) {
- struct msix_entry *msix_entry;
- msix_entry = &data->msix_entries[i];
- msix_entry->vector = pci_irq_vector(data->pdev, i);
- err = devm_request_threaded_irq(&data->pdev->dev,
- msix_entry->vector,
- NULL,
- btintel_pcie_irq_msix_handler,
- IRQF_ONESHOT | IRQF_SHARED,
- KBUILD_MODNAME,
- msix_entry);
- if (err) {
- pci_free_irq_vectors(data->pdev);
- data->alloc_vecs = 0;
- return err;
- }
- }
- return 0;
- }
- struct btintel_pcie_causes_list {
- u32 cause;
- u32 mask_reg;
- u8 cause_num;
- };
- static struct btintel_pcie_causes_list causes_list[] = {
- { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x00 },
- { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
- { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
- { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
- };
- /* This function configures the interrupt masks for both HW_INT_CAUSES and
- * FH_INT_CAUSES which are meaningful to us.
- *
- * After resetting BT function via PCIE FLR or FUNC_CTRL reset, the driver
- * need to call this function again to configure since the masks
- * are reset to 0xFFFFFFFF after reset.
- */
- static void btintel_pcie_config_msix(struct btintel_pcie_data *data)
- {
- int i;
- int val = data->def_irq | BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE;
- /* Set Non Auto Clear Cause */
- for (i = 0; i < ARRAY_SIZE(causes_list); i++) {
- btintel_pcie_wr_reg8(data,
- BTINTEL_PCIE_CSR_MSIX_IVAR(causes_list[i].cause_num),
- val);
- btintel_pcie_clr_reg_bits(data,
- causes_list[i].mask_reg,
- causes_list[i].cause);
- }
- /* Save the initial interrupt mask */
- data->fh_init_mask = ~btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK);
- data->hw_init_mask = ~btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK);
- }
- static int btintel_pcie_config_pcie(struct pci_dev *pdev,
- struct btintel_pcie_data *data)
- {
- int err;
- err = pcim_enable_device(pdev);
- if (err)
- return err;
- pci_set_master(pdev);
- err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
- if (err) {
- err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
- if (err)
- return err;
- }
- data->base_addr = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
- if (IS_ERR(data->base_addr))
- return PTR_ERR(data->base_addr);
- err = btintel_pcie_setup_irq(data);
- if (err)
- return err;
- /* Configure MSI-X with causes list */
- btintel_pcie_config_msix(data);
- return 0;
- }
- static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
- struct ctx_info *ci)
- {
- ci->version = 0x1;
- ci->size = sizeof(*ci);
- ci->config = 0x0000;
- ci->addr_cr_hia = data->ia.cr_hia_p_addr;
- ci->addr_tr_tia = data->ia.tr_tia_p_addr;
- ci->addr_cr_tia = data->ia.cr_tia_p_addr;
- ci->addr_tr_hia = data->ia.tr_hia_p_addr;
- ci->num_cr_ia = BTINTEL_PCIE_NUM_QUEUES;
- ci->num_tr_ia = BTINTEL_PCIE_NUM_QUEUES;
- ci->addr_urbdq0 = data->txq.urbd0s_p_addr;
- ci->addr_tfdq = data->txq.tfds_p_addr;
- ci->num_tfdq = data->txq.count;
- ci->num_urbdq0 = data->txq.count;
- ci->tfdq_db_vec = BTINTEL_PCIE_TXQ_NUM;
- ci->urbdq0_db_vec = BTINTEL_PCIE_TXQ_NUM;
- ci->rbd_size = BTINTEL_PCIE_RBD_SIZE_4K;
- ci->addr_frbdq = data->rxq.frbds_p_addr;
- ci->num_frbdq = data->rxq.count;
- ci->frbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
- ci->addr_urbdq1 = data->rxq.urbd1s_p_addr;
- ci->num_urbdq1 = data->rxq.count;
- ci->urbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
- ci->dbg_output_mode = 0x01;
- ci->dbgc_addr = data->dbgc.frag_p_addr;
- ci->dbgc_size = data->dbgc.frag_size;
- ci->dbg_preset = 0x00;
- }
- static void btintel_pcie_free_txq_bufs(struct btintel_pcie_data *data,
- struct txq *txq)
- {
- /* Free data buffers first */
- dma_free_coherent(&data->pdev->dev, txq->count * BTINTEL_PCIE_BUFFER_SIZE,
- txq->buf_v_addr, txq->buf_p_addr);
- kfree(txq->bufs);
- }
- static int btintel_pcie_setup_txq_bufs(struct btintel_pcie_data *data,
- struct txq *txq)
- {
- int i;
- struct data_buf *buf;
- /* Allocate the same number of buffers as the descriptor */
- txq->bufs = kmalloc_objs(*buf, txq->count);
- if (!txq->bufs)
- return -ENOMEM;
- /* Allocate full chunk of data buffer for DMA first and do indexing and
- * initialization next, so it can be freed easily
- */
- txq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
- txq->count * BTINTEL_PCIE_BUFFER_SIZE,
- &txq->buf_p_addr,
- GFP_KERNEL | __GFP_NOWARN);
- if (!txq->buf_v_addr) {
- kfree(txq->bufs);
- return -ENOMEM;
- }
- /* Setup the allocated DMA buffer to bufs. Each data_buf should
- * have virtual address and physical address
- */
- for (i = 0; i < txq->count; i++) {
- buf = &txq->bufs[i];
- buf->data_p_addr = txq->buf_p_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
- buf->data = txq->buf_v_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
- }
- return 0;
- }
- static void btintel_pcie_free_rxq_bufs(struct btintel_pcie_data *data,
- struct rxq *rxq)
- {
- /* Free data buffers first */
- dma_free_coherent(&data->pdev->dev, rxq->count * BTINTEL_PCIE_BUFFER_SIZE,
- rxq->buf_v_addr, rxq->buf_p_addr);
- kfree(rxq->bufs);
- }
- static int btintel_pcie_setup_rxq_bufs(struct btintel_pcie_data *data,
- struct rxq *rxq)
- {
- int i;
- struct data_buf *buf;
- /* Allocate the same number of buffers as the descriptor */
- rxq->bufs = kmalloc_objs(*buf, rxq->count);
- if (!rxq->bufs)
- return -ENOMEM;
- /* Allocate full chunk of data buffer for DMA first and do indexing and
- * initialization next, so it can be freed easily
- */
- rxq->buf_v_addr = dma_alloc_coherent(&data->pdev->dev,
- rxq->count * BTINTEL_PCIE_BUFFER_SIZE,
- &rxq->buf_p_addr,
- GFP_KERNEL | __GFP_NOWARN);
- if (!rxq->buf_v_addr) {
- kfree(rxq->bufs);
- return -ENOMEM;
- }
- /* Setup the allocated DMA buffer to bufs. Each data_buf should
- * have virtual address and physical address
- */
- for (i = 0; i < rxq->count; i++) {
- buf = &rxq->bufs[i];
- buf->data_p_addr = rxq->buf_p_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
- buf->data = rxq->buf_v_addr + (i * BTINTEL_PCIE_BUFFER_SIZE);
- }
- return 0;
- }
- static void btintel_pcie_setup_ia(struct btintel_pcie_data *data,
- dma_addr_t p_addr, void *v_addr,
- struct ia *ia)
- {
- /* TR Head Index Array */
- ia->tr_hia_p_addr = p_addr;
- ia->tr_hia = v_addr;
- /* TR Tail Index Array */
- ia->tr_tia_p_addr = p_addr + sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES;
- ia->tr_tia = v_addr + sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES;
- /* CR Head index Array */
- ia->cr_hia_p_addr = p_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 2);
- ia->cr_hia = v_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 2);
- /* CR Tail Index Array */
- ia->cr_tia_p_addr = p_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 3);
- ia->cr_tia = v_addr + (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 3);
- }
- static void btintel_pcie_free(struct btintel_pcie_data *data)
- {
- btintel_pcie_free_rxq_bufs(data, &data->rxq);
- btintel_pcie_free_txq_bufs(data, &data->txq);
- dma_pool_free(data->dma_pool, data->dma_v_addr, data->dma_p_addr);
- dma_pool_destroy(data->dma_pool);
- }
- /* Allocate tx and rx queues, any related data structures and buffers.
- */
- static int btintel_pcie_alloc(struct btintel_pcie_data *data)
- {
- int err = 0;
- size_t total;
- dma_addr_t p_addr;
- void *v_addr;
- /* Allocate the chunk of DMA memory for descriptors, index array, and
- * context information, instead of allocating individually.
- * The DMA memory for data buffer is allocated while setting up the
- * each queue.
- *
- * Total size is sum of the following
- * + size of TFD * Number of descriptors in queue
- * + size of URBD0 * Number of descriptors in queue
- * + size of FRBD * Number of descriptors in queue
- * + size of URBD1 * Number of descriptors in queue
- * + size of index * Number of queues(2) * type of index array(4)
- * + size of context information
- */
- total = (sizeof(struct tfd) + sizeof(struct urbd0)) * BTINTEL_PCIE_TX_DESCS_COUNT;
- total += (sizeof(struct frbd) + sizeof(struct urbd1)) * BTINTEL_PCIE_RX_DESCS_COUNT;
- /* Add the sum of size of index array and size of ci struct */
- total += (sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4) + sizeof(struct ctx_info);
- /* Allocate DMA Pool */
- data->dma_pool = dma_pool_create(KBUILD_MODNAME, &data->pdev->dev,
- total, BTINTEL_PCIE_DMA_POOL_ALIGNMENT, 0);
- if (!data->dma_pool) {
- err = -ENOMEM;
- goto exit_error;
- }
- v_addr = dma_pool_zalloc(data->dma_pool, GFP_KERNEL | __GFP_NOWARN,
- &p_addr);
- if (!v_addr) {
- dma_pool_destroy(data->dma_pool);
- err = -ENOMEM;
- goto exit_error;
- }
- data->dma_p_addr = p_addr;
- data->dma_v_addr = v_addr;
- /* Setup descriptor count */
- data->txq.count = BTINTEL_PCIE_TX_DESCS_COUNT;
- data->rxq.count = BTINTEL_PCIE_RX_DESCS_COUNT;
- /* Setup tfds */
- data->txq.tfds_p_addr = p_addr;
- data->txq.tfds = v_addr;
- p_addr += (sizeof(struct tfd) * BTINTEL_PCIE_TX_DESCS_COUNT);
- v_addr += (sizeof(struct tfd) * BTINTEL_PCIE_TX_DESCS_COUNT);
- /* Setup urbd0 */
- data->txq.urbd0s_p_addr = p_addr;
- data->txq.urbd0s = v_addr;
- p_addr += (sizeof(struct urbd0) * BTINTEL_PCIE_TX_DESCS_COUNT);
- v_addr += (sizeof(struct urbd0) * BTINTEL_PCIE_TX_DESCS_COUNT);
- /* Setup FRBD*/
- data->rxq.frbds_p_addr = p_addr;
- data->rxq.frbds = v_addr;
- p_addr += (sizeof(struct frbd) * BTINTEL_PCIE_RX_DESCS_COUNT);
- v_addr += (sizeof(struct frbd) * BTINTEL_PCIE_RX_DESCS_COUNT);
- /* Setup urbd1 */
- data->rxq.urbd1s_p_addr = p_addr;
- data->rxq.urbd1s = v_addr;
- p_addr += (sizeof(struct urbd1) * BTINTEL_PCIE_RX_DESCS_COUNT);
- v_addr += (sizeof(struct urbd1) * BTINTEL_PCIE_RX_DESCS_COUNT);
- /* Setup data buffers for txq */
- err = btintel_pcie_setup_txq_bufs(data, &data->txq);
- if (err)
- goto exit_error_pool;
- /* Setup data buffers for rxq */
- err = btintel_pcie_setup_rxq_bufs(data, &data->rxq);
- if (err)
- goto exit_error_txq;
- /* Setup Index Array */
- btintel_pcie_setup_ia(data, p_addr, v_addr, &data->ia);
- /* Setup data buffers for dbgc */
- err = btintel_pcie_setup_dbgc(data);
- if (err)
- goto exit_error_txq;
- /* Setup Context Information */
- p_addr += sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4;
- v_addr += sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES * 4;
- data->ci = v_addr;
- data->ci_p_addr = p_addr;
- /* Initialize the CI */
- btintel_pcie_init_ci(data, data->ci);
- return 0;
- exit_error_txq:
- btintel_pcie_free_txq_bufs(data, &data->txq);
- exit_error_pool:
- dma_pool_free(data->dma_pool, data->dma_v_addr, data->dma_p_addr);
- dma_pool_destroy(data->dma_pool);
- exit_error:
- return err;
- }
- static int btintel_pcie_open(struct hci_dev *hdev)
- {
- bt_dev_dbg(hdev, "");
- return 0;
- }
- static int btintel_pcie_close(struct hci_dev *hdev)
- {
- bt_dev_dbg(hdev, "");
- return 0;
- }
- static int btintel_pcie_inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
- {
- struct sk_buff *skb;
- struct hci_event_hdr *hdr;
- struct hci_ev_cmd_complete *evt;
- skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
- hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr));
- hdr->evt = HCI_EV_CMD_COMPLETE;
- hdr->plen = sizeof(*evt) + 1;
- evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt));
- evt->ncmd = 0x01;
- evt->opcode = cpu_to_le16(opcode);
- *(u8 *)skb_put(skb, 1) = 0x00;
- hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
- return hci_recv_frame(hdev, skb);
- }
- static int btintel_pcie_send_frame(struct hci_dev *hdev,
- struct sk_buff *skb)
- {
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- struct hci_command_hdr *cmd;
- __u16 opcode = ~0;
- int ret;
- u32 type;
- if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags))
- return -ENODEV;
- /* Due to the fw limitation, the type header of the packet should be
- * 4 bytes unlike 1 byte for UART. In UART, the firmware can read
- * the first byte to get the packet type and redirect the rest of data
- * packet to the right handler.
- *
- * But for PCIe, THF(Transfer Flow Handler) fetches the 4 bytes of data
- * from DMA memory and by the time it reads the first 4 bytes, it has
- * already consumed some part of packet. Thus the packet type indicator
- * for iBT PCIe is 4 bytes.
- *
- * Luckily, when HCI core creates the skb, it allocates 8 bytes of
- * head room for profile and driver use, and before sending the data
- * to the device, append the iBT PCIe packet type in the front.
- */
- switch (hci_skb_pkt_type(skb)) {
- case HCI_COMMAND_PKT:
- type = BTINTEL_PCIE_HCI_CMD_PKT;
- cmd = (void *)skb->data;
- opcode = le16_to_cpu(cmd->opcode);
- if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
- struct hci_command_hdr *cmd = (void *)skb->data;
- __u16 opcode = le16_to_cpu(cmd->opcode);
- /* When the BTINTEL_HCI_OP_RESET command is issued to
- * boot into the operational firmware, it will actually
- * not send a command complete event. To keep the flow
- * control working inject that event here.
- */
- if (opcode == BTINTEL_HCI_OP_RESET)
- btintel_pcie_inject_cmd_complete(hdev, opcode);
- }
- hdev->stat.cmd_tx++;
- break;
- case HCI_ACLDATA_PKT:
- type = BTINTEL_PCIE_HCI_ACL_PKT;
- hdev->stat.acl_tx++;
- break;
- case HCI_SCODATA_PKT:
- type = BTINTEL_PCIE_HCI_SCO_PKT;
- hdev->stat.sco_tx++;
- break;
- case HCI_ISODATA_PKT:
- type = BTINTEL_PCIE_HCI_ISO_PKT;
- break;
- default:
- bt_dev_err(hdev, "Unknown HCI packet type");
- return -EILSEQ;
- }
- ret = btintel_pcie_send_sync(data, skb, type, opcode);
- if (ret) {
- hdev->stat.err_tx++;
- bt_dev_err(hdev, "Failed to send frame (%d)", ret);
- goto exit_error;
- }
- hdev->stat.byte_tx += skb->len;
- kfree_skb(skb);
- exit_error:
- return ret;
- }
- static void btintel_pcie_release_hdev(struct btintel_pcie_data *data)
- {
- struct hci_dev *hdev;
- hdev = data->hdev;
- hci_unregister_dev(hdev);
- hci_free_dev(hdev);
- data->hdev = NULL;
- }
- static void btintel_pcie_disable_interrupts(struct btintel_pcie_data *data)
- {
- spin_lock(&data->irq_lock);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, data->fh_init_mask);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, data->hw_init_mask);
- spin_unlock(&data->irq_lock);
- }
- static void btintel_pcie_enable_interrupts(struct btintel_pcie_data *data)
- {
- spin_lock(&data->irq_lock);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, ~data->fh_init_mask);
- btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, ~data->hw_init_mask);
- spin_unlock(&data->irq_lock);
- }
- static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data)
- {
- for (int i = 0; i < data->alloc_vecs; i++)
- synchronize_irq(data->msix_entries[i].vector);
- }
- static int btintel_pcie_setup_internal(struct hci_dev *hdev)
- {
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- const u8 param[1] = { 0xFF };
- struct intel_version_tlv ver_tlv;
- struct sk_buff *skb;
- int err;
- BT_DBG("%s", hdev->name);
- skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
- if (IS_ERR(skb)) {
- bt_dev_err(hdev, "Reading Intel version command failed (%ld)",
- PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- /* Check the status */
- if (skb->data[0]) {
- bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
- skb->data[0]);
- err = -EIO;
- goto exit_error;
- }
- /* Apply the common HCI quirks for Intel device */
- hci_set_quirk(hdev, HCI_QUIRK_STRICT_DUPLICATE_FILTER);
- hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
- hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_DIAG);
- /* Set up the quality report callback for Intel devices */
- hdev->set_quality_report = btintel_set_quality_report;
- memset(&ver_tlv, 0, sizeof(ver_tlv));
- /* For TLV type device, parse the tlv data */
- err = btintel_parse_version_tlv(hdev, &ver_tlv, skb);
- if (err) {
- bt_dev_err(hdev, "Failed to parse TLV version information");
- goto exit_error;
- }
- switch (INTEL_HW_PLATFORM(ver_tlv.cnvi_bt)) {
- case 0x37:
- break;
- default:
- bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
- INTEL_HW_PLATFORM(ver_tlv.cnvi_bt));
- err = -EINVAL;
- goto exit_error;
- }
- /* Check for supported iBT hardware variants of this firmware
- * loading method.
- *
- * This check has been put in place to ensure correct forward
- * compatibility options when newer hardware variants come
- * along.
- */
- switch (INTEL_HW_VARIANT(ver_tlv.cnvi_bt)) {
- case 0x1e: /* BzrI */
- case 0x1f: /* ScP */
- case 0x22: /* BzrIW */
- /* Display version information of TLV type */
- btintel_version_info_tlv(hdev, &ver_tlv);
- /* Apply the device specific HCI quirks for TLV based devices
- *
- * All TLV based devices support WBS
- */
- hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
- /* Setup MSFT Extension support */
- btintel_set_msft_opcode(hdev,
- INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
- err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
- if (err)
- goto exit_error;
- break;
- default:
- bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
- INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
- err = -EINVAL;
- goto exit_error;
- break;
- }
- data->dmp_hdr.cnvi_top = ver_tlv.cnvi_top;
- data->dmp_hdr.cnvr_top = ver_tlv.cnvr_top;
- data->dmp_hdr.fw_timestamp = ver_tlv.timestamp;
- data->dmp_hdr.fw_build_type = ver_tlv.build_type;
- data->dmp_hdr.fw_build_num = ver_tlv.build_num;
- data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
- if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
- data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
- btintel_print_fseq_info(hdev);
- exit_error:
- kfree_skb(skb);
- return err;
- }
- static int btintel_pcie_setup(struct hci_dev *hdev)
- {
- int err, fw_dl_retry = 0;
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- while ((err = btintel_pcie_setup_internal(hdev)) && fw_dl_retry++ < 1) {
- bt_dev_err(hdev, "Firmware download retry count: %d",
- fw_dl_retry);
- btintel_pcie_dump_debug_registers(hdev);
- btintel_pcie_disable_interrupts(data);
- btintel_pcie_synchronize_irqs(data);
- err = btintel_pcie_reset_bt(data);
- if (err) {
- bt_dev_err(hdev, "Failed to do shr reset: %d", err);
- break;
- }
- usleep_range(10000, 12000);
- btintel_pcie_reset_ia(data);
- btintel_pcie_enable_interrupts(data);
- btintel_pcie_config_msix(data);
- err = btintel_pcie_enable_bt(data);
- if (err) {
- bt_dev_err(hdev, "Failed to enable hardware: %d", err);
- break;
- }
- btintel_pcie_start_rx(data);
- }
- if (!err)
- set_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags);
- return err;
- }
- static struct btintel_pcie_dev_recovery *
- btintel_pcie_get_recovery(struct pci_dev *pdev, struct device *dev)
- {
- struct btintel_pcie_dev_recovery *tmp, *data = NULL;
- const char *name = pci_name(pdev);
- const size_t name_len = strlen(name) + 1;
- struct hci_dev *hdev = to_hci_dev(dev);
- spin_lock(&btintel_pcie_recovery_lock);
- list_for_each_entry(tmp, &btintel_pcie_recovery_list, list) {
- if (strcmp(tmp->name, name))
- continue;
- data = tmp;
- break;
- }
- spin_unlock(&btintel_pcie_recovery_lock);
- if (data) {
- bt_dev_dbg(hdev, "Found restart data for BDF: %s", data->name);
- return data;
- }
- data = kzalloc_flex(*data, name, name_len, GFP_ATOMIC);
- if (!data)
- return NULL;
- strscpy(data->name, name, name_len);
- spin_lock(&btintel_pcie_recovery_lock);
- list_add_tail(&data->list, &btintel_pcie_recovery_list);
- spin_unlock(&btintel_pcie_recovery_lock);
- return data;
- }
- static void btintel_pcie_free_restart_list(void)
- {
- struct btintel_pcie_dev_recovery *tmp;
- while ((tmp = list_first_entry_or_null(&btintel_pcie_recovery_list,
- typeof(*tmp), list))) {
- list_del(&tmp->list);
- kfree(tmp);
- }
- }
- static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
- struct device *dev)
- {
- struct btintel_pcie_dev_recovery *data;
- time64_t retry_window;
- data = btintel_pcie_get_recovery(pdev, dev);
- if (!data)
- return;
- retry_window = ktime_get_boottime_seconds() - data->last_error;
- if (data->count == 0) {
- data->last_error = ktime_get_boottime_seconds();
- data->count++;
- } else if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
- data->count <= BTINTEL_PCIE_FLR_MAX_RETRY) {
- data->count++;
- } else if (retry_window > BTINTEL_PCIE_RESET_WINDOW_SECS) {
- data->last_error = 0;
- data->count = 0;
- }
- }
- static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
- static void btintel_pcie_removal_work(struct work_struct *wk)
- {
- struct btintel_pcie_removal *removal =
- container_of(wk, struct btintel_pcie_removal, work);
- struct pci_dev *pdev = removal->pdev;
- struct btintel_pcie_data *data;
- int err;
- pci_lock_rescan_remove();
- if (!pdev->bus)
- goto error;
- data = pci_get_drvdata(pdev);
- btintel_pcie_disable_interrupts(data);
- btintel_pcie_synchronize_irqs(data);
- flush_work(&data->rx_work);
- bt_dev_dbg(data->hdev, "Release bluetooth interface");
- btintel_pcie_release_hdev(data);
- err = pci_reset_function(pdev);
- if (err) {
- BT_ERR("Failed resetting the pcie device (%d)", err);
- goto error;
- }
- btintel_pcie_enable_interrupts(data);
- btintel_pcie_config_msix(data);
- err = btintel_pcie_enable_bt(data);
- if (err) {
- BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
- err);
- goto error;
- }
- btintel_pcie_reset_ia(data);
- btintel_pcie_start_rx(data);
- data->flags = 0;
- err = btintel_pcie_setup_hdev(data);
- if (err) {
- BT_ERR("Failed registering hdev (%d)", err);
- goto error;
- }
- error:
- pci_dev_put(pdev);
- pci_unlock_rescan_remove();
- kfree(removal);
- }
- static void btintel_pcie_reset(struct hci_dev *hdev)
- {
- struct btintel_pcie_removal *removal;
- struct btintel_pcie_data *data;
- data = hci_get_drvdata(hdev);
- if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags))
- return;
- if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
- return;
- removal = kzalloc_obj(*removal, GFP_ATOMIC);
- if (!removal)
- return;
- removal->pdev = data->pdev;
- INIT_WORK(&removal->work, btintel_pcie_removal_work);
- pci_dev_get(removal->pdev);
- schedule_work(&removal->work);
- }
- static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
- {
- struct btintel_pcie_dev_recovery *data;
- struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev);
- struct pci_dev *pdev = dev_data->pdev;
- time64_t retry_window;
- if (code == 0x13) {
- bt_dev_err(hdev, "Encountered top exception");
- return;
- }
- data = btintel_pcie_get_recovery(pdev, &hdev->dev);
- if (!data)
- return;
- retry_window = ktime_get_boottime_seconds() - data->last_error;
- if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
- data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) {
- bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d",
- BTINTEL_PCIE_FLR_MAX_RETRY, data->count);
- bt_dev_dbg(hdev, "Boot time: %lld seconds",
- ktime_get_boottime_seconds());
- bt_dev_dbg(hdev, "last error at: %lld seconds",
- data->last_error);
- return;
- }
- btintel_pcie_inc_recovery_count(pdev, &hdev->dev);
- btintel_pcie_reset(hdev);
- }
- static bool btintel_pcie_wakeup(struct hci_dev *hdev)
- {
- struct btintel_pcie_data *data = hci_get_drvdata(hdev);
- return device_may_wakeup(&data->pdev->dev);
- }
- static const struct {
- u16 opcode;
- const char *desc;
- } btintel_pcie_hci_drv_supported_commands[] = {
- /* Common commands */
- { HCI_DRV_OP_READ_INFO, "Read Info" },
- };
- static int btintel_pcie_hci_drv_read_info(struct hci_dev *hdev, void *data,
- u16 data_len)
- {
- struct hci_drv_rp_read_info *rp;
- size_t rp_size;
- int err, i;
- u16 opcode, num_supported_commands =
- ARRAY_SIZE(btintel_pcie_hci_drv_supported_commands);
- rp_size = sizeof(*rp) + num_supported_commands * 2;
- rp = kmalloc(rp_size, GFP_KERNEL);
- if (!rp)
- return -ENOMEM;
- strscpy_pad(rp->driver_name, KBUILD_MODNAME);
- rp->num_supported_commands = cpu_to_le16(num_supported_commands);
- for (i = 0; i < num_supported_commands; i++) {
- opcode = btintel_pcie_hci_drv_supported_commands[i].opcode;
- bt_dev_dbg(hdev,
- "Supported HCI Drv command (0x%02x|0x%04x): %s",
- hci_opcode_ogf(opcode),
- hci_opcode_ocf(opcode),
- btintel_pcie_hci_drv_supported_commands[i].desc);
- rp->supported_commands[i] = cpu_to_le16(opcode);
- }
- err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
- HCI_DRV_STATUS_SUCCESS,
- rp, rp_size);
- kfree(rp);
- return err;
- }
- static const struct hci_drv_handler btintel_pcie_hci_drv_common_handlers[] = {
- { btintel_pcie_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
- };
- static const struct hci_drv_handler btintel_pcie_hci_drv_specific_handlers[] = {};
- static struct hci_drv btintel_pcie_hci_drv = {
- .common_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_common_handlers),
- .common_handlers = btintel_pcie_hci_drv_common_handlers,
- .specific_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_specific_handlers),
- .specific_handlers = btintel_pcie_hci_drv_specific_handlers,
- };
- static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
- {
- int err;
- struct hci_dev *hdev;
- hdev = hci_alloc_dev_priv(sizeof(struct btintel_data));
- if (!hdev)
- return -ENOMEM;
- hdev->bus = HCI_PCI;
- hci_set_drvdata(hdev, data);
- data->hdev = hdev;
- SET_HCIDEV_DEV(hdev, &data->pdev->dev);
- hdev->manufacturer = 2;
- hdev->open = btintel_pcie_open;
- hdev->close = btintel_pcie_close;
- hdev->send = btintel_pcie_send_frame;
- hdev->setup = btintel_pcie_setup;
- hdev->shutdown = btintel_shutdown_combined;
- hdev->hw_error = btintel_pcie_hw_error;
- hdev->set_diag = btintel_set_diag;
- hdev->set_bdaddr = btintel_set_bdaddr;
- hdev->reset = btintel_pcie_reset;
- hdev->wakeup = btintel_pcie_wakeup;
- hdev->hci_drv = &btintel_pcie_hci_drv;
- err = hci_register_dev(hdev);
- if (err < 0) {
- BT_ERR("Failed to register to hdev (%d)", err);
- goto exit_error;
- }
- data->dmp_hdr.driver_name = KBUILD_MODNAME;
- return 0;
- exit_error:
- hci_free_dev(hdev);
- return err;
- }
- static int btintel_pcie_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
- {
- int err;
- struct btintel_pcie_data *data;
- if (!pdev)
- return -ENODEV;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->pdev = pdev;
- spin_lock_init(&data->irq_lock);
- spin_lock_init(&data->hci_rx_lock);
- init_waitqueue_head(&data->gp0_wait_q);
- data->gp0_received = false;
- init_waitqueue_head(&data->tx_wait_q);
- data->tx_wait_done = false;
- data->workqueue = alloc_ordered_workqueue(KBUILD_MODNAME, WQ_HIGHPRI);
- if (!data->workqueue)
- return -ENOMEM;
- skb_queue_head_init(&data->rx_skb_q);
- INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
- data->boot_stage_cache = 0x00;
- data->img_resp_cache = 0x00;
- err = btintel_pcie_config_pcie(pdev, data);
- if (err)
- goto exit_error;
- pci_set_drvdata(pdev, data);
- err = btintel_pcie_alloc(data);
- if (err)
- goto exit_error;
- err = btintel_pcie_enable_bt(data);
- if (err)
- goto exit_error;
- /* CNV information (CNVi and CNVr) is in CSR */
- data->cnvi = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_HW_REV_REG);
- data->cnvr = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_RF_ID_REG);
- err = btintel_pcie_start_rx(data);
- if (err)
- goto exit_error;
- err = btintel_pcie_setup_hdev(data);
- if (err)
- goto exit_error;
- bt_dev_dbg(data->hdev, "cnvi: 0x%8.8x cnvr: 0x%8.8x", data->cnvi,
- data->cnvr);
- return 0;
- exit_error:
- /* reset device before exit */
- btintel_pcie_reset_bt(data);
- pci_clear_master(pdev);
- pci_set_drvdata(pdev, NULL);
- return err;
- }
- static void btintel_pcie_remove(struct pci_dev *pdev)
- {
- struct btintel_pcie_data *data;
- data = pci_get_drvdata(pdev);
- btintel_pcie_disable_interrupts(data);
- btintel_pcie_synchronize_irqs(data);
- flush_work(&data->rx_work);
- btintel_pcie_reset_bt(data);
- for (int i = 0; i < data->alloc_vecs; i++) {
- struct msix_entry *msix_entry;
- msix_entry = &data->msix_entries[i];
- free_irq(msix_entry->vector, msix_entry);
- }
- pci_free_irq_vectors(pdev);
- btintel_pcie_release_hdev(data);
- destroy_workqueue(data->workqueue);
- btintel_pcie_free(data);
- pci_clear_master(pdev);
- pci_set_drvdata(pdev, NULL);
- }
- #ifdef CONFIG_DEV_COREDUMP
- static void btintel_pcie_coredump(struct device *dev)
- {
- struct pci_dev *pdev = to_pci_dev(dev);
- struct btintel_pcie_data *data = pci_get_drvdata(pdev);
- if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
- return;
- data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
- queue_work(data->workqueue, &data->rx_work);
- }
- #endif
- static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
- {
- int retry = 0, status;
- u32 dx_intr_timeout_ms = 200;
- do {
- data->gp0_received = false;
- btintel_pcie_wr_sleep_cntrl(data, dxstate);
- status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
- msecs_to_jiffies(dx_intr_timeout_ms));
- if (status)
- return 0;
- bt_dev_warn(data->hdev,
- "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
- dx_intr_timeout_ms, dxstate, retry);
- /* clear gp0 cause */
- btintel_pcie_clr_reg_bits(data,
- BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
- BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
- /* A hardware bug may cause the alive interrupt to be missed.
- * Check if the controller reached the expected state and retry
- * the operation only if it hasn't.
- */
- if (dxstate == BTINTEL_PCIE_STATE_D0) {
- if (btintel_pcie_in_d0(data))
- return 0;
- } else {
- if (btintel_pcie_in_d3(data))
- return 0;
- }
- } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);
- return -EBUSY;
- }
- static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
- {
- struct pci_dev *pdev = to_pci_dev(dev);
- struct btintel_pcie_data *data;
- ktime_t start;
- u32 dxstate;
- int err;
- data = pci_get_drvdata(pdev);
- dxstate = (mesg.event == PM_EVENT_SUSPEND ?
- BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
- data->pm_sx_event = mesg.event;
- start = ktime_get();
- /* Refer: 6.4.11.7 -> Platform power management */
- err = btintel_pcie_set_dxstate(data, dxstate);
- if (err)
- return err;
- bt_dev_dbg(data->hdev,
- "device entered into d3 state from d0 in %lld us",
- ktime_to_us(ktime_get() - start));
- return err;
- }
- static int btintel_pcie_suspend(struct device *dev)
- {
- return btintel_pcie_suspend_late(dev, PMSG_SUSPEND);
- }
- static int btintel_pcie_hibernate(struct device *dev)
- {
- return btintel_pcie_suspend_late(dev, PMSG_HIBERNATE);
- }
- static int btintel_pcie_freeze(struct device *dev)
- {
- return btintel_pcie_suspend_late(dev, PMSG_FREEZE);
- }
- static int btintel_pcie_resume(struct device *dev)
- {
- struct pci_dev *pdev = to_pci_dev(dev);
- struct btintel_pcie_data *data;
- ktime_t start;
- int err;
- data = pci_get_drvdata(pdev);
- data->gp0_received = false;
- start = ktime_get();
- /* When the system enters S4 (hibernate) mode, bluetooth device loses
- * power, which results in the erasure of its loaded firmware.
- * Consequently, function level reset (flr) is required on system
- * resume to bring the controller back into an operational state by
- * initiating a new firmware download.
- */
- if (data->pm_sx_event == PM_EVENT_FREEZE ||
- data->pm_sx_event == PM_EVENT_HIBERNATE) {
- set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
- btintel_pcie_reset(data->hdev);
- return 0;
- }
- /* Refer: 6.4.11.7 -> Platform power management */
- err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);
- if (err == 0) {
- bt_dev_dbg(data->hdev,
- "device entered into d0 state from d3 in %lld us",
- ktime_to_us(ktime_get() - start));
- return err;
- }
- /* Trigger function level reset if the controller is in error
- * state during resume() to bring back the controller to
- * operational mode
- */
- data->boot_stage_cache = btintel_pcie_rd_reg32(data,
- BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
- if (btintel_pcie_in_error(data) ||
- btintel_pcie_in_device_halt(data)) {
- bt_dev_err(data->hdev, "Controller in error state for D0 entry");
- if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
- &data->flags)) {
- data->dmp_hdr.trigger_reason =
- BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
- queue_work(data->workqueue, &data->rx_work);
- }
- set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
- btintel_pcie_reset(data->hdev);
- }
- return err;
- }
- static const struct dev_pm_ops btintel_pcie_pm_ops = {
- .suspend = btintel_pcie_suspend,
- .resume = btintel_pcie_resume,
- .freeze = btintel_pcie_freeze,
- .thaw = btintel_pcie_resume,
- .poweroff = btintel_pcie_hibernate,
- .restore = btintel_pcie_resume,
- };
- static struct pci_driver btintel_pcie_driver = {
- .name = KBUILD_MODNAME,
- .id_table = btintel_pcie_table,
- .probe = btintel_pcie_probe,
- .remove = btintel_pcie_remove,
- .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
- #ifdef CONFIG_DEV_COREDUMP
- .driver.coredump = btintel_pcie_coredump
- #endif
- };
- static int __init btintel_pcie_init(void)
- {
- return pci_register_driver(&btintel_pcie_driver);
- }
- static void __exit btintel_pcie_exit(void)
- {
- pci_unregister_driver(&btintel_pcie_driver);
- btintel_pcie_free_restart_list();
- }
- module_init(btintel_pcie_init);
- module_exit(btintel_pcie_exit);
- MODULE_AUTHOR("Tedd Ho-Jeong An <tedd.an@intel.com>");
- MODULE_DESCRIPTION("Intel Bluetooth PCIe transport driver ver " VERSION);
- MODULE_VERSION(VERSION);
- MODULE_LICENSE("GPL");
|