/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <gpio.h>
#include <i2c.h>
#include <seos.h>
#include <util.h>
#include <gpio.h>
#include <atomicBitset.h>
#include <atomic.h>
#include <platform.h>
#include <plat/inc/cmsis.h>
#include <plat/inc/dma.h>
#include <plat/inc/gpio.h>
#include <plat/inc/i2c.h>
#include <plat/inc/pwr.h>
#include <plat/inc/plat.h>
#include <cpu/inc/barrier.h>
#define I2C_VERBOSE_DEBUG 0
#define I2C_MAX_QUEUE_DEPTH 5
#if I2C_VERBOSE_DEBUG
#define i2c_log_debug(x) osLog(LOG_DEBUG, x "\n")
#else
#define i2c_log_debug(x) do {} while(0)
#endif
#define I2C_CR1_PE (1 << 0)
#define I2C_CR1_SMBUS (1 << 1)
#define I2C_CR1_SMBTYPE (1 << 3)
#define I2C_CR1_ENARP (1 << 4)
#define I2C_CR1_ENPEC (1 << 5)
#define I2C_CR1_ENGC (1 << 6)
#define I2C_CR1_NOSTRETCH (1 << 7)
#define I2C_CR1_START (1 << 8)
#define I2C_CR1_STOP (1 << 9)
#define I2C_CR1_ACK (1 << 10)
#define I2C_CR1_POS (1 << 11)
#define I2C_CR1_PEC (1 << 12)
#define I2C_CR1_ALERT (1 << 13)
#define I2C_CR1_SWRST (1 << 15)
#define I2C_CR2_FREQ(x) ((x) & I2C_CR2_FREQ_MASK)
#define I2C_CR2_FREQ_MASK 0x3F
#define I2C_CR2_ITERREN (1 << 8)
#define I2C_CR2_ITEVTEN (1 << 9)
#define I2C_CR2_ITBUFEN (1 << 10)
#define I2C_CR2_DMAEN (1 << 11)
#define I2C_CR2_LAST (1 << 12)
#define I2C_OAR1_ADD7(x) (((x) & I2C_OAR1_ADD7_MASK) << 1)
#define I2C_OAR1_ADD7_MASK 0x7F
#define I2C_OAR1_ADD10(x) ((x) & I2C_OAR1_ADD10_MASK)
#define I2C_OAR1_ADD10_MASK 0x3FF
#define I2C_OAR1_ADDMODE (1 << 15)
#define I2C_SR1_SB (1 << 0)
#define I2C_SR1_ADDR (1 << 1)
#define I2C_SR1_BTF (1 << 2)
#define I2C_SR1_ADD10 (1 << 3)
#define I2C_SR1_STOPF (1 << 4)
#define I2C_SR1_RXNE (1 << 6)
#define I2C_SR1_TXE (1 << 7)
#define I2C_SR1_BERR (1 << 8)
#define I2C_SR1_ARLO (1 << 9)
#define I2C_SR1_AF (1 << 10)
#define I2C_SR1_OVR (1 << 11)
#define I2C_SR1_PECERR (1 << 12)
#define I2C_SR1_TIMEOUT (1 << 14)
#define I2C_SR1_SMBALERT (1 << 15)
#define I2C_SR2_MSL (1 << 0)
#define I2C_SR2_BUSY (1 << 1)
#define I2C_SR2_TRA (1 << 2)
#define I2C_SR2_GENCALL (1 << 4)
#define I2C_SR2_SMBDEFAULT (1 << 5)
#define I2C_SR2_SMBHOST (1 << 6)
#define I2C_SR2_DUALF (1 << 7)
#define I2C_CCR(x) ((x) & I2C_CCR_MASK)
#define I2C_CCR_MASK 0xFFF
#define I2C_CCR_DUTY_16_9 (1 << 14)
#define I2C_CCR_FM (1 << 15)
#define I2C_TRISE(x) ((x) & I2C_TRISE_MASK)
#define I2C_TRISE_MASK 0x3F
struct StmI2c {
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t OAR1;
volatile uint32_t OAR2;
volatile uint32_t DR;
volatile uint32_t SR1;
volatile uint32_t SR2;
volatile uint32_t CCR;
volatile uint32_t TRISE;
volatile uint32_t FLTR;
};
enum StmI2cSpiMasterState
{
STM_I2C_MASTER_IDLE,
STM_I2C_MASTER_START,
STM_I2C_MASTER_TX_ADDR,
STM_I2C_MASTER_TX_DATA,
STM_I2C_MASTER_RX_ADDR,
STM_I2C_MASTER_RX_DATA,
};
struct I2cStmState {
struct {
union {
uint8_t *buf;
const uint8_t *cbuf;
uint8_t byte;
};
size_t size;
size_t offset;
bool preamble;
I2cCallbackF callback;
void *cookie;
} rx, tx;
enum {
STM_I2C_DISABLED,
STM_I2C_SLAVE,
STM_I2C_MASTER,
} mode;
enum {
STM_I2C_SLAVE_IDLE,
STM_I2C_SLAVE_RX_ARMED,
STM_I2C_SLAVE_RX,
STM_I2C_SLAVE_TX_ARMED,
STM_I2C_SLAVE_TX,
} slaveState;
// StmI2cSpiMasterState
uint8_t masterState;
uint16_t tid;
};
struct StmI2cCfg {
struct StmI2c *regs;
uint32_t clock;
IRQn_Type irqEv;
IRQn_Type irqEr;
};
struct StmI2cDev {
const struct StmI2cCfg *cfg;
const struct StmI2cBoardCfg *board;
struct I2cStmState state;
uint32_t next;
uint32_t last;
struct Gpio *scl;
struct Gpio *sda;
uint8_t addr;
};
static const struct StmI2cCfg mStmI2cCfgs[] = {
[0] = {
.regs = (struct StmI2c *)I2C1_BASE,
.clock = PERIPH_APB1_I2C1,
.irqEv = I2C1_EV_IRQn,
.irqEr = I2C1_ER_IRQn,
},
[1] = {
.regs = (struct StmI2c *)I2C2_BASE,
.clock = PERIPH_APB1_I2C2,
.irqEv = I2C2_EV_IRQn,
.irqEr = I2C2_ER_IRQn,
},
[2] = {
.regs = (struct StmI2c *)I2C3_BASE,
.clock = PERIPH_APB1_I2C3,
.irqEv = I2C3_EV_IRQn,
.irqEr = I2C3_ER_IRQn,
},
};
static struct StmI2cDev mStmI2cDevs[ARRAY_SIZE(mStmI2cCfgs)];
struct StmI2cXfer
{
uint32_t id;
const void *txBuf;
size_t txSize;
void *rxBuf;
size_t rxSize;
I2cCallbackF callback;
void *cookie;
uint8_t busId; /* for us these are both fine in a uint 8 */
uint8_t addr;
};
ATOMIC_BITSET_DECL(mXfersValid, I2C_MAX_QUEUE_DEPTH, static);
static struct StmI2cXfer mXfers[I2C_MAX_QUEUE_DEPTH] = { };
static inline struct StmI2cXfer *stmI2cGetXfer(void)
{
int32_t idx = atomicBitsetFindClearAndSet(mXfersValid);
if (idx < 0)
return NULL;
else
return mXfers + idx;
}
static inline void stmI2cPutXfer(struct StmI2cXfer *xfer)
{
if (xfer)
atomicBitsetClearBit(mXfersValid, xfer - mXfers);
}
static inline void stmI2cAckEnable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR1 |= I2C_CR1_ACK;
}
static inline void stmI2cAckDisable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR1 &= ~I2C_CR1_ACK;
}
static inline void stmI2cDmaEnable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR2 |= I2C_CR2_DMAEN;
}
static inline void stmI2cDmaDisable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR2 &= ~I2C_CR2_DMAEN;
}
static inline void stmI2cStopEnable(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
while (regs->CR1 & (I2C_CR1_STOP | I2C_CR1_START))
;
regs->CR1 |= I2C_CR1_STOP;
}
static inline void stmI2cStartEnable(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
while (regs->CR1 & (I2C_CR1_STOP | I2C_CR1_START))
;
regs->CR1 |= I2C_CR1_START;
}
static inline void stmI2cIrqEnable(struct StmI2cDev *pdev,
uint32_t mask)
{
pdev->cfg->regs->CR2 |= mask;
}
static inline void stmI2cIrqDisable(struct StmI2cDev *pdev,
uint32_t mask)
{
pdev->cfg->regs->CR2 &= ~mask;
}
static inline void stmI2cEnable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR1 |= I2C_CR1_PE;
}
static inline void stmI2cDisable(struct StmI2cDev *pdev)
{
pdev->cfg->regs->CR1 &= ~I2C_CR1_PE;
}
static inline void stmI2cSpeedSet(struct StmI2cDev *pdev,
const uint32_t speed)
{
struct StmI2c *regs = pdev->cfg->regs;
int ccr, ccr_1, ccr_2;
int apb1_clk;
apb1_clk = pwrGetBusSpeed(PERIPH_BUS_APB1);
regs->CR2 = (regs->CR2 & ~I2C_CR2_FREQ_MASK) |
I2C_CR2_FREQ(apb1_clk / 1000000);
if (speed <= 100000) {
ccr = apb1_clk / (speed * 2);
if (ccr < 4)
ccr = 4;
regs->CCR = I2C_CCR(ccr);
regs->TRISE = I2C_TRISE((apb1_clk / 1000000) + 1);
} else if (speed <= 400000) {
ccr_1 = apb1_clk / (speed * 3);
if (ccr_1 == 0 || apb1_clk / (ccr_1 * 3) > speed)
ccr_1 ++;
ccr_2 = apb1_clk / (speed * 25);
if (ccr_2 == 0 || apb1_clk / (ccr_2 * 25) > speed)
ccr_2 ++;
if ((apb1_clk / (ccr_1 * 3)) > (apb1_clk / (ccr_2 * 25)))
regs->CCR = I2C_CCR_FM | I2C_CCR(ccr_1);
else
regs->CCR = I2C_CCR_FM | I2C_CCR_DUTY_16_9 | I2C_CCR(ccr_2);
regs->TRISE = I2C_TRISE(((3*apb1_clk)/10000000) + 1);
}
}
static inline void stmI2cSlaveIdle(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
state->slaveState = STM_I2C_SLAVE_RX_ARMED;
stmI2cAckEnable(pdev);
stmI2cIrqDisable(pdev, I2C_CR2_ITBUFEN | I2C_CR2_ITERREN);
}
static inline void stmI2cInvokeRxCallback(struct I2cStmState *state, size_t tx, size_t rx, int err)
{
uint16_t oldTid = osSetCurrentTid(state->tid);
state->rx.callback(state->rx.cookie, tx, rx, err);
osSetCurrentTid(oldTid);
}
static inline void stmI2cInvokeTxCallback(struct I2cStmState *state, size_t tx, size_t rx, int err)
{
uint16_t oldTid = osSetCurrentTid(state->tid);
state->tx.callback(state->tx.cookie, tx, rx, err);
osSetCurrentTid(oldTid);
}
static inline void stmI2cSlaveRxDone(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
size_t rxOffst = state->rx.offset;
state->rx.offset = 0;
stmI2cInvokeRxCallback(state, 0, rxOffst, 0);
}
static inline void stmI2cSlaveTxDone(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
size_t txOffst = state->tx.offset;
stmI2cSlaveIdle(pdev);
stmI2cInvokeTxCallback(state, txOffst, 0, 0);
}
static void stmI2cSlaveTxNextByte(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
if (state->tx.preamble) {
regs->DR = state->tx.byte;
state->tx.offset++;
} else if (state->tx.offset < state->tx.size) {
regs->DR = state->tx.cbuf[state->tx.offset];
state->tx.offset++;
} else {
state->slaveState = STM_I2C_SLAVE_TX_ARMED;
stmI2cIrqDisable(pdev, I2C_CR2_ITBUFEN);
stmI2cInvokeTxCallback(state, state->tx.offset, 0, 0);
}
}
static void stmI2cSlaveAddrMatched(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
i2c_log_debug("addr");
if (state->slaveState == STM_I2C_SLAVE_RX_ARMED) {
state->slaveState = STM_I2C_SLAVE_RX;
stmI2cIrqEnable(pdev, I2C_CR2_ITBUFEN | I2C_CR2_ITERREN);
} else if (state->slaveState == STM_I2C_SLAVE_TX) {
stmI2cIrqEnable(pdev, I2C_CR2_ITBUFEN | I2C_CR2_ITERREN);
}
/* clear ADDR by doing a dummy reads from SR1 (already read) then SR2 */
(void)regs->SR2;
}
static void stmI2cSlaveStopRxed(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
i2c_log_debug("stopf");
(void)regs->SR1;
stmI2cEnable(pdev);
/* clear STOPF by doing a dummy read from SR1 and strobing the PE bit */
stmI2cSlaveIdle(pdev);
stmI2cSlaveRxDone(pdev);
}
static inline void stmI2cSlaveRxBufNotEmpty(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
uint8_t data = regs->DR;
i2c_log_debug("rxne");
if (state->rx.offset < state->rx.size) {
state->rx.buf[state->rx.offset] = data;
state->rx.offset++;
} else {
stmI2cAckDisable(pdev);
/* TODO: error on overflow */
}
}
static void stmI2cSlaveTxBufEmpty(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
i2c_log_debug("txe");
if (state->slaveState == STM_I2C_SLAVE_RX) {
state->slaveState = STM_I2C_SLAVE_TX_ARMED;
stmI2cIrqDisable(pdev, I2C_CR2_ITBUFEN);
stmI2cAckDisable(pdev);
stmI2cSlaveRxDone(pdev);
/* stmI2cTxNextByte() will happen when the task provides a
TX buffer; the I2C controller will stretch the clock until then */
} else {
stmI2cSlaveTxNextByte(pdev);
}
}
static void stmI2cSlaveNakRxed(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
i2c_log_debug("af");
if (state->slaveState == STM_I2C_SLAVE_TX) {
state->tx.offset--;
/* NACKs seem to be preceded by a spurious TXNE, so adjust the offset to
compensate (the corresponding byte written to DR was never actually
transmitted) */
stmI2cSlaveTxDone(pdev);
}
regs->SR1 &= ~I2C_SR1_AF;
}
static inline void stmI2cMasterTxRxDone(struct StmI2cDev *pdev, int err)
{
struct I2cStmState *state = &pdev->state;
size_t txOffst = state->tx.offset;
size_t rxOffst = state->rx.offset;
uint32_t id;
int i;
struct StmI2cXfer *xfer;
if (pdev->board->sleepDev >= 0)
platReleaseDevInSleepMode(pdev->board->sleepDev);
state->tx.offset = 0;
state->rx.offset = 0;
stmI2cInvokeTxCallback(state, txOffst, rxOffst, err);
do {
id = atomicAdd32bits(&pdev->next, 1);
} while (!id);
for (i=0; i<I2C_MAX_QUEUE_DEPTH; i++) {
xfer = &mXfers[i];
if (xfer->busId == (pdev - mStmI2cDevs) &&
atomicCmpXchg32bits(&xfer->id, id, 0)) {
pdev->addr = xfer->addr;
state->tx.cbuf = xfer->txBuf;
state->tx.offset = 0;
state->tx.size = xfer->txSize;
state->tx.callback = xfer->callback;
state->tx.cookie = xfer->cookie;
state->rx.buf = xfer->rxBuf;
state->rx.offset = 0;
state->rx.size = xfer->rxSize;
state->rx.callback = NULL;
state->rx.cookie = NULL;
atomicWriteByte(&state->masterState, STM_I2C_MASTER_START);
if (pdev->board->sleepDev >= 0)
platRequestDevInSleepMode(pdev->board->sleepDev, 12);
stmI2cPutXfer(xfer);
stmI2cStartEnable(pdev);
return;
}
}
atomicWriteByte(&state->masterState, STM_I2C_MASTER_IDLE);
}
static void stmI2cMasterDmaTxDone(void *cookie, uint16_t bytesLeft, int err)
{
struct StmI2cDev *pdev = cookie;
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
state->tx.offset = state->tx.size - bytesLeft;
state->tx.size = 0;
stmI2cDmaDisable(pdev);
if (err == 0 && state->rx.size > 0) {
atomicWriteByte(&state->masterState, STM_I2C_MASTER_START);
stmI2cStartEnable(pdev);
} else {
while (!(regs->SR1 & I2C_SR1_BTF))
;
stmI2cStopEnable(pdev);
stmI2cMasterTxRxDone(pdev, err);
}
}
static void stmI2cMasterDmaRxDone(void *cookie, uint16_t bytesLeft, int err)
{
struct StmI2cDev *pdev = cookie;
struct I2cStmState *state = &pdev->state;
state->rx.offset = state->rx.size - bytesLeft;
state->rx.size = 0;
stmI2cDmaDisable(pdev);
stmI2cStopEnable(pdev);
stmI2cMasterTxRxDone(pdev, err);
}
static inline void stmI2cMasterDmaCancel(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
dmaStop(I2C_DMA_BUS, pdev->board->dmaRx.stream);
state->rx.offset = state->rx.size - dmaBytesLeft(I2C_DMA_BUS,
pdev->board->dmaRx.stream);
dmaStop(I2C_DMA_BUS, pdev->board->dmaTx.stream);
state->tx.offset = state->tx.size - dmaBytesLeft(I2C_DMA_BUS,
pdev->board->dmaTx.stream);
stmI2cDmaDisable(pdev);
}
static inline void stmI2cMasterStartDma(struct StmI2cDev *pdev,
const struct StmI2cDmaCfg *dmaCfg, const void *buf,
size_t size, DmaCallbackF callback, bool rx, bool last)
{
struct StmI2c *regs = pdev->cfg->regs;
struct dmaMode mode;
memset(&mode, 0, sizeof(mode));
mode.priority = DMA_PRIORITY_HIGH;
mode.direction = rx ? DMA_DIRECTION_PERIPH_TO_MEM :
DMA_DIRECTION_MEM_TO_PERIPH;
mode.periphAddr = (uintptr_t)®s->DR;
mode.minc = true;
mode.channel = dmaCfg->channel;
dmaStart(I2C_DMA_BUS, dmaCfg->stream, buf, size, &mode, callback, pdev);
if (last)
stmI2cIrqEnable(pdev, I2C_CR2_LAST);
else
stmI2cIrqDisable(pdev, I2C_CR2_LAST);
stmI2cDmaEnable(pdev);
}
static void stmI2cMasterSentStart(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
if (atomicReadByte(&state->masterState) == STM_I2C_MASTER_START) {
if (state->tx.size > 0) {
atomicWriteByte(&state->masterState, STM_I2C_MASTER_TX_ADDR);
regs->DR = pdev->addr << 1;
} else {
atomicWriteByte(&state->masterState, STM_I2C_MASTER_RX_ADDR);
stmI2cAckEnable(pdev);
regs->DR = (pdev->addr << 1) | 0x01;
}
}
}
static void stmI2cMasterSentAddr(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
uint8_t masterState = atomicReadByte(&state->masterState);
if (masterState == STM_I2C_MASTER_TX_ADDR) {
stmI2cMasterStartDma(pdev, &pdev->board->dmaTx, state->tx.cbuf,
state->tx.size, stmI2cMasterDmaTxDone, false, !!state->rx.size);
regs->SR2; // Clear ADDR
atomicWriteByte(&state->masterState, STM_I2C_MASTER_TX_DATA);
} else if (masterState == STM_I2C_MASTER_RX_ADDR) {
if (state->rx.size == 1) // Generate NACK here for 1 byte transfers
stmI2cAckDisable(pdev);
stmI2cMasterStartDma(pdev, &pdev->board->dmaRx, state->rx.buf,
state->rx.size, stmI2cMasterDmaRxDone, true,
state->rx.size > 1);
regs->SR2; // Clear ADDR
atomicWriteByte(&state->masterState, STM_I2C_MASTER_RX_DATA);
}
}
static void stmI2cMasterNakRxed(struct StmI2cDev *pdev)
{
struct I2cStmState *state = &pdev->state;
struct StmI2c *regs = pdev->cfg->regs;
uint8_t masterState = atomicReadByte(&state->masterState);
int err = 0;
if (masterState == STM_I2C_MASTER_TX_ADDR ||
masterState == STM_I2C_MASTER_RX_ADDR) {
err = -ENXIO;
} else if (masterState == STM_I2C_MASTER_TX_DATA ||
masterState == STM_I2C_MASTER_RX_DATA) {
stmI2cMasterDmaCancel(pdev);
err = -EIO;
}
if (err) {
regs->SR1 &= ~I2C_SR1_AF;
stmI2cStopEnable(pdev);
stmI2cMasterTxRxDone(pdev, err);
}
}
static void stmI2cMasterBusError(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
stmI2cMasterDmaCancel(pdev);
regs->SR1 &= ~I2C_SR1_BERR;
stmI2cMasterTxRxDone(pdev, -EIO);
}
static void stmI2cMasterArbitrationLoss(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
stmI2cMasterDmaCancel(pdev);
regs->SR1 &= ~I2C_SR1_ARLO;
stmI2cMasterTxRxDone(pdev, -EBUSY);
}
static void stmI2cMasterUnexpectedError(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
osLog(LOG_ERROR, "Unexpected I2C ERR interrupt: SR1 = %04lX, SR2 = %04lX\n",
regs->SR1, regs->SR2);
stmI2cMasterDmaCancel(pdev);
regs->SR1 = 0;
stmI2cMasterTxRxDone(pdev, -EIO);
}
static void stmI2cIsrEvent(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
uint16_t sr1 = regs->SR1;
if (pdev->state.mode == STM_I2C_SLAVE) {
if (sr1 & I2C_SR1_ADDR) {
stmI2cSlaveAddrMatched(pdev);
} else if (sr1 & I2C_SR1_RXNE) {
stmI2cSlaveRxBufNotEmpty(pdev);
} else if (sr1 & I2C_SR1_TXE) {
stmI2cSlaveTxBufEmpty(pdev);
} else if (sr1 & I2C_SR1_BTF) {
if (regs->SR2 & I2C_SR2_TRA)
stmI2cSlaveTxBufEmpty(pdev);
else
stmI2cSlaveRxBufNotEmpty(pdev);
} else if (sr1 & I2C_SR1_STOPF) {
stmI2cSlaveStopRxed(pdev);
}
/* TODO: other flags */
} else if (pdev->state.mode == STM_I2C_MASTER) {
if (sr1 & I2C_SR1_SB)
stmI2cMasterSentStart(pdev);
else if (sr1 & I2C_SR1_ADDR)
stmI2cMasterSentAddr(pdev);
}
}
static void stmI2cIsrError(struct StmI2cDev *pdev)
{
struct StmI2c *regs = pdev->cfg->regs;
uint16_t sr1 = regs->SR1;
if (pdev->state.mode == STM_I2C_SLAVE) {
if (sr1 & I2C_SR1_AF)
stmI2cSlaveNakRxed(pdev);
/* TODO: other flags */
} else if (pdev->state.mode == STM_I2C_MASTER) {
if (sr1 & I2C_SR1_AF)
stmI2cMasterNakRxed(pdev);
else if (sr1 & I2C_SR1_BERR)
stmI2cMasterBusError(pdev);
else if (sr1 & I2C_SR1_ARLO)
stmI2cMasterArbitrationLoss(pdev);
else
stmI2cMasterUnexpectedError(pdev);
}
}
#define DECLARE_IRQ_HANDLERS(_n) \
extern void I2C##_n##_EV_IRQHandler(); \
extern void I2C##_n##_ER_IRQHandler(); \
\
extern void I2C##_n##_EV_IRQHandler() \
{ \
stmI2cIsrEvent(&mStmI2cDevs[_n - 1]); \
} \
\
extern void I2C##_n##_ER_IRQHandler() \
{ \
stmI2cIsrError(&mStmI2cDevs[_n - 1]); \
}
DECLARE_IRQ_HANDLERS(1);
DECLARE_IRQ_HANDLERS(3);
static inline struct Gpio* stmI2cGpioInit(const struct StmI2cBoardCfg *board, const struct StmI2cGpioCfg *cfg)
{
struct Gpio* gpio = gpioRequest(cfg->gpioNum);
gpioConfigAlt(gpio, board->gpioSpeed, board->gpioPull, GPIO_OUT_OPEN_DRAIN,
cfg->func);
return gpio;
}
int i2cMasterRequest(uint32_t busId, uint32_t speed)
{
if (busId >= ARRAY_SIZE(mStmI2cDevs))
return -EINVAL;
const struct StmI2cBoardCfg *board = boardStmI2cCfg(busId);
if (!board)
return -EINVAL;
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
struct I2cStmState *state = &pdev->state;
const struct StmI2cCfg *cfg = &mStmI2cCfgs[busId];
if (state->mode == STM_I2C_DISABLED) {
state->mode = STM_I2C_MASTER;
pdev->cfg = cfg;
pdev->board = board;
pdev->next = 2;
pdev->last = 1;
atomicBitsetInit(mXfersValid, I2C_MAX_QUEUE_DEPTH);
pdev->scl = stmI2cGpioInit(board, &board->gpioScl);
pdev->sda = stmI2cGpioInit(board, &board->gpioSda);
pwrUnitClock(PERIPH_BUS_APB1, cfg->clock, true);
stmI2cDisable(pdev);
pwrUnitReset(PERIPH_BUS_APB1, cfg->clock, true);
pwrUnitReset(PERIPH_BUS_APB1, cfg->clock, false);
stmI2cIrqEnable(pdev, I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
stmI2cSpeedSet(pdev, speed);
atomicWriteByte(&state->masterState, STM_I2C_MASTER_IDLE);
NVIC_EnableIRQ(cfg->irqEr);
NVIC_EnableIRQ(cfg->irqEv);
stmI2cEnable(pdev);
return 0;
} else {
return -EBUSY;
}
}
int i2cMasterRelease(uint32_t busId)
{
if (busId >= ARRAY_SIZE(mStmI2cDevs))
return -EINVAL;
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
struct I2cStmState *state = &pdev->state;
const struct StmI2cCfg *cfg = pdev->cfg;
if (state->mode == STM_I2C_MASTER) {
if (atomicReadByte(&state->masterState) == STM_I2C_MASTER_IDLE) {
state->mode = STM_I2C_DISABLED;
stmI2cIrqEnable(pdev, I2C_CR2_ITERREN | I2C_CR2_ITEVTEN);
stmI2cDisable(pdev);
pwrUnitClock(PERIPH_BUS_APB1, cfg->clock, false);
gpioRelease(pdev->scl);
gpioRelease(pdev->sda);
return 0;
} else {
return -EBUSY;
}
} else {
return -EINVAL;
}
}
int i2cMasterTxRx(uint32_t busId, uint32_t addr,
const void *txBuf, size_t txSize, void *rxBuf, size_t rxSize,
I2cCallbackF callback, void *cookie)
{
uint32_t id;
if (busId >= ARRAY_SIZE(mStmI2cDevs))
return -EINVAL;
else if (addr & 0x80)
return -ENXIO;
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
struct I2cStmState *state = &pdev->state;
if (state->mode != STM_I2C_MASTER)
return -EINVAL;
struct StmI2cXfer *xfer = stmI2cGetXfer();
if (xfer) {
xfer->busId = busId;
xfer->addr = addr;
xfer->txBuf = txBuf;
xfer->txSize = txSize;
xfer->rxBuf = rxBuf;
xfer->rxSize = rxSize;
xfer->callback = callback;
xfer->cookie = cookie;
do {
id = atomicAdd32bits(&pdev->last, 1);
} while (!id);
// after this point the transfer can be picked up by the transfer
// complete interrupt
atomicWrite32bits(&xfer->id, id);
// only initiate transfer here if we are in IDLE. Otherwise the transfer
// completion interrupt will start the next transfer (not necessarily
// this one)
if (atomicCmpXchgByte((uint8_t *)&state->masterState,
STM_I2C_MASTER_IDLE, STM_I2C_MASTER_START)) {
// it is possible for this transfer to already be complete by the
// time we get here. if so, transfer->id will have been set to 0.
if (atomicCmpXchg32bits(&xfer->id, id, 0)) {
pdev->addr = xfer->addr;
state->tx.cbuf = xfer->txBuf;
state->tx.offset = 0;
state->tx.size = xfer->txSize;
state->tx.callback = xfer->callback;
state->tx.cookie = xfer->cookie;
state->rx.buf = xfer->rxBuf;
state->rx.offset = 0;
state->rx.size = xfer->rxSize;
state->rx.callback = NULL;
state->rx.cookie = NULL;
state->tid = osGetCurrentTid();
if (pdev->board->sleepDev >= 0)
platRequestDevInSleepMode(pdev->board->sleepDev, 12);
stmI2cPutXfer(xfer);
stmI2cStartEnable(pdev);
}
}
return 0;
} else {
return -EBUSY;
}
}
int i2cSlaveRequest(uint32_t busId, uint32_t addr)
{
if (busId >= ARRAY_SIZE(mStmI2cDevs))
return -EINVAL;
const struct StmI2cBoardCfg *board = boardStmI2cCfg(busId);
if (!board)
return -EINVAL;
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
const struct StmI2cCfg *cfg = &mStmI2cCfgs[busId];
if (pdev->state.mode == STM_I2C_DISABLED) {
pdev->state.mode = STM_I2C_SLAVE;
pdev->addr = addr;
pdev->cfg = cfg;
pdev->board = board;
pdev->scl = stmI2cGpioInit(board, &board->gpioScl);
pdev->sda = stmI2cGpioInit(board, &board->gpioSda);
return 0;
} else {
return -EBUSY;
}
}
int i2cSlaveRelease(uint32_t busId)
{
if (busId >= ARRAY_SIZE(mStmI2cDevs))
return -EINVAL;
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
const struct StmI2cCfg *cfg = pdev->cfg;
if (pdev->state.mode == STM_I2C_SLAVE) {
pdev->state.mode = STM_I2C_DISABLED;
stmI2cIrqDisable(pdev, I2C_CR2_ITERREN | I2C_CR2_ITEVTEN);
stmI2cAckDisable(pdev);
stmI2cDisable(pdev);
pwrUnitClock(PERIPH_BUS_APB1, cfg->clock, false);
gpioRelease(pdev->scl);
gpioRelease(pdev->sda);
return 0;
} else {
return -EBUSY;
}
}
void i2cSlaveEnableRx(uint32_t busId, void *rxBuf, size_t rxSize,
I2cCallbackF callback, void *cookie)
{
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
const struct StmI2cCfg *cfg = pdev->cfg;
struct I2cStmState *state = &pdev->state;
if (pdev->state.mode == STM_I2C_SLAVE) {
state->rx.buf = rxBuf;
state->rx.offset = 0;
state->rx.size = rxSize;
state->rx.callback = callback;
state->rx.cookie = cookie;
state->slaveState = STM_I2C_SLAVE_RX_ARMED;
state->tid = osGetCurrentTid();
pwrUnitClock(PERIPH_BUS_APB1, cfg->clock, true);
pwrUnitReset(PERIPH_BUS_APB1, cfg->clock, true);
pwrUnitReset(PERIPH_BUS_APB1, cfg->clock, false);
NVIC_EnableIRQ(cfg->irqEr);
NVIC_EnableIRQ(cfg->irqEv);
stmI2cEnable(pdev);
cfg->regs->OAR1 = I2C_OAR1_ADD7(pdev->addr);
stmI2cIrqEnable(pdev, I2C_CR2_ITERREN | I2C_CR2_ITEVTEN);
stmI2cAckEnable(pdev);
}
}
static int i2cSlaveTx(uint32_t busId, const void *txBuf, uint8_t byte,
size_t txSize, I2cCallbackF callback, void *cookie)
{
struct StmI2cDev *pdev = &mStmI2cDevs[busId];
struct I2cStmState *state = &pdev->state;
if (pdev->state.mode == STM_I2C_SLAVE) {
if (state->slaveState == STM_I2C_SLAVE_RX)
return -EBUSY;
if (txBuf) {
state->tx.cbuf = txBuf;
state->tx.preamble = false;
} else {
state->tx.byte = byte;
state->tx.preamble = true;
}
state->tx.offset = 0;
state->tx.size = txSize;
state->tx.callback = callback;
state->tx.cookie = cookie;
if (state->slaveState == STM_I2C_SLAVE_TX_ARMED) {
state->slaveState = STM_I2C_SLAVE_TX;
stmI2cSlaveTxNextByte(pdev);
stmI2cIrqEnable(pdev, I2C_CR2_ITBUFEN);
} else {
state->slaveState = STM_I2C_SLAVE_TX;
}
return 0;
} else {
return -EBUSY;
}
}
int i2cSlaveTxPreamble(uint32_t busId, uint8_t byte, I2cCallbackF callback,
void *cookie)
{
return i2cSlaveTx(busId, NULL, byte, 0, callback, cookie);
}
int i2cSlaveTxPacket(uint32_t busId, const void *txBuf, size_t txSize,
I2cCallbackF callback, void *cookie)
{
return i2cSlaveTx(busId, txBuf, 0, txSize, callback, cookie);
}