mirror of
https://github.com/Fishwaldo/opensbi.git
synced 2025-03-15 19:31:32 +00:00
lib: utils/i2c: Add minimal StarFive jh7110 I2C driver
Starfive JH7110 I2C IP is synopsys designware. Minimum StarFIve I2C driver to read/send bytes over I2C bus. This allows querying information and perform operation of onboard PMIC, as well as power-off and reset. Signed-off-by: Minda Chen <minda.chen@starfivetech.com> Reviewed-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
parent
4b28afc98b
commit
e9d08bd99c
5 changed files with 286 additions and 0 deletions
21
include/sbi_utils/i2c/dw_i2c.h
Normal file
21
include/sbi_utils/i2c/dw_i2c.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 StarFive Technology Co., Ltd.
|
||||
*
|
||||
* Author: Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#ifndef __DW_I2C_H__
|
||||
#define __DW_I2C_H__
|
||||
|
||||
#include <sbi_utils/i2c/i2c.h>
|
||||
|
||||
int dw_i2c_init(struct i2c_adapter *, int nodeoff);
|
||||
|
||||
struct dw_i2c_adapter {
|
||||
unsigned long addr;
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,8 +14,16 @@ config FDT_I2C_SIFIVE
|
|||
bool "SiFive I2C FDT driver"
|
||||
default n
|
||||
|
||||
config FDT_I2C_DW
|
||||
bool "Synopsys Designware I2C FDT driver"
|
||||
select I2C_DW
|
||||
default n
|
||||
endif
|
||||
|
||||
config I2C_DW
|
||||
bool "Synopsys Designware I2C support"
|
||||
default n
|
||||
|
||||
config I2C
|
||||
bool "I2C support"
|
||||
default n
|
||||
|
|
190
lib/utils/i2c/dw_i2c.c
Normal file
190
lib/utils/i2c/dw_i2c.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 starfivetech.com
|
||||
*
|
||||
* Authors:
|
||||
* Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi_utils/i2c/dw_i2c.h>
|
||||
|
||||
#define DW_IC_CON 0x00
|
||||
#define DW_IC_TAR 0x04
|
||||
#define DW_IC_SAR 0x08
|
||||
#define DW_IC_DATA_CMD 0x10
|
||||
#define DW_IC_SS_SCL_HCNT 0x14
|
||||
#define DW_IC_SS_SCL_LCNT 0x18
|
||||
#define DW_IC_FS_SCL_HCNT 0x1c
|
||||
#define DW_IC_FS_SCL_LCNT 0x20
|
||||
#define DW_IC_HS_SCL_HCNT 0x24
|
||||
#define DW_IC_HS_SCL_LCNT 0x28
|
||||
#define DW_IC_INTR_STAT 0x2c
|
||||
#define DW_IC_INTR_MASK 0x30
|
||||
#define DW_IC_RAW_INTR_STAT 0x34
|
||||
#define DW_IC_RX_TL 0x38
|
||||
#define DW_IC_TX_TL 0x3c
|
||||
#define DW_IC_CLR_INTR 0x40
|
||||
#define DW_IC_CLR_RX_UNDER 0x44
|
||||
#define DW_IC_CLR_RX_OVER 0x48
|
||||
#define DW_IC_CLR_TX_OVER 0x4c
|
||||
#define DW_IC_CLR_RD_REQ 0x50
|
||||
#define DW_IC_CLR_TX_ABRT 0x54
|
||||
#define DW_IC_CLR_RX_DONE 0x58
|
||||
#define DW_IC_CLR_ACTIVITY 0x5c
|
||||
#define DW_IC_CLR_STOP_DET 0x60
|
||||
#define DW_IC_CLR_START_DET 0x64
|
||||
#define DW_IC_CLR_GEN_CALL 0x68
|
||||
#define DW_IC_ENABLE 0x6c
|
||||
#define DW_IC_STATUS 0x70
|
||||
#define DW_IC_TXFLR 0x74
|
||||
#define DW_IC_RXFLR 0x78
|
||||
#define DW_IC_SDA_HOLD 0x7c
|
||||
#define DW_IC_TX_ABRT_SOURCE 0x80
|
||||
#define DW_IC_ENABLE_STATUS 0x9c
|
||||
#define DW_IC_CLR_RESTART_DET 0xa8
|
||||
#define DW_IC_COMP_PARAM_1 0xf4
|
||||
#define DW_IC_COMP_VERSION 0xf8
|
||||
|
||||
#define DW_I2C_STATUS_TXFIFO_EMPTY BIT(2)
|
||||
#define DW_I2C_STATUS_RXFIFO_NOT_EMPTY BIT(3)
|
||||
|
||||
#define IC_DATA_CMD_READ BIT(8)
|
||||
#define IC_DATA_CMD_STOP BIT(9)
|
||||
#define IC_DATA_CMD_RESTART BIT(10)
|
||||
#define IC_INT_STATUS_STOPDET BIT(9)
|
||||
|
||||
static inline void dw_i2c_setreg(struct dw_i2c_adapter *adap,
|
||||
u8 reg, u32 value)
|
||||
{
|
||||
writel(value, (void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static inline u32 dw_i2c_getreg(struct dw_i2c_adapter *adap,
|
||||
u32 reg)
|
||||
{
|
||||
return readl((void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_poll(struct dw_i2c_adapter *adap,
|
||||
u32 mask, u32 addr,
|
||||
bool inverted)
|
||||
{
|
||||
unsigned int timeout = 10; /* msec */
|
||||
int count = 0;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = dw_i2c_getreg(adap, addr);
|
||||
if (inverted) {
|
||||
if (!(val & mask))
|
||||
return 0;
|
||||
} else {
|
||||
if (val & mask)
|
||||
return 0;
|
||||
}
|
||||
sbi_timer_udelay(2);
|
||||
count += 1;
|
||||
if (count == (timeout * 1000))
|
||||
return SBI_ETIMEDOUT;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
#define dw_i2c_adapter_poll_rxrdy(adap) \
|
||||
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_RXFIFO_NOT_EMPTY, DW_IC_STATUS, 0)
|
||||
#define dw_i2c_adapter_poll_txfifo_ready(adap) \
|
||||
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_TXFIFO_EMPTY, DW_IC_STATUS, 0)
|
||||
|
||||
static int dw_i2c_write_addr(struct dw_i2c_adapter *adap, u8 addr)
|
||||
{
|
||||
dw_i2c_setreg(adap, DW_IC_ENABLE, 0);
|
||||
dw_i2c_setreg(adap, DW_IC_TAR, addr);
|
||||
dw_i2c_setreg(adap, DW_IC_ENABLE, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_read(struct i2c_adapter *ia, u8 addr,
|
||||
u8 reg, u8 *buffer, int len)
|
||||
{
|
||||
struct dw_i2c_adapter *adap =
|
||||
container_of(ia, struct dw_i2c_adapter, adapter);
|
||||
int rc;
|
||||
|
||||
dw_i2c_write_addr(adap, addr);
|
||||
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set register address */
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
|
||||
|
||||
/* set value */
|
||||
while (len) {
|
||||
if (len == 1)
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD,
|
||||
IC_DATA_CMD_READ | IC_DATA_CMD_STOP);
|
||||
else
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, IC_DATA_CMD_READ);
|
||||
|
||||
rc = dw_i2c_adapter_poll_rxrdy(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*buffer = dw_i2c_getreg(adap, DW_IC_DATA_CMD) & 0xff;
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_write(struct i2c_adapter *ia, u8 addr,
|
||||
u8 reg, u8 *buffer, int len)
|
||||
{
|
||||
struct dw_i2c_adapter *adap =
|
||||
container_of(ia, struct dw_i2c_adapter, adapter);
|
||||
int rc;
|
||||
|
||||
dw_i2c_write_addr(adap, addr);
|
||||
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set register address */
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
|
||||
|
||||
while (len) {
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (len == 1)
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer | IC_DATA_CMD_STOP);
|
||||
else
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer);
|
||||
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dw_i2c_init(struct i2c_adapter *adapter, int nodeoff)
|
||||
{
|
||||
adapter->id = nodeoff;
|
||||
adapter->write = dw_i2c_adapter_write;
|
||||
adapter->read = dw_i2c_adapter_read;
|
||||
|
||||
return i2c_adapter_add(adapter);
|
||||
}
|
62
lib/utils/i2c/fdt_i2c_dw.c
Normal file
62
lib/utils/i2c/fdt_i2c_dw.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 starfivetech.com
|
||||
*
|
||||
* Authors:
|
||||
* Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/i2c/dw_i2c.h>
|
||||
#include <sbi_utils/i2c/fdt_i2c.h>
|
||||
|
||||
#define FDT_DW_I2C_ADAPTER_MAX 7
|
||||
|
||||
static unsigned int fdt_dw_i2c_adapter_count;
|
||||
static struct dw_i2c_adapter
|
||||
fdt_dw_i2c_adapter_array[FDT_DW_I2C_ADAPTER_MAX];
|
||||
|
||||
extern struct fdt_i2c_adapter fdt_i2c_adapter_dw;
|
||||
|
||||
static int fdt_dw_i2c_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct dw_i2c_adapter *adapter;
|
||||
u64 addr;
|
||||
|
||||
if (fdt_dw_i2c_adapter_count >= FDT_DW_I2C_ADAPTER_MAX)
|
||||
return SBI_ENOSPC;
|
||||
|
||||
adapter = &fdt_dw_i2c_adapter_array[fdt_dw_i2c_adapter_count];
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
adapter->addr = addr;
|
||||
adapter->adapter.driver = &fdt_i2c_adapter_dw;
|
||||
|
||||
rc = dw_i2c_init(&adapter->adapter, nodeoff);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
fdt_dw_i2c_adapter_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match fdt_dw_i2c_match[] = {
|
||||
{ .compatible = "snps,designware-i2c" },
|
||||
{ .compatible = "starfive,jh7110-i2c" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_i2c_adapter fdt_i2c_adapter_dw = {
|
||||
.match_table = fdt_dw_i2c_match,
|
||||
.init = fdt_dw_i2c_init,
|
||||
};
|
|
@ -14,3 +14,8 @@ libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
|
|||
|
||||
carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
|
||||
libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
|
||||
|
||||
carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_DW) += fdt_i2c_adapter_dw
|
||||
libsbiutils-objs-$(CONFIG_FDT_I2C_DW) += i2c/fdt_i2c_dw.o
|
||||
|
||||
libsbiutils-objs-$(CONFIG_I2C_DW) += i2c/dw_i2c.o
|
||||
|
|
Loading…
Add table
Reference in a new issue