gk7205v200-uboot/product/i2c/i2c.c
2025-08-07 17:13:54 +08:00

732 lines
17 KiB
C
Executable File

/*
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
*/
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <linux/errno.h>
struct i2c_init_desc {
unsigned long ctrl_base;
unsigned char flag;
};
#define I2C_INIT_UNDO 0
#define I2C_INIT_DONE 1
#define i2c_desc(reg) {.ctrl_base = (reg), \
.flag = I2C_INIT_UNDO}
#define I2C_BUS_CLOCK mhz(50)
#define I2C_MAX_NUM 0
struct i2c_init_desc gk_i2c_init_desc[] = NULL;
/*
* I2C Registers offsets
*/
#define VENDOR_I2C_GLB 0x0
#define VENDOR_I2C_SCL_H 0x4
#define VENDOR_I2C_SCL_L 0x8
#define VENDOR_I2C_DATA1 0x10
#define VENDOR_I2C_TXF 0x20
#define VENDOR_I2C_RXF 0x24
#define VENDOR_I2C_CMD_BASE 0x30
#define VENDOR_I2C_LOOP1 0xb0
#define VENDOR_I2C_DST1 0xb4
#define VENDOR_I2C_TX_WATER 0xc8
#define VENDOR_I2C_RX_WATER 0xcc
#define VENDOR_I2C_CTRL1 0xd0
#define VENDOR_I2C_CTRL2 0xd4
#define VENDOR_I2C_STAT 0xd8
#define VENDOR_I2C_INTR_RAW 0xe0
#define VENDOR_I2C_INTR_EN 0xe4
#define VENDOR_I2C_INTR_STAT 0xe8
/*
* Create a contiguous bitmask starting at bit position @l and ending at
* position @h.
*/
#define genmask(h, l) \
(((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
/*
* I2C Global Config Register -- VENDOR_I2C_GLB
*/
#define GLB_EN_MASK BIT(0)
#define GLB_SDA_HOLD_MASK genmask(23, 8)
#define GLB_SDA_HOLD_SHIFT 8
/*
* I2C Timing CMD Register -- VENDOR_I2C_CMD_BASE + n * 4 (n = 0, 1, 2, ... 31)
*/
#define CMD_EXIT 0x0
#define CMD_TX_S 0x1
#define CMD_TX_D1_2 0x4
#define CMD_TX_D1_1 0x5
#define CMD_TX_FIFO 0x9
#define CMD_RX_FIFO 0x12
#define CMD_RX_ACK 0x13
#define CMD_IGN_ACK 0x15
#define CMD_TX_ACK 0x16
#define CMD_TX_NACK 0x17
#define CMD_JMP1 0x18
#define CMD_UP_TXF 0x1d
#define CMD_TX_RS 0x1e
#define CMD_TX_P 0x1f
/*
* I2C Control Register 1 -- VENDOR_I2C_CTRL1
*/
#define CTRL1_CMD_START_MASK BIT(0)
/*
* I2C Status Register -- VENDOR_I2C_STAT
*/
#define STAT_RXF_NOE_MASK BIT(16) /* RX FIFO not empty flag */
#define STAT_TXF_NOF_MASK BIT(19) /* TX FIFO not full flag */
/*
* I2C Interrupt status and mask Register --
* VENDOR_I2C_INTR_RAW, VENDOR_I2C_STAT, VENDOR_I2C_INTR_STAT
*/
#define INTR_ABORT_MASK (BIT(0) | BIT(11))
#define INTR_RX_MASK BIT(2)
#define INTR_TX_MASK BIT(4)
#define INTR_CMD_DONE_MASK BIT(12)
#define INTR_USE_MASK (INTR_ABORT_MASK | \
INTR_RX_MASK | \
INTR_TX_MASK | \
INTR_CMD_DONE_MASK)
#define INTR_ALL_MASK genmask(31, 0)
#define I2C_DEFAULT_FREQUENCY 100000
#define I2C_TXF_DEPTH 64
#define I2C_RXF_DEPTH 64
#define I2C_TXF_WATER 32
#define I2C_RXF_WATER 32
#define I2C_WAIT_TIMEOUT 0x400
#define I2C_IRQ_TIMEOUT (msecs_to_jiffies(1000))
#define khz(clk) ((clk) * 1000)
#define mhz(clk) ((clk) * 1000000)
#define MAX_SPEED khz(400)
unsigned int g_i2c_speed = I2C_DEFAULT_FREQUENCY;
int g_i2c_num = -1;
struct gk_i2c_msg {
__u16 chip; /* slave address */
__u16 addr; /* reg address */
__u16 alen; /* reg width,1 or 2 */
__u16 flags;
__u16 len; /* msg length,1 or 2 */
__u8 *buf; /* pointer to msg data */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
};
struct gk_i2c_dev {
struct device *dev;
void *base;
unsigned int clk;
unsigned int freq;
struct gk_i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_buf_ptr;
int status;
};
struct gk_i2c_dev i2c_dev;
static inline void gk_i2c_disable(struct gk_i2c_dev *i2c);
static inline void gk_i2c_cfg_irq(struct gk_i2c_dev *i2c, unsigned int flag);
static inline unsigned int gk_i2c_clr_irq(struct gk_i2c_dev *i2c);
static inline void gk_i2c_enable(struct gk_i2c_dev *i2c);
#define CHECK_SDA_IN_SHIFT 16
#define GPIO_MODE_SHIFT 8
#define FORCE_SCL_OEN_SHIFT 4
#define FORCE_SDA_OEN_SHIFT 0
static void gk_i2c_rescue(struct gk_i2c_dev *i2c)
{
unsigned int val;
unsigned int time_cnt;
int index;
gk_i2c_disable(i2c);
gk_i2c_cfg_irq(i2c, 0);
gk_i2c_clr_irq(i2c);
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
(0x1 << FORCE_SDA_OEN_SHIFT);
writel(val, i2c->base + VENDOR_I2C_CTRL2);
time_cnt = 0;
do {
for (index = 0; index < 9; index++) { /* 9: Cycle */
val = (0x1 << GPIO_MODE_SHIFT) | 0x1;
writel(val, i2c->base + VENDOR_I2C_CTRL2);
udelay(5); /* delay 5us */
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
(0x1 << FORCE_SDA_OEN_SHIFT);
writel(val, i2c->base + VENDOR_I2C_CTRL2);
udelay(5); /* delay 5us */
}
time_cnt++;
if (time_cnt > I2C_WAIT_TIMEOUT) {
printf("wait Timeout!\n");
goto disable_rescue;
}
val = readl(i2c->base + VENDOR_I2C_CTRL2);
} while (!(val & (0x1 << CHECK_SDA_IN_SHIFT)));
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
(0x1 << FORCE_SDA_OEN_SHIFT);
writel(val, i2c->base + VENDOR_I2C_CTRL2);
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT);
writel(val, i2c->base + VENDOR_I2C_CTRL2);
udelay(10); /* delay 10us */
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
(0x1 << FORCE_SDA_OEN_SHIFT);
writel(val, i2c->base + VENDOR_I2C_CTRL2);
disable_rescue:
val = (0x1 << FORCE_SCL_OEN_SHIFT) | 0x1;
writel(val, i2c->base + VENDOR_I2C_CTRL2);
}
static void gk_i2c_disable(struct gk_i2c_dev *i2c)
{
unsigned int val;
val = readl(i2c->base + VENDOR_I2C_GLB);
val &= ~GLB_EN_MASK;
writel(val, i2c->base + VENDOR_I2C_GLB);
}
static void gk_i2c_enable(struct gk_i2c_dev *i2c)
{
unsigned int val;
val = readl(i2c->base + VENDOR_I2C_GLB);
val |= GLB_EN_MASK;
writel(val, i2c->base + VENDOR_I2C_GLB);
}
static inline void gk_i2c_cfg_irq(struct gk_i2c_dev *i2c,
unsigned int flag)
{
writel(flag, i2c->base + VENDOR_I2C_INTR_EN);
}
static void gk_i2c_disable_irq(struct gk_i2c_dev *i2c,
unsigned int flag)
{
unsigned int val;
val = readl(i2c->base + VENDOR_I2C_INTR_EN);
val &= ~flag;
writel(val, i2c->base + VENDOR_I2C_INTR_EN);
}
static unsigned int gk_i2c_clr_irq(struct gk_i2c_dev *i2c)
{
unsigned int val;
val = readl(i2c->base + VENDOR_I2C_INTR_STAT);
writel(INTR_ALL_MASK, i2c->base + VENDOR_I2C_INTR_RAW);
return val;
}
static inline void gk_i2c_cmdreg_set(struct gk_i2c_dev *i2c,
unsigned int cmd, unsigned int *offset)
{
/* the I2C_TIMING_CMD reg offset is (*offset * 4) */
writel(cmd, i2c->base + VENDOR_I2C_CMD_BASE + *offset * 4);
(*offset)++;
}
/*
* config i2c slave addr
*/
static void gk_i2c_set_addr(struct gk_i2c_dev *i2c)
{
struct gk_i2c_msg *msg = i2c->msg;
u16 addr;
if (msg->flags & I2C_M_TEN) {
/* First byte is 11110XX0 where XX is upper 2 bits */
addr = ((msg->addr & 0x300) << 1) | 0xf000;
if (msg->flags & I2C_M_RD)
addr |= 1 << 8; /* Move Left 8bit */
/* Second byte is the remaining 8 bits */
addr |= msg->addr & 0xff;
} else {
addr = (msg->addr & 0x7f) << 1;
if (msg->flags & I2C_M_RD)
addr |= 1;
}
writel(addr, i2c->base + VENDOR_I2C_DATA1);
}
/*
* Start command sequence
*/
static void gk_i2c_init_descart_cmd(struct gk_i2c_dev *i2c)
{
unsigned int val;
val = readl(i2c->base + VENDOR_I2C_CTRL1);
val |= CTRL1_CMD_START_MASK;
writel(val, i2c->base + VENDOR_I2C_CTRL1);
}
static int gk_i2c_wait_rx_noempty(struct gk_i2c_dev *i2c)
{
unsigned int time_cnt = 0;
unsigned int val;
do {
val = readl(i2c->base + VENDOR_I2C_STAT);
if (val & STAT_RXF_NOE_MASK)
return 0;
udelay(50); /* delay 50us */
} while (time_cnt++ < I2C_WAIT_TIMEOUT);
gk_i2c_rescue(i2c);
printf("wait rx no empty timeout, RIS: 0x%x, SR: 0x%x\n",
readl(i2c->base + VENDOR_I2C_INTR_RAW), val);
return -EIO;
}
static int gk_i2c_wait_tx_nofull(struct gk_i2c_dev *i2c)
{
unsigned int time_cnt = 0;
unsigned int val;
do {
val = readl(i2c->base + VENDOR_I2C_STAT);
if (val & STAT_TXF_NOF_MASK)
return 0;
udelay(50); /* delay 50us */
} while (time_cnt++ < I2C_WAIT_TIMEOUT);
gk_i2c_rescue(i2c);
printf("wait tx no empty timeout, RIS: 0x%x, SR: 0x%x\n",
readl(i2c->base + VENDOR_I2C_INTR_RAW), val);
return -EIO;
}
static int gk_i2c_wait_idle(struct gk_i2c_dev *i2c)
{
unsigned int time_cnt = 0;
unsigned int val;
do {
val = readl(i2c->base + VENDOR_I2C_INTR_RAW);
if (val & (INTR_ABORT_MASK)) {
printf("wait idle abort!, RIS: 0x%x\n",
val);
return -EIO;
}
if (val & INTR_CMD_DONE_MASK)
return 0;
udelay(50); /* delay 50us */
} while (time_cnt++ < I2C_WAIT_TIMEOUT);
gk_i2c_rescue(i2c);
printf("wait idle timeout, RIS: 0x%x, SR: 0x%x\n",
val, readl(i2c->base + VENDOR_I2C_STAT));
return -EIO;
}
static void gk_i2c_set_freq(struct gk_i2c_dev *i2c)
{
unsigned int max_freq, freq;
unsigned int clk_rate;
unsigned int val;
freq = i2c->freq;
clk_rate = i2c->clk;
max_freq = clk_rate >> 1;
if (freq > max_freq) {
i2c->freq = max_freq;
freq = i2c->freq;
}
if (!freq) {
printf("i2c_set_freq:freq can't be zero!");
return;
}
if (freq <= 100000) { /* 100000 : freq */
/* 1/2 of the SCL cycle in standard mode. */
val = clk_rate / (freq * 2);
writel(val, i2c->base + VENDOR_I2C_SCL_H);
writel(val, i2c->base + VENDOR_I2C_SCL_L);
} else {
/* 36/100 of the SCL cycle in fast mode. */
val = (clk_rate * 36) / (freq * 100);
writel(val, i2c->base + VENDOR_I2C_SCL_H);
/* 64/100 of the SCL cycle in fast mode. */
val = (clk_rate * 64) / (freq * 100);
writel(val, i2c->base + VENDOR_I2C_SCL_L);
}
val = readl(i2c->base + VENDOR_I2C_GLB);
val &= ~GLB_SDA_HOLD_MASK;
val |= ((0xa << GLB_SDA_HOLD_SHIFT) & GLB_SDA_HOLD_MASK);
writel(val, i2c->base + VENDOR_I2C_GLB);
}
/*
* set i2c controller TX and RX FIFO water
*/
static inline void gk_i2c_set_water(struct gk_i2c_dev *i2c)
{
writel(I2C_TXF_WATER, i2c->base + VENDOR_I2C_TX_WATER);
writel(I2C_RXF_WATER, i2c->base + VENDOR_I2C_RX_WATER);
}
/*
* initialise the controller, set i2c bus interface freq
*/
static void gk_i2c_hw_init(struct gk_i2c_dev *i2c)
{
gk_i2c_disable(i2c);
gk_i2c_disable_irq(i2c, INTR_ALL_MASK);
gk_i2c_set_freq(i2c);
gk_i2c_set_water(i2c);
}
/*
* gk_i2c_cfg_cmd - config i2c controller command sequence
*
* After all the timing command is configured,
* and then start the command, you can i2c communication,
* and then only need to read and write i2c fifo.
*/
static void gk_i2c_cfg_cmd(struct gk_i2c_dev *i2c)
{
struct gk_i2c_msg *msg = i2c->msg;
unsigned int offset = 0;
if (i2c->msg_idx == 0)
gk_i2c_cmdreg_set(i2c, CMD_TX_S, &offset);
else
gk_i2c_cmdreg_set(i2c, CMD_TX_RS, &offset);
if (msg->flags & I2C_M_TEN) {
if (i2c->msg_idx == 0) {
gk_i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
gk_i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
} else {
gk_i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
}
} else {
gk_i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
}
if (msg->flags & I2C_M_IGNORE_NAK)
gk_i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
else
gk_i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
if (msg->flags & I2C_M_RD) {
if (msg->len >= 2) { /* 2:len */
writel(offset, i2c->base + VENDOR_I2C_DST1);
writel(msg->len - 2, i2c->base + VENDOR_I2C_LOOP1); /* 2: len */
gk_i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
gk_i2c_cmdreg_set(i2c, CMD_TX_ACK, &offset);
gk_i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
}
gk_i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
gk_i2c_cmdreg_set(i2c, CMD_TX_NACK, &offset);
} else {
writel(offset, i2c->base + VENDOR_I2C_DST1);
writel(msg->len - 1, i2c->base + VENDOR_I2C_LOOP1);
gk_i2c_cmdreg_set(i2c, CMD_UP_TXF, &offset);
gk_i2c_cmdreg_set(i2c, CMD_TX_FIFO, &offset);
if (msg->flags & I2C_M_IGNORE_NAK)
gk_i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
else
gk_i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
gk_i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
}
if ((i2c->msg_idx == (i2c->msg_num - 1)) || (msg->flags & I2C_M_STOP))
gk_i2c_cmdreg_set(i2c, CMD_TX_P, &offset);
gk_i2c_cmdreg_set(i2c, CMD_EXIT, &offset);
}
static int gk_i2c_polling_xfer_one_msg(struct gk_i2c_dev *i2c)
{
int status;
unsigned int val;
struct gk_i2c_msg *msg = i2c->msg;
gk_i2c_enable(i2c);
gk_i2c_clr_irq(i2c);
gk_i2c_set_addr(i2c);
gk_i2c_cfg_cmd(i2c);
gk_i2c_init_descart_cmd(i2c);
i2c->msg_buf_ptr = 0;
if (msg->flags & I2C_M_RD) {
while (i2c->msg_buf_ptr < msg->len) {
status = gk_i2c_wait_rx_noempty(i2c);
if (status)
goto end;
val = readl(i2c->base + VENDOR_I2C_RXF);
msg->buf[i2c->msg_buf_ptr] = val;
i2c->msg_buf_ptr++;
}
} else {
while (i2c->msg_buf_ptr < msg->len) {
status = gk_i2c_wait_tx_nofull(i2c);
if (status)
goto end;
val = msg->buf[i2c->msg_buf_ptr];
writel(val, i2c->base + VENDOR_I2C_TXF);
i2c->msg_buf_ptr++;
}
}
status = gk_i2c_wait_idle(i2c);
end:
gk_i2c_disable(i2c);
return status;
}
/*
* Master transfer function
*/
static int gk_i2c_xfer(struct gk_i2c_msg *msgs, int num)
{
struct gk_i2c_dev *i2c = &i2c_dev;
int status = -EINVAL;
if (!msgs || (num <= 0)) {
printf("msgs == NULL || num <= 0, Invalid argument!\n");
return -EINVAL;
}
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_idx = 0;
while (i2c->msg_idx < i2c->msg_num) {
status = gk_i2c_polling_xfer_one_msg(i2c);
if (status)
break;
i2c->msg++;
i2c->msg_idx++;
}
if (!status || i2c->msg_idx > 0)
status = i2c->msg_idx;
return status;
}
void i2c_init(int speed, int i2c_num)
{
if (i2c_num < 0 || i2c_num >= I2C_MAX_NUM) {
printf("Cannot found I2C[%d]!\n", i2c_num);
return;
}
if (gk_i2c_init_desc[i2c_num].flag == I2C_INIT_UNDO) {
struct gk_i2c_dev *i2c = &i2c_dev;
i2c->base = (void *)(gk_i2c_init_desc[i2c_num].ctrl_base);
i2c->clk = I2C_BUS_CLOCK;
if (speed <= 0 || speed > MAX_SPEED) {
printf("Error speed out of range:[0, %d]!\n", MAX_SPEED);
} else {
i2c->freq = speed;
g_i2c_speed = i2c->freq;
}
gk_i2c_hw_init(i2c);
gk_i2c_init_desc[i2c_num].flag = I2C_INIT_DONE;
g_i2c_num = i2c_num;
printf("I2C[%d] init OK!\n", i2c_num);
} else {
printf("I2C[%d] had init!\n", i2c_num);
return;
}
}
int i2c_probe(unsigned char i2c_num)
{
printf("init i2c [%d],speed:[%u]\n", i2c_num, g_i2c_speed);
i2c_init(g_i2c_speed, i2c_num);
return 0;
}
unsigned int i2c_set_bus_speed(unsigned int speed)
{
if (g_i2c_num < 0) {
printf("please i2c probe [num] first!");
return 1;
}
if (g_i2c_speed != speed) {
g_i2c_speed = speed;
gk_i2c_init_desc[g_i2c_num].flag = I2C_INIT_UNDO;
printf("Set the i2c speed to %u\n", g_i2c_speed);
i2c_init(g_i2c_speed, g_i2c_num);
} else {
printf("The i2c speed has not changed!speed:%u\n", g_i2c_speed);
}
return 0;
}
unsigned int i2c_get_bus_speed(void)
{
return g_i2c_speed;
}
unsigned int i2c_set_bus_num(int bus_no)
{
struct gk_i2c_dev *i2c = &i2c_dev;
if (bus_no < 0 || bus_no >= I2C_MAX_NUM) {
printf("Error! out of range:[0, %d]!\n", I2C_MAX_NUM);
return 0;
}
if (gk_i2c_init_desc[bus_no].flag == I2C_INIT_UNDO) {
i2c_init(g_i2c_speed, bus_no);
} else {
g_i2c_num = bus_no;
i2c->base = (void *)(gk_i2c_init_desc[g_i2c_num].ctrl_base);
}
return 0;
}
unsigned int i2c_get_bus_num(void)
{
if (g_i2c_num < 0)
printf("Please set the I2C device number!\n");
return g_i2c_num;
}
int i2c_read(unsigned char chip, uint addr, int alen, unsigned char *buffer,
int len)
{
struct gk_i2c_dev;
struct gk_i2c_msg msg[2]; /* 2 bit array */
int num = (__u32)2;
int len_idx = 1;
unsigned int cur_addr;
int ret;
unsigned char buf[4]; /* 4 bit array */
memset(buf, 0x0, 4); /* memset the ength is 4 array. */
msg[0].addr = (chip >> 1);
msg[0].len = alen;
msg[0].buf = buf;
msg[0].flags = 0;
msg[1].addr = (chip >> 1);
msg[1].len = len;
msg[1].buf = buf;
msg[1].flags = 0;
msg[1].flags |= I2C_M_RD;
for (cur_addr = addr; len_idx <= len; cur_addr += 1) {
if (alen == 2) { /* 2:len */
buf[0] = (cur_addr >> 8) & 0xff; /* Move Right 8bit */
buf[1] = cur_addr & 0xff;
} else {
buf[0] = cur_addr & 0xff;
}
ret = gk_i2c_xfer(msg, num);
if (ret != 2) { /* 2: Return Value */
printf("i2c read: failed %d\n", ret);
return 0;
}
if (len == 2) /* 2:len */
*buffer = buf[0] | (buf[1] << 8); /* Move Left 8bit */
else
*buffer = buf[0];
printf("0x%x:0x%x\n", cur_addr, buf[0]);
len_idx++;
}
return 0;
}
int i2c_write(unsigned char chip, uint addr, int alen,
const unsigned char *buffer, int len)
{
struct gk_i2c_msg msg = {0};
unsigned char buf[4]; /* 4 bit array */
int index = 0;
int ret;
msg.addr = (chip >> 1);
msg.len = alen + len;
msg.buf = buf;
msg.flags = 0;
if (alen == 2) { /* 2:len */
buf[index] = (addr >> 8) & 0xff; /* Move Right 8bit */
index++;
buf[index] = addr & 0xff;
index++;
} else {
buf[index] = addr & 0xff;
index++;
}
if (len == 2) { /* 2:len */
buf[index] = *buffer & 0xff;
index++;
buf[index] = (*buffer >> 8) & 0xff; /* Move Right 8bit */
index++;
} else {
buf[index] = *buffer & 0xff;
index++;
}
ret = gk_i2c_xfer(&msg, 1);
if (ret != 1) {
printf("I2c write: failed %d\n", ret);
return 1;
}
return 0;
}