/******************************************************************************
|
*
|
* 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 <drv_types.h> /* PADAPTER and etc. */
|
|
#ifdef CONFIG_PLATFORM_RTK129X
|
#include <soc/realtek/rtd129x_lockapi.h>
|
|
#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 /* CONFIG_PLATFORM_RTK129X */
|
|
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 CONFIG_PLATFORM_RTK129X
|
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 /* CONFIG_PLATFORM_RTK129X */
|
|
pops->_read_mem = &pci_read_mem;
|
pops->_read_port = &pci_read_port;
|
|
#ifdef CONFIG_PLATFORM_RTK129X
|
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 /* CONFIG_PLATFORM_RTK129X */
|
|
pops->_write_mem = &pci_write_mem;
|
pops->_write_port = &pci_write_port;
|
|
|
}
|