sonic-buildimage/platform/broadcom/sonic-platform-modules-delta/ag9064/modules/i2c-mei_rw.c

591 lines
16 KiB
C
Raw Normal View History

#include "i2c-mei_rw.h"
#define MicroSecondDelay(time) udelay(time)
/****** Parameter *****/
/****** Struct *****/
typedef struct
{
volatile UINT32 CB_WW; // Circular Buffer Write Window
volatile UINT32 H_CSR; // Host Control and Status Register
volatile UINT32 CB_RW; // Circular Buffer Read Window
volatile UINT32 ME_CSR; // ME Control and Status Register (read only)
} HECI_MBAR_REGS;
typedef union
{
UINT32 DWord;
struct
{
UINT32 H_IE : 1, // 0 - Interrupt Enable ME
H_IS : 1, // 1 - Interrupt Status ME
H_IG : 1, // 2 - Interrupt Generate
H_RDY : 1, // 3 - Ready
H_RST : 1, // 4 - Reset
Reserved: 3, // 5~7
H_CBRP : 8, // 8~15 - CB Read Pointer
H_CBWP : 8, // 16~23 - CB Write Pointer
H_CBD : 8; // 24~31 - Circular Buffer Depth
} Bits;
} HECI_HOST_CSR;
// HECI_MBAR_REGS::ME_CSR - ME Control and Status Register
typedef union
{
UINT32 DWord;
struct
{
UINT32 ME_IE : 1, // 0 - Interrupt Enable (Host Read Access)
ME_IS : 1, // 1 - Interrupt Status (Host Read Access)
ME_IG : 1, // 2 - Interrupt Generate (Host Read Access)
ME_RDY : 1, // 3 - Ready (Host Read Access)
ME_RST : 1, // 4 - Reset (Host Read Access)
Reserved: 3, // 5~7
ME_CBRP : 8, // 8~15 - CB Read Pointer (Host Read Access)
ME_CBWP : 8, // 16~23 - CB Write Pointer (Host Read Access)
ME_CBD : 8; // 24~31 - Circular Buffer Depth (Host Read Access)
} Bits;
} HECI_ME_CSR;
/****** Function *****/
VOID* HeciMbarRead(IN HECI_DEVICE *pThis);
VOID HeciTrace(IN HECI_DEVICE*, IN CHAR8*, IN HECI_MSG_HEADER*, IN INT32);
EFI_STATUS HeciInit ( HECI_DEVICE *pThis,
UINT32 *pTimeout)
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Timeout = 0;
HECI_HOST_CSR sHCsr;
HECI_ME_CSR sMeCsr;
HECI_MBAR_REGS *pMbarRegs;
VOID *pAddrPoint;
if (pThis == NULL || (pThis->Mbar & 0xF) != 0 || pThis->Hidm > HECI_HIDM_LAST)
{
printk("Heci Init Failed");
return EFI_INVALID_PARAMETER;
}
if (pTimeout != NULL)
{
Timeout = *pTimeout;
}
// HECI vendor and device information away
pThis->PciCfg = PCI_LIB_ADDRESS(pThis->Bus, pThis->Dev, pThis->Fun, 0);
pThis->Vid = PciRead16(pThis->PciCfg + HECI_REG_VENDORID);
pThis->Did = PciRead16(pThis->PciCfg + HECI_REG_DEVICEID);
if (pThis->Vid != 0x8086)
{
printk("[HECI] Init failed, PCI device %d/%d/%d not valid HECI (%2X-%2X)\n",
pThis->Bus, pThis->Dev, pThis->Fun, pThis->Vid, pThis->Did);
return EFI_DEVICE_ERROR;
}
// Check MBAR,
pAddrPoint = HeciMbarRead(pThis);
pMbarRegs = (HECI_MBAR_REGS*)ioremap_nocache(pAddrPoint, 0x1000);
if (pMbarRegs == NULL)
{
printk("[HECI-%d] Init failed (device disabled)\n", pThis->Fun);
printk("Check MBAR Failed");
Status = EFI_DEVICE_ERROR;
goto GO_FAIL;
}
// Set HECI interrupt delivery mode.
sHCsr.DWord = pMbarRegs->H_CSR;
sHCsr.Bits.H_IE = 0;
pMbarRegs->H_CSR = sHCsr.DWord;
PciWrite8(pThis->PciCfg + HECI_REG_HIDM, pThis->Hidm);
// Check HECI was free
sMeCsr.DWord = pMbarRegs->ME_CSR;
if (!sMeCsr.Bits.ME_RDY)
{
Status = HecClearQue(pThis, &Timeout);
}
else
{
if (!sHCsr.Bits.H_RDY)
{
sHCsr.Bits.H_IG = 1;
sHCsr.Bits.H_RDY = 1;
sHCsr.Bits.H_RST = 0;
pMbarRegs->H_CSR = sHCsr.DWord;
}
pThis->HMtu = sHCsr.Bits.H_CBD * sizeof(UINT32) - sizeof(HECI_MSG_HEADER);
pThis->MeMtu = sMeCsr.Bits.ME_CBD * sizeof(UINT32) - sizeof(HECI_MSG_HEADER);
}
GO_FAIL:
if (pTimeout != NULL)
{
*pTimeout = Timeout;
}
pThis->Mefs1.DWord = HeciPciReadMefs1();
iounmap((VOID *)pMbarRegs);
return Status;
}
EFI_STATUS HecClearQue ( HECI_DEVICE *pThis,
UINT32 *pTimeout)
{
EFI_STATUS Status;
UINT32 Timeout = 0;
HECI_HOST_CSR sHCsr;
HECI_ME_CSR sMeCsr;
HECI_MBAR_REGS *pMbarRegs;
VOID *pAddrPoint;
if (pThis == NULL)
{
printk("Failed");
return EFI_INVALID_PARAMETER;
}
// Check for HECI availability on PCI
pAddrPoint = HeciMbarRead(pThis);
pMbarRegs = (HECI_MBAR_REGS*)ioremap_nocache(pAddrPoint, 0x1000);
printk("pMbarRegs: %x\n", pMbarRegs);
if (pMbarRegs == NULL)
{
printk("[HECI-%d] Reset failed (device disabled)\n", pThis->Fun);
printk("Failed");
return EFI_DEVICE_ERROR;
}
if (pTimeout != NULL)
{
Timeout = *pTimeout;
}
printk("[HECI-%d] Resetting HECI interface (CSR %X/%X)\n",
pThis->Fun, pMbarRegs->H_CSR, pMbarRegs->ME_CSR);
sHCsr.DWord = pMbarRegs->H_CSR;
if (!sHCsr.Bits.H_RST)
{
sHCsr.Bits.H_RST = 1;
sHCsr.Bits.H_IG = 1;
pMbarRegs->H_CSR = sHCsr.DWord;
}
// Wait for H_RDY cleared to make sure that the reset started.
while (1)
{
sHCsr.DWord = pMbarRegs->H_CSR;
if (!sHCsr.Bits.H_RDY)
{
break;
}
if (Timeout == 0)
{
printk("[HECI-%d] Reset failed (timeout)(CSR %X/%X)\n",
pThis->Fun, pMbarRegs->H_CSR, pMbarRegs->ME_CSR);
Status = EFI_TIMEOUT;
goto GO_FAIL;
}
MicroSecondDelay(HECI_TIMEOUT_UNIT);
Timeout--;
}
// Wait for ME to perform reset and signal it is ready.
while (1)
{
sMeCsr.DWord = pMbarRegs->ME_CSR;
if (sMeCsr.Bits.ME_RDY)
{
break;
}
if (Timeout == 0)
{
printk("[HECI-%d] Reset failed (timeout)(CSR %X/%X)\n",
pThis->Fun, pMbarRegs->H_CSR, pMbarRegs->ME_CSR);
Status = EFI_TIMEOUT;
goto GO_FAIL;
}
MicroSecondDelay(HECI_TIMEOUT_UNIT);
Timeout--;
}
// ME side is ready, signal host side is ready too.
sHCsr.DWord = pMbarRegs->H_CSR;
sHCsr.Bits.H_RST = 0;
sHCsr.Bits.H_RDY = 1;
sHCsr.Bits.H_IG = 1;
pMbarRegs->H_CSR = sHCsr.DWord;
// Update MTU, ME could change it during reset.
pThis->HMtu = sHCsr.Bits.H_CBD * sizeof(UINT32) - sizeof(HECI_MSG_HEADER);
pThis->MeMtu = sMeCsr.Bits.ME_CBD * sizeof(UINT32) - sizeof(HECI_MSG_HEADER);
Status = EFI_SUCCESS;
GO_FAIL:
if (pTimeout != NULL)
{
*pTimeout = Timeout;
}
pThis->Mefs1.DWord = HeciPciReadMefs1();
iounmap((VOID *)pMbarRegs);
return Status;
}
EFI_STATUS HeciMsgRecv ( HECI_DEVICE *pThis,
UINT32 *pTimeout,
HECI_MSG_HEADER *pMsgBuf,
UINT32 *pBufLen )
{
EFI_STATUS Status = EFI_SUCCESS;
UINT32 Timeout = 0;
UINT32 DWord, dwDataReads, dwBufLen;
UINT8 bFilledSlots, bMsgLen = 0;
HECI_HOST_CSR sHCsr;
HECI_ME_CSR sMeCsr;
HECI_MBAR_REGS *pMbarRegs;
VOID *pAddrPoint;
if (pThis == NULL || pMsgBuf == NULL ||
pBufLen == NULL || *pBufLen < sizeof(HECI_MSG_HEADER))
{
printk("Heci MsgRecv Failed\n");
return EFI_INVALID_PARAMETER;
}
// Check for HECI availability on PCI
pAddrPoint = HeciMbarRead(pThis);
pMbarRegs = (HECI_MBAR_REGS*)ioremap_nocache(pAddrPoint, 0x1000);
if (pMbarRegs == NULL)
{
printk("[HECI-%d] Receive failed (device disabled)\n", pThis->Fun);
printk("pMbarRegs Failed\n");
return EFI_DEVICE_ERROR;
}
if (pTimeout != NULL)
{
Timeout = *pTimeout;
}
// read from queue.
dwBufLen = *pBufLen;
*pBufLen = dwDataReads = 0;
while (1)
{
sHCsr.DWord = pMbarRegs->H_CSR;
sMeCsr.DWord = pMbarRegs->ME_CSR;
bFilledSlots = (UINT8)((INT8)sMeCsr.Bits.ME_CBWP - (INT8)sMeCsr.Bits.ME_CBRP);
// Is it ready ?
if (!sMeCsr.Bits.ME_RDY || !sHCsr.Bits.H_RDY || bFilledSlots > sHCsr.Bits.H_CBD)
{
Status = HecClearQue(pThis, &Timeout);
if (EFI_ERROR(Status))
{
goto GO_FAIL;
}
continue;
}
// Read queue
while (bFilledSlots-- > 0)
{
DWord = pMbarRegs->CB_RW;
if (*pBufLen < dwBufLen)
{
if (dwDataReads < dwBufLen / sizeof(UINT32))
{
((UINT32*)pMsgBuf)[dwDataReads] = DWord;
*pBufLen += sizeof(UINT32);
}
else
{
switch (dwBufLen % sizeof(UINT32))
{
case 3: ((UINT8*)pMsgBuf)[*pBufLen + 2] = (UINT8)(DWord >> 16);
case 2: ((UINT8*)pMsgBuf)[*pBufLen + 1] = (UINT8)(DWord >> 8);
case 1: ((UINT8*)pMsgBuf)[*pBufLen + 0] = (UINT8)DWord;
}
*pBufLen += dwBufLen % sizeof(UINT32);
}
}
else
{
printk("[HECI-%d] Message 0x%08X exceeds buffer size (%dB)\n",
pThis->Fun, pMsgBuf[0].DWord, dwBufLen);
}
dwDataReads++;
// Read message length.
if (bMsgLen == 0)
{
bMsgLen = (UINT8)((pMsgBuf[0].Bits.Length + sizeof(UINT32) - 1) / sizeof(UINT32));
bMsgLen++; // One more double word for message header
//
// Sanity check. If message length exceeds queue length this is
// not valid header. We are out of synch, let's reset the queue.
//
if (bMsgLen > sMeCsr.Bits.ME_CBD)
{
printk("[HECI-%d] 0x%08X does not seem to be msg header, reseting...\n",
pThis->Fun, pMsgBuf[0].DWord);
Status = HecClearQue(pThis, &Timeout);
if (EFI_ERROR(Status))
{
goto GO_FAIL;
}
*pBufLen = dwDataReads = bMsgLen = 0;
break; // while (bFilledSlots)
}
}
// If message is complete set interrupt to ME to let it know that next
// message can be sent and exit.
if (dwDataReads >= bMsgLen)
{
sMeCsr.DWord = pMbarRegs->ME_CSR;
sHCsr.DWord = pMbarRegs->H_CSR;
if (!sMeCsr.Bits.ME_RDY)
{
HecClearQue(pThis, &Timeout);
Status = EFI_ABORTED;
}
else
{
HeciTrace(pThis, " Got msg: ", pMsgBuf, *pBufLen);
sHCsr.Bits.H_IG = 1;
pMbarRegs->H_CSR = sHCsr.DWord;
}
goto GO_FAIL;
}
}
if (Timeout == 0)
{
printk("[HECI-%d] Receive failed (timeout)\n", pThis->Fun);
Status = EFI_TIMEOUT;
goto GO_FAIL;
}
MicroSecondDelay(HECI_TIMEOUT_UNIT);
Timeout--;
}
GO_FAIL:
if (pTimeout != NULL)
{
*pTimeout = Timeout;
}
pThis->Mefs1.DWord = HeciPciReadMefs1();
iounmap((VOID *)pMbarRegs);
return Status;
}
EFI_STATUS HeciMsgSend ( HECI_DEVICE *pThis,
UINT32 *pTimeout,
HECI_MSG_HEADER *pMessage)
{
EFI_STATUS Status;
UINT32 Timeout = 0;
UINT8 bEmptySlots;
UINT8 i, bMsgLen;
HECI_HOST_CSR sHCsr;
HECI_ME_CSR sMeCsr;
HECI_MBAR_REGS *pMbarRegs;
VOID *pAddrPoint;
if (pThis == NULL || pMessage == NULL)
{
printk("HeciMsgSend Failed\n");
return EFI_INVALID_PARAMETER;
}
HeciTrace(pThis, "Send msg: ", pMessage, sizeof(HECI_MSG_HEADER) + pMessage->Bits.Length);
// Check for HECI availability
pAddrPoint = HeciMbarRead(pThis);
pMbarRegs = (HECI_MBAR_REGS*)ioremap_nocache(pAddrPoint, 0x1000);
if (pMbarRegs == NULL)
{
printk("[HECI-%d] Send failed (device disabled)\n", pThis->Fun);
printk("Failed\n");
return EFI_DEVICE_ERROR;
}
if (pTimeout != NULL)
{
Timeout = *pTimeout;
}
bMsgLen = (UINT8)((pMessage->Bits.Length + sizeof(UINT32) - 1) / sizeof(UINT32));
bMsgLen++; //message header
while (1)
{
sHCsr.DWord = pMbarRegs->H_CSR;
sMeCsr.DWord = pMbarRegs->ME_CSR;
// If message is more than queue length go fail.
if (bMsgLen > sHCsr.Bits.H_CBD)
{
printk("[HECI-%d] Send failed (msg %d B, queue %d B only)\n",
pThis->Fun, pMessage->Bits.Length, sHCsr.Bits.H_CBD * sizeof(UINT32));
Status = EFI_BAD_BUFFER_SIZE;
goto GO_FAIL;
}
bEmptySlots = (UINT8)sHCsr.Bits.H_CBD -
(UINT8)((INT8)sHCsr.Bits.H_CBWP - (INT8)sHCsr.Bits.H_CBRP);
// Is it ready ?
if (!sMeCsr.Bits.ME_RDY || !sHCsr.Bits.H_RDY || bEmptySlots > sHCsr.Bits.H_CBD)
{
Status = HecClearQue(pThis, &Timeout);
if (EFI_ERROR(Status))
{
goto GO_FAIL;
}
continue;
}
if (bMsgLen <= bEmptySlots)
{
for (i = 0; i < bMsgLen; i++)
{
pMbarRegs->CB_WW = ((UINT32*)pMessage)[i];
}
sMeCsr.DWord = pMbarRegs->ME_CSR;
if (!sMeCsr.Bits.ME_RDY)
{
printk("[HECI-%d] Queue has been reset while sending\n", pThis->Fun);
continue;
}
sHCsr.DWord = pMbarRegs->H_CSR;
sHCsr.Bits.H_IS = 0;
sHCsr.Bits.H_IG = 1;
pMbarRegs->H_CSR = sHCsr.DWord;
Status = EFI_SUCCESS;
goto GO_FAIL;
}
if (Timeout == 0)
{
printk("[HECI-%d] Send failed (timeout)\n", pThis->Fun);
Status = EFI_TIMEOUT;
goto GO_FAIL;
}
MicroSecondDelay(HECI_TIMEOUT_UNIT);
Timeout--;
}
GO_FAIL:
if (pTimeout != NULL)
{
*pTimeout = Timeout;
}
pThis->Mefs1.DWord = HeciPciReadMefs1();
iounmap((VOID *)pMbarRegs);
return Status;
}
VOID *HeciMbarRead(HECI_DEVICE *pThis)
{
VOID *start;
UINT16 Cmd;
union
{
UINT64 QWord;
struct
{
UINT32 DWordL;
UINT32 DWordH;
} Bits;
} Mbar;
//
// Read MBAR.
Mbar.QWord = 0;
Mbar.Bits.DWordL = PciRead32(pThis->PciCfg + HECI_REG_MBAR);
if (Mbar.Bits.DWordL == 0xFFFFFFFF)
{
printk("[HECI-%d] Device disabled\n", pThis->Fun);
Mbar.Bits.DWordL = 0;
goto GO_FAIL;
}
if (Mbar.Bits.DWordL & 0x4) // if 64-bit address add the upper half
{
Mbar.Bits.DWordH = PciRead32(pThis->PciCfg + HECI_REG_MBAR + 4);
}
Mbar.Bits.DWordL &= 0xFFFFFFF0;
if (Mbar.QWord == 0)
{
if (pThis->Mbar == 0)
{
printk("[HECI-%d] MBAR not programmed\n", pThis->Fun);
goto GO_FAIL;
}
else
{
Mbar.QWord = pThis->Mbar;
printk("[HECI-%d] MBAR not programmed, using default 0x%08X%08X\n",
pThis->Fun, Mbar.Bits.DWordH, Mbar.Bits.DWordL);
// Programm the MBAR, set the 64-bit support bit regardless of the size
// of the address currently used.
PciWrite32(pThis->PciCfg + HECI_REG_MBAR + 4, Mbar.Bits.DWordH);
PciWrite32(pThis->PciCfg + HECI_REG_MBAR, Mbar.Bits.DWordL | 4);
}
}
else
{
pThis->Mbar = Mbar.QWord;
}
// Enable the MBAR
Cmd = PciRead16(pThis->PciCfg + HECI_REG_COMMAND);
if (!(Cmd & HECI_CMD_MSE))
{
PciWrite16(pThis->PciCfg + HECI_REG_COMMAND, Cmd | HECI_CMD_BME | HECI_CMD_MSE);
}
GO_FAIL:
return (VOID*)(INTN)Mbar.QWord;
}
VOID HeciTrace( HECI_DEVICE *pThis,
CHAR8 *pPrefix,
HECI_MSG_HEADER *pMsg,
INT32 MsgLen)
{
#if 0 /// Trace Enable or Disable
if (MsgLen > 4)
{
UINT32 dwLineBreak = 0;
UINT32 dwIndex = 0;
UINT8 *pMsgBody = (UINT8*)&pMsg[1];
MsgLen -= 4;
while (MsgLen-- > 0)
{
if (dwLineBreak == 0)
printk("%02x: ", (dwIndex & 0xF0));
printk("%02x ", pMsgBody[dwIndex++]);
dwLineBreak++;
if (dwLineBreak == 16)
{
printk("\n");
dwLineBreak = 0;
}
if (dwLineBreak == 8)
{
printk("-");
}
}
printk("\n");
}
#endif
}