/****************************************************************************** * * Copyright(c) 2015 - 2018 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. * *****************************************************************************/ #define _RTL8822CE_HALMAC_C_ #include /* struct dvobj_priv and etc. */ #include "../../hal_halmac.h" #include "../rtl8822c.h" /* rtl8822c_get_tx_desc_size() */ #include "rtl8822ce.h" static u8 pci_write_port_not_xmitframe(void *d, u32 size, u8 *pBuf, u8 qsel) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; struct pci_dev *pdev = pobj->ppcidev; PADAPTER padapter = dvobj_get_primary_adapter(pobj); u8 *txbd; dma_addr_t txbd_dma; u8 ret = _SUCCESS; dma_addr_t mapping; u16 tx_page_size = 128; u16 tx_page_used = 0; int i; /* map TX DESC buf_addr (including TX DESC + tx data) */ mapping = pci_map_single(pdev, pBuf, size + TX_WIFI_INFO_SIZE, PCI_DMA_TODEVICE); /* Calculate page size. * Total buffer length including TX_WIFI_INFO and PacketLen */ if (tx_page_size > 0) { tx_page_used = (size + TX_WIFI_INFO_SIZE) / tx_page_size; if (((size + TX_WIFI_INFO_SIZE) % tx_page_size) > 0) tx_page_used++; } txbd = pci_alloc_consistent(pdev, sizeof(struct tx_buf_desc), &txbd_dma); if (!txbd) { pci_unmap_single(pdev, mapping, size + TX_WIFI_INFO_SIZE, PCI_DMA_FROMDEVICE); return _FALSE; } /* BD init */ if (qsel == HALMAC_TXDESC_QSEL_H2C_CMD) { rtw_write32(padapter, REG_H2CQ_TXBD_DESA_8822C, txbd_dma & DMA_BIT_MASK(32)); #ifdef CONFIG_64BIT_DMA rtw_write32(padapter, REG_H2CQ_TXBD_DESA_8822C + 4, ((u64)txbd_dma) >> 32); #endif rtw_write32(padapter, REG_H2CQ_TXBD_NUM_8822C, 2 | ((RTL8822CE_SEG_NUM << 12) & 0x3000)); } else { rtw_write32(padapter, REG_BCNQ_TXBD_DESA_8822C, txbd_dma & DMA_BIT_MASK(32)); #ifdef CONFIG_64BIT_DMA rtw_write32(padapter, REG_BCNQ_TXBD_DESA_8822C + 4, ((u64)txbd_dma) >> 32); #endif } /* * Reset all tx buffer desciprtor content * -- Reset first element */ _rtw_memset(txbd, 0, sizeof(struct tx_buf_desc)); /* * Fill buffer length of the first buffer, * For 8821ce, it is required that TX_WIFI_INFO is put in first segment, * and the size of the first segment cannot be larger than * TX_WIFI_INFO_SIZE. */ SET_TX_BD_TX_BUFF_SIZE0(txbd, TX_WIFI_INFO_SIZE); SET_TX_BD_PSB(txbd, tx_page_used); /* starting addr of TXDESC */ SET_TX_BD_PHYSICAL_ADDR0_LOW(txbd, mapping); /* * It is assumed that in linux implementation, packet is coalesced * in only one buffer. Extension mode is not supported here */ SET_TXBUFFER_DESC_LEN_WITH_OFFSET(txbd, 1, size); /* don't using extendsion mode. */ SET_TXBUFFER_DESC_AMSDU_WITH_OFFSET(txbd, 1, 0); SET_TXBUFFER_DESC_ADD_LOW_WITH_OFFSET(txbd, 1, mapping + TX_WIFI_INFO_SIZE); /* pkt */ wmb(); if (qsel == HALMAC_TXDESC_QSEL_H2C_CMD) rtw_write16(padapter, REG_H2CQ_TXBD_IDX, 1); else { SET_TX_BD_OWN(txbd, 1); /* kick start */ rtw_write8(padapter, REG_RX_RXBD_NUM + 1, rtw_read8(padapter, REG_RX_RXBD_NUM + 1) | BIT(4)); } udelay(100); pci_free_consistent(pdev, sizeof(struct tx_buf_desc), txbd, txbd_dma); pci_unmap_single(pdev, mapping, size + TX_WIFI_INFO_SIZE, PCI_DMA_FROMDEVICE); return ret; } static u8 pci_write_data_not_xmitframe(void *d, u8 *pBuf, u32 size, u8 qsel) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; PADAPTER padapter = dvobj_get_primary_adapter(pobj); struct halmac_adapter *halmac = dvobj_to_halmac((struct dvobj_priv *)d); struct halmac_api *api = HALMAC_GET_API(halmac); u32 desclen = 0; u32 len = 0; u8 *buf = NULL; u8 ret = _FALSE; if (size + TXDESC_OFFSET > MAX_CMDBUF_SZ) { RTW_INFO("%s: total buffer size(%d) > MAX_CMDBUF_SZ(%d)\n", __func__, size + TXDESC_OFFSET, MAX_CMDBUF_SZ); return _FALSE; } desclen = rtl8822c_get_tx_desc_size(padapter); len = desclen + size; buf = rtw_zmalloc(len); if (!buf) { RTW_ERR("%s: alloc buffer fail!\n", __func__); return _FALSE; } /* copy data */ _rtw_memcpy(buf + desclen, pBuf, size); SET_TX_DESC_TXPKTSIZE_8822C(buf, size); /* TX_DESC is not included in the data, * driver needs to fill in the TX_DESC with qsel=h2c * Offset in TX_DESC should be set to 0. */ if (qsel == HALMAC_TXDESC_QSEL_H2C_CMD) SET_TX_DESC_OFFSET_8822C(buf, 0); else SET_TX_DESC_OFFSET_8822C(buf, desclen); SET_TX_DESC_QSEL_8822C(buf, qsel); api->halmac_fill_txdesc_checksum(halmac, buf); ret = pci_write_port_not_xmitframe(d, size, buf, qsel); if (ret == _SUCCESS) ret = _TRUE; else ret = _FALSE; rtw_mfree(buf, len); return _TRUE; } static u8 pci_write_data_rsvd_page_xmitframe(void *d, u8 *pBuf, u32 size) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; PADAPTER padapter = dvobj_get_primary_adapter(pobj); struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_tx_ring *ring = &pxmitpriv->tx_ring[BCN_QUEUE_INX]; struct pci_dev *pdev = pobj->ppcidev; struct xmit_frame *pcmdframe = NULL; struct xmit_buf *pxmitbuf = NULL; struct pkt_attrib *pattrib = NULL; u32 desclen = 0; u8 *txdesc = NULL; u8 DLBcnCount = 0; u32 poll = 0; u8 *txbd; BOOLEAN bcn_valid = _FALSE; if (size + TXDESC_OFFSET > MAX_CMDBUF_SZ) { RTW_INFO("%s: total buffer size(%d) > MAX_CMDBUF_SZ(%d)\n" , __func__, size + TXDESC_OFFSET, MAX_CMDBUF_SZ); return _FALSE; } pcmdframe = rtw_alloc_cmdxmitframe(pxmitpriv); if (pcmdframe == NULL) { RTW_INFO("%s: alloc ReservedPagePacket fail!\n", __func__); return _FALSE; } pxmitbuf = pcmdframe->pxmitbuf; desclen = rtl8822c_get_tx_desc_size(padapter); txdesc = pcmdframe->buf_addr; _rtw_memcpy((txdesc + desclen), pBuf, size); /* shift desclen */ /* update attribute */ pattrib = &pcmdframe->attrib; update_mgntframe_attrib(padapter, pattrib); pattrib->qsel = QSLT_BEACON; pattrib->pktlen = size; pattrib->last_txcmdsz = size; /* Clear beacon valid check bit. */ rtw_hal_set_hwreg(padapter, HW_VAR_BCN_VALID, NULL); rtw_hal_set_hwreg(padapter, HW_VAR_DL_BCN_SEL, NULL); dump_mgntframe(padapter, pcmdframe); DLBcnCount = 0; poll = 0; do { DLBcnCount++; do { rtw_yield_os(); /* does rsvd page download OK. */ rtw_hal_get_hwreg(padapter, HW_VAR_BCN_VALID,(u8 *)(&bcn_valid)); poll++; } while (!bcn_valid && (poll % 10) != 0 && !RTW_CANNOT_RUN(padapter)); } while (!bcn_valid && DLBcnCount <= 100 && !RTW_CANNOT_RUN(padapter)); txbd = (u8 *)(&ring->buf_desc[0]); pci_unmap_single(pdev, GET_TX_BD_PHYSICAL_ADDR0_LOW(txbd), pxmitbuf->len, PCI_DMA_TODEVICE); return _TRUE; } static u8 pci_write_data_h2c_normal(void *d, u8 *pBuf, u32 size) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; PADAPTER padapter = dvobj_get_primary_adapter(pobj); struct halmac_adapter *halmac = dvobj_to_halmac((struct dvobj_priv *)d); struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct xmit_frame *pcmdframe = NULL; struct pkt_attrib *pattrib = NULL; struct halmac_api *api; u32 desclen; u8 *buf; if (size + TXDESC_OFFSET > MAX_XMIT_EXTBUF_SZ) { RTW_INFO("%s: total buffer size(%d) > MAX_XMIT_EXTBUF_SZ(%d)\n" , __func__, size + TXDESC_OFFSET, MAX_XMIT_EXTBUF_SZ); return _FALSE; } pcmdframe = alloc_mgtxmitframe(pxmitpriv); if (pcmdframe == NULL) { RTW_INFO("%s: alloc ReservedPagePacket fail!\n", __func__); return _FALSE; } api = HALMAC_GET_API(halmac); desclen = rtl8822c_get_tx_desc_size(padapter); buf = pcmdframe->buf_addr; _rtw_memcpy(buf + desclen, pBuf, size); /* shift desclen */ SET_TX_DESC_TXPKTSIZE_8822C(buf, size); SET_TX_DESC_OFFSET_8822C(buf, 0); SET_TX_DESC_QSEL_8822C(buf, HALMAC_TXDESC_QSEL_H2C_CMD); SET_TX_DESC_TXDESC_CHECKSUM_8822C(buf, 0); api->halmac_fill_txdesc_checksum(halmac, buf); /* update attribute */ pattrib = &pcmdframe->attrib; update_mgntframe_attrib(padapter, pattrib); pattrib->qsel = QSLT_CMD; pattrib->pktlen = size; pattrib->last_txcmdsz = size; /* fill tx desc in dump_mgntframe */ dump_mgntframe(padapter, pcmdframe); return _TRUE; } static u8 pci_write_data_rsvd_page(void *d, u8 *pBuf, u32 size) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; PADAPTER padapter = dvobj_get_primary_adapter(pobj); HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); u8 ret; if (pHalData->not_xmitframe_fw_dl) ret = pci_write_data_not_xmitframe(d, pBuf, size, HALMAC_TXDESC_QSEL_BEACON); else ret = pci_write_data_rsvd_page_xmitframe(d, pBuf, size); if (ret == _TRUE) return 1; return 0; } static u8 pci_write_data_h2c(void *d, u8 *pBuf, u32 size) { struct dvobj_priv *pobj = (struct dvobj_priv *)d; PADAPTER padapter = dvobj_get_primary_adapter(pobj); HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); u8 ret; if (pHalData->not_xmitframe_fw_dl) ret = pci_write_data_not_xmitframe(d, pBuf, size, HALMAC_TXDESC_QSEL_H2C_CMD); else ret = pci_write_data_h2c_normal(d, pBuf, size); if (ret == _TRUE) return 1; return 0; } int rtl8822ce_halmac_init_adapter(PADAPTER padapter) { struct dvobj_priv *d; struct halmac_platform_api *api; int err; d = adapter_to_dvobj(padapter); api = &rtw_halmac_platform_api; api->SEND_RSVD_PAGE = pci_write_data_rsvd_page; api->SEND_H2C_PKT = pci_write_data_h2c; err = rtw_halmac_init_adapter(d, api); return err; }