/****************************************************************************** * * Copyright(c) 2015 - 2017 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_IO_C_ #include /* PADAPTER and etc. */ #ifdef RTK_129X_PLATFORM #include #define IO_2K_MASK 0xFFFFF800 #define IO_4K_MASK 0xFFFFF000 #define MAX_RETRY 5 static u32 pci_io_read_129x(struct dvobj_priv *pdvobjpriv, u32 addr, u8 size) { unsigned long mask_addr = pdvobjpriv->mask_addr; unsigned long tran_addr = pdvobjpriv->tran_addr; u8 busnumber = pdvobjpriv->pcipriv.busnumber; u32 rval = 0; u32 mask; u32 translate_val = 0; u32 tmp_addr = addr & 0xFFF; _irqL irqL; u32 pci_error_status = 0; int retry_cnt = 0; unsigned long flags; _enter_critical(&pdvobjpriv->io_reg_lock, &irqL); /* PCIE1.1 0x9804FCEC, PCIE2.0 0x9803CCEC & 0x9803CC68 * can't be used because of 1295 hardware issue. */ if ((tmp_addr == 0xCEC) || ((busnumber == 0x01) && (tmp_addr == 0xC68))) { mask = IO_2K_MASK; writel(0xFFFFF800, (u8 *)mask_addr); translate_val = readl((u8 *)tran_addr); writel(translate_val|(addr&mask), (u8 *)tran_addr); } else if (addr >= 0x1000) { mask = IO_4K_MASK; translate_val = readl((u8 *)tran_addr); writel(translate_val|(addr&mask), (u8 *)tran_addr); } else mask = 0x0; pci_read_129x_retry: /* All RBUS1 driver need to have a workaround for emmc hardware error */ /* Need to protect 0xXXXX_X8XX~ 0xXXXX_X9XX */ if ((tmp_addr > 0x7FF) && (tmp_addr < 0xA00)) rtk_lockapi_lock(flags, __func__); switch (size) { case 1: rval = readb((u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; case 2: rval = readw((u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; case 4: rval = readl((u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; default: RTW_WARN("RTD129X: %s: wrong size %d\n", __func__, size); break; } if ((tmp_addr > 0x7FF) && (tmp_addr < 0xA00)) rtk_lockapi_unlock(flags, __func__); //DLLP error patch pci_error_status = readl( (u8 *)(pdvobjpriv->ctrl_start + 0x7C)); if(pci_error_status & 0x1F) { writel(pci_error_status, (u8 *)(pdvobjpriv->ctrl_start + 0x7C)); RTW_WARN("RTD129X: %s: DLLP(#%d) 0x%x reg=0x%x val=0x%x\n", __func__, retry_cnt, pci_error_status, addr, rval); if(retry_cnt < MAX_RETRY) { retry_cnt++; goto pci_read_129x_retry; } } /* PCIE1.1 0x9804FCEC, PCIE2.0 0x9803CCEC & 0x9803CC68 * can't be used because of 1295 hardware issue. */ if ((tmp_addr == 0xCEC) || ((busnumber == 0x01) && (tmp_addr == 0xC68))) { writel(translate_val, (u8 *)tran_addr); writel(0xFFFFF000, (u8 *)mask_addr); } else if (addr >= 0x1000) { writel(translate_val, (u8 *)tran_addr); } _exit_critical(&pdvobjpriv->io_reg_lock, &irqL); return rval; } static void pci_io_write_129x(struct dvobj_priv *pdvobjpriv, u32 addr, u8 size, u32 wval) { unsigned long mask_addr = pdvobjpriv->mask_addr; unsigned long tran_addr = pdvobjpriv->tran_addr; u8 busnumber = pdvobjpriv->pcipriv.busnumber; u32 mask; u32 translate_val = 0; u32 tmp_addr = addr & 0xFFF; _irqL irqL; unsigned long flags; _enter_critical(&pdvobjpriv->io_reg_lock, &irqL); /* PCIE1.1 0x9804FCEC, PCIE2.0 0x9803CCEC & 0x9803CC68 * can't be used because of 1295 hardware issue. */ if ((tmp_addr == 0xCEC) || ((busnumber == 0x01) && (tmp_addr == 0xC68))) { mask = IO_2K_MASK; writel(0xFFFFF800, (u8 *)mask_addr); translate_val = readl((u8 *)tran_addr); writel(translate_val|(addr&mask), (u8 *)tran_addr); } else if (addr >= 0x1000) { mask = IO_4K_MASK; translate_val = readl((u8 *)tran_addr); writel(translate_val|(addr&mask), (u8 *)tran_addr); } else mask = 0x0; /* All RBUS1 driver need to have a workaround for emmc hardware error */ /* Need to protect 0xXXXX_X8XX~ 0xXXXX_X9XX */ if ((tmp_addr > 0x7FF) && (tmp_addr < 0xA00)) rtk_lockapi_lock(flags, __func__); switch (size) { case 1: writeb((u8)wval, (u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; case 2: writew((u16)wval, (u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; case 4: writel((u32)wval, (u8 *)pdvobjpriv->pci_mem_start + (addr&~mask)); break; default: RTW_WARN("RTD129X: %s: wrong size %d\n", __func__, size); break; } if ((tmp_addr > 0x7FF) && (tmp_addr < 0xA00)) rtk_lockapi_unlock(flags, __func__); /* PCIE1.1 0x9804FCEC, PCIE2.0 0x9803CCEC & 0x9803CC68 * can't be used because of 1295 hardware issue. */ if ((tmp_addr == 0xCEC) || ((busnumber == 0x01) && (tmp_addr == 0xC68))) { writel(translate_val, (u8 *)tran_addr); writel(0xFFFFF000, (u8 *)mask_addr); } else if (addr >= 0x1000) { writel(translate_val, (u8 *)tran_addr); } _exit_critical(&pdvobjpriv->io_reg_lock, &irqL); } static u8 pci_read8_129x(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return (u8)pci_io_read_129x(pdvobjpriv, addr, 1); } static u16 pci_read16_129x(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return (u16)pci_io_read_129x(pdvobjpriv, addr, 2); } static u32 pci_read32_129x(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return (u32)pci_io_read_129x(pdvobjpriv, addr, 4); } /* * 2009.12.23. by tynli. Suggested by SD1 victorh. * For ASPM hang on AMD and Nvidia. * 20100212 Tynli: Do read IO operation after write for * all PCI bridge suggested by SD1. Origianally this is only for INTEL. */ static int pci_write8_129x(struct intf_hdl *phdl, u32 addr, u8 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; pci_io_write_129x(pdvobjpriv, addr, 1, val); return 1; } static int pci_write16_129x(struct intf_hdl *phdl, u32 addr, u16 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; pci_io_write_129x(pdvobjpriv, addr, 2, val); return 2; } static int pci_write32_129x(struct intf_hdl *phdl, u32 addr, u32 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; pci_io_write_129x(pdvobjpriv, addr, 4, val); return 4; } #else /* original*/ static u8 pci_read8(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return 0xff & readb((u8 *)pdvobjpriv->pci_mem_start + addr); } static u16 pci_read16(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return readw((u8 *)pdvobjpriv->pci_mem_start + addr); } static u32 pci_read32(struct intf_hdl *phdl, u32 addr) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; return readl((u8 *)pdvobjpriv->pci_mem_start + addr); } /* * 2009.12.23. by tynli. Suggested by SD1 victorh. * For ASPM hang on AMD and Nvidia. * 20100212 Tynli: Do read IO operation after write for * all PCI bridge suggested by SD1. Origianally this is only for INTEL. */ static int pci_write8(struct intf_hdl *phdl, u32 addr, u8 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; writeb(val, (u8 *)pdvobjpriv->pci_mem_start + addr); return 1; } static int pci_write16(struct intf_hdl *phdl, u32 addr, u16 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; writew(val, (u8 *)pdvobjpriv->pci_mem_start + addr); return 2; } static int pci_write32(struct intf_hdl *phdl, u32 addr, u32 val) { struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)phdl->pintf_dev; writel(val, (u8 *)pdvobjpriv->pci_mem_start + addr); return 4; } #endif /* RTK_129X_PLATFORM */ static void pci_read_mem(struct intf_hdl *phdl, u32 addr, u32 cnt, u8 *rmem) { RTW_INFO("%s(%d)fake function\n", __func__, __LINE__); } static void pci_write_mem(struct intf_hdl *phdl, u32 addr, u32 cnt, u8 *wmem) { RTW_INFO("%s(%d)fake function\n", __func__, __LINE__); } static u32 pci_read_port(struct intf_hdl *phdl, u32 addr, u32 cnt, u8 *rmem) { return 0; } static u32 pci_write_port(struct intf_hdl *phdl, u32 addr, u32 cnt, u8 *wmem) { _adapter *padapter = (_adapter *)phdl->padapter; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) netif_trans_update(padapter->pnetdev); #else padapter->pnetdev->trans_start = jiffies; #endif return 0; } void rtl8822ce_set_intf_ops(struct _io_ops *pops) { _rtw_memset((u8 *)pops, 0, sizeof(struct _io_ops)); #ifdef RTK_129X_PLATFORM pops->_read8 = &pci_read8_129x; pops->_read16 = &pci_read16_129x; pops->_read32 = &pci_read32_129x; #else pops->_read8 = &pci_read8; pops->_read16 = &pci_read16; pops->_read32 = &pci_read32; #endif /* RTK_129X_PLATFORM */ pops->_read_mem = &pci_read_mem; pops->_read_port = &pci_read_port; #ifdef RTK_129X_PLATFORM pops->_write8 = &pci_write8_129x; pops->_write16 = &pci_write16_129x; pops->_write32 = &pci_write32_129x; #else pops->_write8 = &pci_write8; pops->_write16 = &pci_write16; pops->_write32 = &pci_write32; #endif /* RTK_129X_PLATFORM */ pops->_write_mem = &pci_write_mem; pops->_write_port = &pci_write_port; }