mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-25 22:35:42 +00:00
nxp: i2c driver support.
NXP I2C driver support for NXP SoC(s). Signed-off-by: York Sun <york.sun@nxp.com> Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com> Change-Id: I234b76f9fa1b30dd13aa087001411370cc6c8dd0
This commit is contained in:
parent
d8e9799921
commit
c6d9fdbc78
3 changed files with 334 additions and 0 deletions
257
drivers/nxp/i2c/i2c.c
Normal file
257
drivers/nxp/i2c/i2c.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright 2016-2020 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include "i2c.h"
|
||||
#include <nxp_timer.h>
|
||||
|
||||
static uintptr_t g_nxp_i2c_addr;
|
||||
|
||||
void i2c_init(uintptr_t nxp_i2c_addr)
|
||||
{
|
||||
struct ls_i2c *ccsr_i2c = (void *)nxp_i2c_addr;
|
||||
|
||||
g_nxp_i2c_addr = nxp_i2c_addr;
|
||||
/* Presume workaround for erratum a009203 applied */
|
||||
i2c_out(&ccsr_i2c->cr, I2C_CR_DIS);
|
||||
i2c_out(&ccsr_i2c->fd, I2C_FD_CONSERV);
|
||||
i2c_out(&ccsr_i2c->sr, I2C_SR_RST);
|
||||
i2c_out(&ccsr_i2c->cr, I2C_CR_EN);
|
||||
}
|
||||
|
||||
static int wait_for_state(struct ls_i2c *ccsr_i2c,
|
||||
unsigned char state, unsigned char mask)
|
||||
{
|
||||
unsigned char sr;
|
||||
uint64_t start_time = get_timer_val(0);
|
||||
uint64_t timer;
|
||||
|
||||
do {
|
||||
sr = i2c_in(&ccsr_i2c->sr);
|
||||
if (sr & I2C_SR_AL) {
|
||||
i2c_out(&ccsr_i2c->sr, sr);
|
||||
WARN("I2C arbitration lost\n");
|
||||
return -EIO;
|
||||
}
|
||||
if ((sr & mask) == state) {
|
||||
return (int)sr;
|
||||
}
|
||||
|
||||
timer = get_timer_val(start_time);
|
||||
if (timer > I2C_TIMEOUT)
|
||||
break;
|
||||
mdelay(1);
|
||||
} while (1);
|
||||
WARN("I2C: Timeout waiting for state 0x%x, sr = 0x%x\n", state, sr);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int tx_byte(struct ls_i2c *ccsr_i2c, unsigned char c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
|
||||
i2c_out(&ccsr_i2c->dr, c);
|
||||
ret = wait_for_state(ccsr_i2c, I2C_SR_IF, I2C_SR_IF);
|
||||
if (ret < 0) {
|
||||
WARN("%s: state error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
if (ret & I2C_SR_RX_NAK) {
|
||||
WARN("%s: nodev\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gen_stop(struct ls_i2c *ccsr_i2c)
|
||||
{
|
||||
unsigned char cr;
|
||||
int ret;
|
||||
|
||||
cr = i2c_in(&ccsr_i2c->cr);
|
||||
cr &= ~(I2C_CR_MA | I2C_CR_TX);
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
ret = wait_for_state(ccsr_i2c, I2C_SR_IDLE, I2C_SR_BB);
|
||||
if (ret < 0) {
|
||||
WARN("I2C: Generating stop failed.\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_write_addr(struct ls_i2c *ccsr_i2c, unsigned char chip,
|
||||
int addr, int alen)
|
||||
{
|
||||
int ret;
|
||||
unsigned char cr;
|
||||
|
||||
if (alen != 1) {
|
||||
WARN("I2C: Unsupported address len [%d]\n", alen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (i2c_in(&ccsr_i2c->ad) == (chip << 1)) {
|
||||
WARN("I2C: slave address same as self\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
|
||||
ret = wait_for_state(ccsr_i2c, I2C_SR_IDLE, I2C_SR_BB);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cr = i2c_in(&ccsr_i2c->cr);
|
||||
cr |= I2C_CR_MA;
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
ret = wait_for_state(ccsr_i2c, I2C_SR_BB, I2C_SR_BB);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
VERBOSE("Before writing chip %d\n", chip);
|
||||
cr |= I2C_CR_TX | I2C_CR_TX_NAK;
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
ret = tx_byte(ccsr_i2c, chip << 1);
|
||||
if (ret < 0) {
|
||||
gen_stop(ccsr_i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
VERBOSE("Before writing addr\n");
|
||||
while (alen--) {
|
||||
ret = tx_byte(ccsr_i2c, (addr >> (alen << 3)) & 0xff);
|
||||
if (ret < 0) {
|
||||
gen_stop(ccsr_i2c);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_data(struct ls_i2c *ccsr_i2c, unsigned char chip,
|
||||
unsigned char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
unsigned char cr;
|
||||
|
||||
cr = i2c_in(&ccsr_i2c->cr);
|
||||
cr &= ~(I2C_CR_TX | I2C_CR_TX_NAK);
|
||||
if (len == 1) {
|
||||
cr |= I2C_CR_TX_NAK;
|
||||
}
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
|
||||
i2c_in(&ccsr_i2c->dr); /* dummy read */
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = wait_for_state(ccsr_i2c, I2C_SR_IF, I2C_SR_IF);
|
||||
if (ret < 0) {
|
||||
gen_stop(ccsr_i2c);
|
||||
return ret;
|
||||
}
|
||||
if (i == (len - 1)) {
|
||||
gen_stop(ccsr_i2c);
|
||||
} else if (i == (len - 2)) {
|
||||
/* Updating the command to send
|
||||
* No ACK.
|
||||
*/
|
||||
cr = i2c_in(&ccsr_i2c->cr);
|
||||
cr |= I2C_CR_TX_NAK;
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
}
|
||||
i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
|
||||
buf[i] = i2c_in(&ccsr_i2c->dr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_data(struct ls_i2c *ccsr_i2c, unsigned char chip,
|
||||
const unsigned char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = tx_byte(ccsr_i2c, buf[i]);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = gen_stop(ccsr_i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int i2c_read(unsigned char chip, int addr, int alen,
|
||||
unsigned char *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
unsigned char cr;
|
||||
struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
|
||||
|
||||
ret = i2c_write_addr(ccsr_i2c, chip, addr, alen);
|
||||
if (ret < 0) {
|
||||
gen_stop(ccsr_i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cr = i2c_in(&ccsr_i2c->cr);
|
||||
cr |= I2C_CR_RSTA;
|
||||
i2c_out(&ccsr_i2c->cr, cr);
|
||||
|
||||
ret = tx_byte(ccsr_i2c, (chip << 1) | 1);
|
||||
if (ret < 0) {
|
||||
gen_stop(ccsr_i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return read_data(ccsr_i2c, chip, buf, len);
|
||||
}
|
||||
|
||||
int i2c_write(unsigned char chip, int addr, int alen,
|
||||
const unsigned char *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
|
||||
|
||||
ret = i2c_write_addr(ccsr_i2c, chip, addr, alen);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return write_data(ccsr_i2c, chip, buf, len);
|
||||
}
|
||||
|
||||
int i2c_probe_chip(unsigned char chip)
|
||||
{
|
||||
int ret;
|
||||
struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
|
||||
|
||||
ret = i2c_write_addr(ccsr_i2c, chip, 0, 0);
|
||||
if (ret < 0) {
|
||||
WARN("write addr failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gen_stop(ccsr_i2c);
|
||||
if (ret < 0) {
|
||||
WARN("I2C: Probe not complete.\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
52
drivers/nxp/i2c/i2c.h
Normal file
52
drivers/nxp/i2c/i2c.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2016-2020 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef I2C_H
|
||||
#define I2C_H
|
||||
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#define I2C_TIMEOUT 1000 /* ms */
|
||||
|
||||
#define I2C_FD_CONSERV 0x7e
|
||||
#define I2C_CR_DIS (1 << 7)
|
||||
#define I2C_CR_EN (0 << 7)
|
||||
#define I2C_CR_MA (1 << 5)
|
||||
#define I2C_CR_TX (1 << 4)
|
||||
#define I2C_CR_TX_NAK (1 << 3)
|
||||
#define I2C_CR_RSTA (1 << 2)
|
||||
#define I2C_SR_BB (1 << 5)
|
||||
#define I2C_SR_IDLE (0 << 5)
|
||||
#define I2C_SR_AL (1 << 4)
|
||||
#define I2C_SR_IF (1 << 1)
|
||||
#define I2C_SR_RX_NAK (1 << 0)
|
||||
#define I2C_SR_RST (I2C_SR_AL | I2C_SR_IF)
|
||||
|
||||
#define I2C_GLITCH_EN 0x8
|
||||
|
||||
#define i2c_in(a) mmio_read_8((uintptr_t)(a))
|
||||
#define i2c_out(a, v) mmio_write_8((uintptr_t)(a), (v))
|
||||
|
||||
struct ls_i2c {
|
||||
unsigned char ad; /* I2c Bus Address Register */
|
||||
unsigned char fd; /* I2c Bus Frequency Dividor Register */
|
||||
unsigned char cr; /* I2c Bus Control Register */
|
||||
unsigned char sr; /* I2c Bus Status Register */
|
||||
unsigned char dr; /* I2C Bus Data I/O Register */
|
||||
unsigned char ic; /* I2C Bus Interrupt Config Register */
|
||||
unsigned char dbg; /* I2C Bus Debug Register */
|
||||
};
|
||||
|
||||
void i2c_init(uintptr_t nxp_i2c_addr);
|
||||
int i2c_read(unsigned char chip, int addr, int alen,
|
||||
unsigned char *buf, int len);
|
||||
int i2c_write(unsigned char chip, int addr, int alen,
|
||||
const unsigned char *buf, int len);
|
||||
int i2c_probe_chip(unsigned char chip);
|
||||
|
||||
#endif /* I2C_H */
|
25
drivers/nxp/i2c/i2c.mk
Normal file
25
drivers/nxp/i2c/i2c.mk
Normal file
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Copyright 2020 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
ifeq (${ADD_I2C},)
|
||||
|
||||
ADD_I2C := 1
|
||||
I2C_DRIVERS_PATH := ${PLAT_DRIVERS_PATH}/i2c
|
||||
|
||||
I2C_SOURCES += $(I2C_DRIVERS_PATH)/i2c.c
|
||||
PLAT_INCLUDES += -I$(I2C_DRIVERS_PATH)
|
||||
|
||||
ifeq (${BL_COMM_I2C_NEEDED},yes)
|
||||
BL_COMMON_SOURCES += ${I2C_SOURCES}
|
||||
else
|
||||
ifeq (${BL2_I2C_NEEDED},yes)
|
||||
BL2_SOURCES += ${I2C_SOURCES}
|
||||
endif
|
||||
ifeq (${BL31_I2C_NEEDED},yes)
|
||||
BL31_SOURCES += ${I2C_SOURCES}
|
||||
endif
|
||||
endif
|
||||
endif
|
Loading…
Add table
Reference in a new issue