123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- /*++
- Copyright (c) 2014 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- i2c.c
- Abstract:
- This module implements I2C bus support for OMAP3 and OMAP4 SoCs. This
- file should be removed when firmware enables this hardware.
- Author:
- Evan Green 12-Mar-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "sdomap4.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- #define OMAP_I2C_READ_REGISTER(_Register) \
- HlReadRegister32(OmapI2cBase + (_Register))
- #define OMAP_I2C_WRITE_REGISTER(_Register, _Value) \
- HlWriteRegister32(OmapI2cBase + (_Register), (_Value))
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define OMAP44XX_L4_PER_BASE 0x48000000
- #define I2C_BASE (OMAP44XX_L4_PER_BASE + 0x70000)
- #define I2C_SIZE 0x1000
- #define I2C_BUSY_RETRY_COUNT 10000
- #define I2C_STATUS_RETRY_COUNT 10000
- //
- // Define the I2C timeout in seconds.
- //
- #define I2C_TIMEOUT 1
- //
- // Control register bit definitions.
- //
- #define OMAP_I2C_CONTROL_ENABLE (1 << 15)
- #define OMAP_I2C_CONTROL_MASTER (1 << 10)
- #define OMAP_I2C_CONTROL_TRANSMIT (1 << 9)
- #define OMAP_I2C_CONTROL_STOP_CONDITION (1 << 1)
- #define OMAP_I2C_CONTROL_START_CONDITION (1 << 0)
- //
- // Interrupt bit definitions.
- //
- #define OMAP_I2C_INTERRUPT_ARBITRATION_LOST (1 << 0)
- #define OMAP_I2C_INTERRUPT_NACK (1 << 1)
- #define OMAP_I2C_INTERRUPT_ACCESS_READY (1 << 2)
- #define OMAP_I2C_INTERRUPT_RECEIVE_READY (1 << 3)
- #define OMAP_I2C_INTERRUPT_TRANSMIT_READY (1 << 4)
- #define OMAP_I2C_STATUS_BUSY (1 << 12)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _OMAP_I2C_REGISTER {
- OmapI2cRevisionLow = 0x00,
- OmapI2cRevisionHigh = 0x04,
- OmapI2cSystemControl = 0x10,
- OmapI2cInterruptStatusRaw = 0x24,
- OmapI2cInterruptStatus = 0x28,
- OmapI2cInterruptEnableSet = 0x2C,
- OmapI2cInterruptEnableClear = 0x30,
- OmapI2cWakeupEnable = 0x34,
- OmapI2cDmaReceiveEnableSet = 0x38,
- OmapI2cDmaTransmitEnableSet = 0x3C,
- OmapI2cDmaReceiveEnableClear = 0x40,
- OmapI2cDmaTransmitEnableClear = 0x44,
- OmapI2cDmaReceiveWakeEnable = 0x48,
- OmapI2cDmaTransmitWakeEnable = 0x4C,
- OmapI2cInterruptEnableLegacy = 0x84,
- OmapI2cInterruptStatusLegacy = 0x88,
- OmapI2cSystemStatus = 0x90,
- OmapI2cBufferConfiguration = 0x94,
- OmapI2cCount = 0x98,
- OmapI2cData = 0x9C,
- OmapI2cControl = 0xA4,
- OmapI2cOwnAddress = 0xA8,
- OmapI2cSlaveAddress = 0xAC,
- OmapI2cPrescaler = 0xB0,
- OmapI2cClockLowTime = 0xB4,
- OmapI2cClockHighTime = 0xB8,
- OmapI2cSystemTest = 0xBC,
- OmapI2cBufferStatus = 0xC0,
- OmapI2cOwnAddress1 = 0xC4,
- OmapI2cOwnAddress2 = 0xC8,
- OmapI2cOwnAddress3 = 0xCC,
- OmapI2cActiveOwnAddress = 0xD0,
- OmapI2cClockBlockingEnable = 0xD4
- } OMAP_I2C_REGISTER, *POMAP_I2C_REGISTER;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- OmapI2cWaitForBusyBit (
- VOID
- );
- KSTATUS
- OmapI2cWaitForEvent (
- ULONG Mask
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- PVOID OmapI2cBase;
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- OmapI2cInitialize (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes the I2C device.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- ULONG Value;
- if (OmapI2cBase == NULL) {
- OmapI2cBase = MmMapPhysicalAddress(I2C_BASE,
- I2C_SIZE,
- TRUE,
- FALSE,
- TRUE);
- }
- ASSERT(OmapI2cBase != NULL);
- //
- // Set up the divisors.
- //
- OMAP_I2C_WRITE_REGISTER(OmapI2cPrescaler, 0);
- OMAP_I2C_WRITE_REGISTER(OmapI2cClockLowTime, 0x35);
- OMAP_I2C_WRITE_REGISTER(OmapI2cClockHighTime, 0x35);
- //
- // Take the i2c controller out of reset.
- //
- Value = OMAP_I2C_READ_REGISTER(OmapI2cControl);
- Value |= OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER;
- OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
- //
- // Enable interrupts to be able to get their status.
- //
- Value = OMAP_I2C_INTERRUPT_NACK |
- OMAP_I2C_INTERRUPT_ACCESS_READY |
- OMAP_I2C_INTERRUPT_RECEIVE_READY |
- OMAP_I2C_INTERRUPT_TRANSMIT_READY;
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptEnableLegacy, Value);
- HlBusySpin(1000);
- OmapI2cFlushData();
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
- OMAP_I2C_WRITE_REGISTER(OmapI2cCount, 0);
- return;
- }
- VOID
- OmapI2cFlushData (
- VOID
- )
- /*++
- Routine Description:
- This routine flushes extraneous data out of the internal FIFOs.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- ULONG Status;
- while (TRUE) {
- Status = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
- if ((Status & OMAP_I2C_INTERRUPT_RECEIVE_READY) != 0) {
- OMAP_I2C_READ_REGISTER(OmapI2cData);
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_RECEIVE_READY);
- HlBusySpin(1000);
- } else {
- break;
- }
- }
- return;
- }
- KSTATUS
- OmapI2cWrite (
- UCHAR Chip,
- ULONG Address,
- ULONG AddressLength,
- PVOID Buffer,
- ULONG Length
- )
- /*++
- Routine Description:
- This routine writes the given buffer out to the given i2c device.
- Arguments:
- Chip - Supplies the device to write to.
- Address - Supplies the address.
- AddressLength - Supplies the width of the address. Valid values are zero
- through two.
- Buffer - Supplies the buffer containing the data to write.
- Length - Supplies the length of the buffer in bytes.
- Return Value:
- Status code.
- --*/
- {
- PUCHAR Bytes;
- ULONG DataIndex;
- KSTATUS Status;
- ULONG Value;
- ASSERT((Buffer != NULL) && (AddressLength <= 2) &&
- (Address + Length < 0x10000));
- Status = OmapI2cWaitForBusyBit();
- if (!KSUCCESS(Status)) {
- return Status;
- }
- OMAP_I2C_WRITE_REGISTER(OmapI2cCount, AddressLength + Length);
- OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
- Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
- OMAP_I2C_CONTROL_START_CONDITION |
- OMAP_I2C_CONTROL_STOP_CONDITION | OMAP_I2C_CONTROL_TRANSMIT;
- OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
- while (AddressLength != 0) {
- Status = OmapI2cWaitForEvent(OMAP_I2C_INTERRUPT_TRANSMIT_READY);
- if (!KSUCCESS(Status)) {
- goto I2cWriteEnd;
- }
- AddressLength -= 1;
- Value = (Address >> (AddressLength * BITS_PER_BYTE)) & 0xFF;
- OMAP_I2C_WRITE_REGISTER(OmapI2cData, Value);
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_TRANSMIT_READY);
- }
- Bytes = Buffer;
- for (DataIndex = 0; DataIndex < Length; DataIndex += 1) {
- Status = OmapI2cWaitForEvent(OMAP_I2C_INTERRUPT_TRANSMIT_READY);
- if (!KSUCCESS(Status)) {
- goto I2cWriteEnd;
- }
- OMAP_I2C_WRITE_REGISTER(OmapI2cData, Bytes[DataIndex]);
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_TRANSMIT_READY);
- }
- Status = STATUS_SUCCESS;
- I2cWriteEnd:
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
- return Status;
- }
- KSTATUS
- OmapI2cRead (
- UCHAR Chip,
- ULONG Address,
- ULONG AddressLength,
- PVOID Buffer,
- ULONG Length
- )
- /*++
- Routine Description:
- This routine reads from the given i2c device into the given buffer.
- Arguments:
- Chip - Supplies the device to read from.
- Address - Supplies the address.
- AddressLength - Supplies the width of the address. Valid values are zero
- through two.
- Buffer - Supplies a pointer to the buffer where the read data will be
- returned.
- Length - Supplies the length of the buffer in bytes.
- Return Value:
- Status code.
- --*/
- {
- PUCHAR Bytes;
- ULONG DataIndex;
- ULONG InterruptStatus;
- ULONG Mask;
- KSTATUS Status;
- ULONG Value;
- ASSERT((Buffer != NULL) && (AddressLength <= 2) &&
- (Address + Length < 0x10000));
- Status = OmapI2cWaitForBusyBit();
- if (!KSUCCESS(Status)) {
- return Status;
- }
- OMAP_I2C_WRITE_REGISTER(OmapI2cCount, AddressLength);
- OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
- Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
- OMAP_I2C_CONTROL_START_CONDITION |
- OMAP_I2C_CONTROL_STOP_CONDITION | OMAP_I2C_CONTROL_TRANSMIT;
- OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
- Mask = OMAP_I2C_INTERRUPT_TRANSMIT_READY |
- OMAP_I2C_INTERRUPT_ACCESS_READY;
- while (TRUE) {
- Status = OmapI2cWaitForEvent(Mask);
- if (!KSUCCESS(Status)) {
- goto I2cReadEnd;
- }
- InterruptStatus = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
- if (AddressLength != 0) {
- if ((InterruptStatus & OMAP_I2C_INTERRUPT_TRANSMIT_READY) != 0) {
- AddressLength -= 1;
- Value = (Address >> (AddressLength * BITS_PER_BYTE)) & 0xFF;
- OMAP_I2C_WRITE_REGISTER(OmapI2cData, Value);
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_TRANSMIT_READY);
- }
- }
- if ((InterruptStatus & OMAP_I2C_INTERRUPT_ACCESS_READY) != 0) {
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_ACCESS_READY);
- break;
- }
- }
- Status = OmapI2cWaitForBusyBit();
- if (!KSUCCESS(Status)) {
- goto I2cReadEnd;
- }
- OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
- OMAP_I2C_WRITE_REGISTER(OmapI2cCount, Length);
- Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
- OMAP_I2C_CONTROL_START_CONDITION |
- OMAP_I2C_CONTROL_STOP_CONDITION;
- OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
- Bytes = Buffer;
- Mask = OMAP_I2C_INTERRUPT_RECEIVE_READY |
- OMAP_I2C_INTERRUPT_ACCESS_READY;
- DataIndex = 0;
- while (DataIndex < Length) {
- Status = OmapI2cWaitForEvent(Mask);
- if (!KSUCCESS(Status)) {
- goto I2cReadEnd;
- }
- InterruptStatus = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
- if ((InterruptStatus & OMAP_I2C_INTERRUPT_RECEIVE_READY) != 0) {
- Bytes[DataIndex] = OMAP_I2C_READ_REGISTER(OmapI2cData);
- DataIndex += 1;
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_RECEIVE_READY);
- }
- if ((InterruptStatus & OMAP_I2C_INTERRUPT_ACCESS_READY) != 0) {
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
- OMAP_I2C_INTERRUPT_ACCESS_READY);
- }
- }
- Status = STATUS_SUCCESS;
- I2cReadEnd:
- OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- OmapI2cWaitForBusyBit (
- VOID
- )
- /*++
- Routine Description:
- This routine waits for the busy bit to clear.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- ULONGLONG Timeout;
- Timeout = KeGetRecentTimeCounter() +
- (HlQueryTimeCounterFrequency() * I2C_TIMEOUT);
- do {
- if ((OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy) &
- OMAP_I2C_STATUS_BUSY) == 0) {
- return STATUS_SUCCESS;
- }
- } while (KeGetRecentTimeCounter() <= Timeout);
- return STATUS_TIMEOUT;
- }
- KSTATUS
- OmapI2cWaitForEvent (
- ULONG Mask
- )
- /*++
- Routine Description:
- This routine waits for the busy bit to clear.
- Arguments:
- Mask - Supplies the mask to wait to become non-zero.
- Return Value:
- Status code.
- --*/
- {
- ULONG Status;
- ULONGLONG Timeout;
- Timeout = KeGetRecentTimeCounter() +
- (HlQueryTimeCounterFrequency() * I2C_TIMEOUT);
- do {
- Status = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
- if ((Status & Mask) != 0) {
- return STATUS_SUCCESS;
- }
- } while (KeGetRecentTimeCounter() <= Timeout);
- return STATUS_TIMEOUT;
- }
|