📄 i2c.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
I2C.C I2C H/W Access routines (logical PDD)
Abstract:
Supports:
---------
o) Master Tx/Rx Modes only.
o) Multi-byte Tx/Rx (slave must support auto increment for multi-byte)
Functions:
Notes:
--*/
#include <windows.h>
#include <nkintr.h>
#include "drv.h"
//
// I2C Registers
//
#define rIICCON (pI2C->pI2CReg->IICCON) // Control Register
#define rIICSTAT (pI2C->pI2CReg->IICSTAT) // Status Register
#define rIICADD (pI2C->pI2CReg->IICADD) // Address Register
#define rIICDS (pI2C->pI2CReg->IICDS) // Data Shift Register
// BUGBUG: need ioctl to set timeout parms based on num chars client is expecting
DWORD gIntrIIC = SYSINTR_NOP;
#define RX_TIMEOUT 3000
#define TX_TIMEOUT 3000
#define I2C_POWER_ON 0x10000 // rCLKCON bit 16
static DWORD I2C_IST(LPVOID Context);
VOID
InitRegs(
PI2C_CONTEXT pI2C
)
{
EnterCriticalSection(&pI2C->RegCS);
//
// enable the I2C Clock (PCLK)
//
pI2C->pCLKPWRReg->CLKCON |= I2C_POWER_ON;
// setup GPIO for I2C
// rGPEUP &= ~0xc000; //Pull-up disable
// rGPEUP |= 0xc000; //pull-ups disable
// rGPECON &= ~(0xF<<28);
// rGPECON |= (0xA << 28); //GPE15:IICSDA, GPE14:IICSCL
// config controller
rIICCON = RESUME_ACK;
rIICSTAT = M_IDLE;
pI2C->State = IDLE;
pI2C->DataCount = INVALID_DATA_COUNT;
LeaveCriticalSection(&pI2C->RegCS);
}
DWORD
HW_Init(
PI2C_CONTEXT pI2C
)
{
DWORD dwErr = ERROR_SUCCESS;
UINT32 Irq;
RETAILMSG(1,(TEXT("I2C Init\r\n")));
if ( !pI2C ) {
return ERROR_INVALID_PARAMETER;
}
DEBUGMSG(ZONE_TRACE,(TEXT("+I2C_Init: %u, 0x%x, 0x%x \r\n"),
pI2C->Mode, pI2C->SlaveAddress));
InitializeCriticalSection(&pI2C->RegCS);
pI2C->Status = 0;
pI2C->Data = NULL;
pI2C->DataCount = INVALID_DATA_COUNT;
pI2C->Flags.DropRxAddr = FALSE;
pI2C->hProc = (HANDLE)GetCurrentProcessId();
InitRegs(pI2C);
// create I/O Done Event
if ( (pI2C->DoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
{
dwErr = GetLastError();
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Init ERROR: Unable to create Done event: %u \r\n"), dwErr));
goto _init_error;
}
// setup Operating Mode
if ( pI2C->Mode == INTERRUPT ) {
// create IST event
if ( (pI2C->ISTEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
{
dwErr = GetLastError();
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Init ERROR: Unable to create IST event: %u \r\n"), dwErr));
goto _init_error;
}
// Obtain sysintr values from the OAL for the camera interrupt.
//
Irq = IRQ_IIC;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &gIntrIIC, sizeof(UINT32), NULL))
{
DEBUGMSG(ZONE_ERR, (TEXT("ERROR: Failed to request the IIC sysintr.\r\n")));
gIntrIIC = SYSINTR_UNDEFINED;
return(FALSE);
}
RETAILMSG(1, (TEXT("IIC IRQ mapping: [IRQ:%d->sysIRQ:%d].\r\n"), Irq, gIntrIIC));
// initialize the interrupt
if( !InterruptInitialize(gIntrIIC, pI2C->ISTEvent, NULL, 0) )
{
dwErr = GetLastError();
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Init ERROR: Unable to initialize interrupt: %u\r\n"), dwErr));
goto _init_error;
}
InterruptDone(gIntrIIC);
// create the IST
if ( (pI2C->IST = CreateThread(NULL, 0, I2C_IST, (LPVOID)pI2C, 0, NULL)) == NULL)
{
dwErr = GetLastError();
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Init ERROR: Unable to create IST: %u\r\n"), dwErr));
goto _init_error;
}
// TODO: registry override
if ( !CeSetThreadPriority(pI2C->IST, I2C_THREAD_PRIORITY)) {
dwErr = GetLastError();
DEBUGMSG(ZONE_ERR, (TEXT("I2C_Init ERROR: CeSetThreadPriority ERROR:%d\n"), dwErr));
goto _init_error;
}
}
DEBUGMSG(ZONE_TRACE,(TEXT("-I2C_Init \r\n")));
return dwErr;
_init_error:
HW_Deinit(pI2C);
return dwErr;
}
DWORD
HW_Deinit(
PI2C_CONTEXT pI2C
)
{
if ( !pI2C )
return ERROR_INVALID_PARAMETER;
DEBUGMSG(ZONE_TRACE,(TEXT("I2C_Deinit: %u \r\n"), pI2C->State));
if ( pI2C->State == IDLE ) {
pI2C->State = OFF; // also signals the IST to terminate
rIICSTAT = M_IDLE;
if (pI2C->DoneEvent && CloseHandle(pI2C->DoneEvent))
pI2C->DoneEvent = NULL;
if (pI2C->Mode == INTERRUPT) {
InterruptDisable(gIntrIIC);
if (pI2C->ISTEvent && CloseHandle(pI2C->ISTEvent))
pI2C->ISTEvent = NULL;
if (pI2C->IST && CloseHandle(pI2C->IST))
pI2C->IST = NULL;
}
pI2C->Mode = POLLING;
pI2C->Data = NULL;
pI2C->DataCount = INVALID_DATA_COUNT;
pI2C->Flags.DropRxAddr = FALSE;
DeleteCriticalSection(&pI2C->RegCS);
return ERROR_SUCCESS;
} else {
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Deinit ERROR: %u \r\n"), pI2C->State));
return ERROR_BUSY;
}
}
DWORD
HW_Open(
PI2C_CONTEXT pI2C
)
{
// BUGBUG: power on device
return ERROR_SUCCESS;
}
DWORD
HW_Close(
PI2C_CONTEXT pI2C
)
{
// BUGBUG: power off device
return ERROR_SUCCESS;
}
__inline
DWORD
SyncIst(
PI2C_CONTEXT pI2C,
DWORD dwTimeout
)
{
DWORD w;
if (pI2C->Mode == INTERRUPT) {
DEBUGMSG(ZONE_READ|ZONE_WRITE,(TEXT("SyncIst...\r\n")));
RETAILMSG(1,(TEXT("SyncIst...\r\n")));
w = WaitForSingleObject(pI2C->DoneEvent, dwTimeout);
if (WAIT_OBJECT_0 != w) {
pI2C->State = IO_ABANDONED;
if (pI2C->LastError == ERROR_SUCCESS)
pI2C->LastError = ERROR_TIMEOUT;
DEBUGMSG(ZONE_WRN|ZONE_READ|ZONE_WRITE,(TEXT("SyncIst: IO_ABANDONED\r\n")));
RETAILMSG(1, (TEXT("SyncIst: IO_ABANDONED\r\n")));
}
DEBUGMSG(ZONE_READ|ZONE_WRITE,(TEXT("...SyncIst: 0x%X\r\n"), pI2C->LastError));
return pI2C->LastError;
} else {
while(pI2C->DataCount != INVALID_DATA_COUNT) {
// poll INT pending bit
if (pI2C->Mode == POLLING && (rIICCON & 0x10))
I2C_IST(pI2C);
RETAILMSG(1, (TEXT("SynIst] I2C_IST : Mode = %d"), pI2C->Mode));
}
}
return ERROR_SUCCESS;
}
DWORD
HW_Read(
PI2C_CONTEXT pI2C,
DWORD SlaveAddr, // slave address
UCHAR WordAddr, // starting word address
PUCHAR pData, // pdata
DWORD Count // bytes to read
)
{
DWORD dwErr;
if ( !VALID_CONTEXT(pI2C) )
return ERROR_INVALID_PARAMETER;
DEBUGMSG(ZONE_READ|ZONE_TRACE,(TEXT("+I2C_Read[%u]: 0x%X, 0x%X, 0x%X, %u\r\n"),
pI2C->State, SlaveAddr, WordAddr, pData, Count));
if ( !pData || !Count || IsBadWritePtr(pData, Count) ) {
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Read ERROR: invalid parameter \r\n")));
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&pI2C->RegCS);
if ( pI2C->State != IDLE) {
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Read ERROR: i2cState: %u \r\n"), pI2C->State));
LeaveCriticalSection(&pI2C->RegCS);
return ERROR_BUSY;
}
pI2C->LastError = ERROR_SUCCESS;
ResetEvent(pI2C->DoneEvent);
rIICSTAT = M_ACTIVE;
// pre-setup word address
pI2C->Data = NULL;
pI2C->State = SET_READ_ADDR;
pI2C->WordAddr = WordAddr;
pI2C->Flags.WordAddr = TRUE;
pI2C->DataCount = 1;
// enable the slave address drop
pI2C->Flags.DropRxAddr = TRUE;
// write slave address
rIICDS = (UCHAR)SlaveAddr;
rIICSTAT = MTX_START;
// Wait for IST to write the word address
if (WAIT_OBJECT_0 != SyncIst(pI2C, RX_TIMEOUT)) {
DEBUGMSG(ZONE_READ|ZONE_ERR,(TEXT("RX_TIMEOUT.1\r\n")));
goto _done;
}
ResetEvent(pI2C->DoneEvent);
// get read data
pI2C->State = READ_DATA;
pI2C->Data = pData;
pI2C->DataCount = Count;
rIICDS = (UCHAR)SlaveAddr;
rIICSTAT = MRX_START;
rIICCON = RESUME_ACK; // Resume IIC operation (clear bit 4)
// Wait for IST to get data
if (WAIT_OBJECT_0 != SyncIst(pI2C, RX_TIMEOUT)) {
DEBUGMSG(ZONE_READ|ZONE_ERR,(TEXT("RX_TIMEOUT.2\r\n")));
goto _done;
}
_done:
rIICSTAT = M_IDLE; // disable Rx/Tx
pI2C->State = IDLE;
pI2C->Data = NULL;
pI2C->DataCount = INVALID_DATA_COUNT;
if ( !pI2C->LastError && (SlaveAddr != pI2C->RxRetAddr) ) {
DEBUGMSG(ZONE_READ|ZONE_ERR,(TEXT("I2C_Read Invalid Return Address: 0x%X != 0x%X \r\n"),
pI2C->RxRetAddr, SlaveAddr ));
pI2C->LastError = ERROR_INCORRECT_ADDRESS;
TEST_TRAP;
}
pI2C->RxRetAddr = 0;
dwErr = pI2C->LastError;
LeaveCriticalSection(&pI2C->RegCS);
DEBUGMSG(ZONE_READ|ZONE_TRACE,(TEXT("-I2C_Read:%u \r\n"), dwErr ));
//RETAILMSG(1,(_T("data = 0x%X\r\n", *pData)));
return dwErr;
}
DWORD
HW_Write(
PI2C_CONTEXT pI2C,
DWORD SlaveAddr, // slave address
UCHAR WordAddr, // starting slave word address
PUCHAR pData, // pdata
DWORD Count // bytes to write
)
{
DWORD dwErr;
if ( !VALID_CONTEXT(pI2C) )
return ERROR_INVALID_PARAMETER;
DEBUGMSG(ZONE_WRITE|ZONE_TRACE,(TEXT("+I2C_Write[%u]: 0x%X, 0x%X, 0x%X, %u \r\n"),
//RETAILMSG(1,(TEXT("+I2C_Write[%u]: 0x%X, 0x%X, 0x%X, %u \r\n"),
pI2C->State, SlaveAddr, WordAddr, *pData, Count));
if ( !pData || !Count || IsBadReadPtr(pData, Count) ) {
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Write ERROR: invalid parameter \r\n")));
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&pI2C->RegCS);
if ( pI2C->State != IDLE) {
DEBUGMSG(ZONE_ERR,(TEXT("I2C_Write ERROR: i2cState: %u \r\n"), pI2C->State));
LeaveCriticalSection(&pI2C->RegCS);
return ERROR_BUSY;
}
pI2C->LastError = ERROR_SUCCESS;
ResetEvent(pI2C->DoneEvent);
rIICSTAT = M_ACTIVE;
// pre-setup write data
pI2C->State = WRITE_DATA;
pI2C->DataCount = 1 + Count; // slave word address + data
pI2C->WordAddr = WordAddr;
pI2C->Flags.WordAddr = TRUE;
pI2C->Data = pData;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -