commit d1edce71135cc6d98c0a4b5729774542b676e769 Author: sophgo-forum-service <forum_service@sophgo.com> Date: Fri Mar 15 16:07:33 2024 +0800 [fix] recommend using ssh method to clone repo. [fix] fix sensor driver repo branch name.
268 lines
5.7 KiB
C
268 lines
5.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
//
|
|
// EFUSE implementation
|
|
//
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/io.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/module.h>
|
|
#include "linux/cv180x_efuse.h"
|
|
|
|
#define ERROR(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
|
|
#define VERBOSE(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
|
|
|
|
#define EFUSE_BASE (0x03050000)
|
|
|
|
#define EFUSE_SHADOW_REG (efuse_base + 0x100)
|
|
#define EFUSE_SIZE 0x100
|
|
|
|
#define EFUSE_MODE (efuse_base + 0x0)
|
|
#define EFUSE_ADR (efuse_base + 0x4)
|
|
#define EFUSE_DIR_CMD (efuse_base + 0x8)
|
|
#define EFUSE_RD_DATA (efuse_base + 0xC)
|
|
#define EFUSE_STATUS (efuse_base + 0x10)
|
|
#define EFUSE_ONE_WAY (efuse_base + 0x14)
|
|
|
|
#define EFUSE_BIT_AREAD BIT(0)
|
|
#define EFUSE_BIT_MREAD BIT(1)
|
|
#define EFUSE_BIT_PRG BIT(2)
|
|
#define EFUSE_BIT_PWR_DN BIT(3)
|
|
#define EFUSE_BIT_CMD BIT(4)
|
|
#define EFUSE_BIT_BUSY BIT(0)
|
|
#define EFUSE_CMD_REFRESH (0x30)
|
|
|
|
enum EFUSE_READ_TYPE { EFUSE_AREAD, EFUSE_MREAD };
|
|
|
|
static void __iomem *efuse_base;
|
|
static struct clk *efuse_clk;
|
|
|
|
static inline void mmio_write_32(void __iomem *addr, uint32_t value)
|
|
{
|
|
iowrite32(value, addr);
|
|
}
|
|
|
|
static inline uint32_t mmio_read_32(void __iomem *addr)
|
|
{
|
|
return ioread32(addr);
|
|
}
|
|
|
|
static inline void mmio_setbits_32(void __iomem *addr, uint32_t set)
|
|
{
|
|
mmio_write_32(addr, mmio_read_32(addr) | set);
|
|
}
|
|
|
|
void cvi_efuse_wait_for_ready(void)
|
|
{
|
|
while (mmio_read_32(EFUSE_STATUS) & EFUSE_BIT_BUSY)
|
|
;
|
|
}
|
|
|
|
static void cvi_efuse_power_on(uint32_t on)
|
|
{
|
|
if (on)
|
|
mmio_setbits_32(EFUSE_MODE, EFUSE_BIT_CMD);
|
|
else
|
|
mmio_setbits_32(EFUSE_MODE, EFUSE_BIT_PWR_DN | EFUSE_BIT_CMD);
|
|
}
|
|
|
|
static void cvi_efuse_refresh(void)
|
|
{
|
|
mmio_write_32(EFUSE_MODE, EFUSE_CMD_REFRESH);
|
|
}
|
|
|
|
static void cvi_efuse_prog_bit(uint32_t word_addr, uint32_t bit_addr,
|
|
uint32_t high_row)
|
|
{
|
|
uint32_t phy_addr;
|
|
|
|
// word_addr: virtual addr, take "lower 6-bits" from 7-bits (0-127)
|
|
// bit_addr: virtual addr, 5-bits (0-31)
|
|
|
|
// composite physical addr[11:0] = [11:7]bit_addr + [6:0]word_addr
|
|
phy_addr =
|
|
((bit_addr & 0x1F) << 7) | ((word_addr & 0x3F) << 1) | high_row;
|
|
|
|
cvi_efuse_wait_for_ready();
|
|
|
|
// send efuse program cmd
|
|
mmio_write_32(EFUSE_ADR, phy_addr);
|
|
mmio_write_32(EFUSE_MODE, EFUSE_BIT_PRG | EFUSE_BIT_CMD);
|
|
}
|
|
|
|
static uint32_t cvi_efuse_read_from_phy(uint32_t phy_word_addr,
|
|
enum EFUSE_READ_TYPE type)
|
|
{
|
|
// power on efuse macro
|
|
cvi_efuse_power_on(1);
|
|
|
|
cvi_efuse_wait_for_ready();
|
|
|
|
mmio_write_32(EFUSE_ADR, phy_word_addr);
|
|
|
|
if (type == EFUSE_AREAD) // array read
|
|
mmio_write_32(EFUSE_MODE, EFUSE_BIT_AREAD | EFUSE_BIT_CMD);
|
|
else if (type == EFUSE_MREAD) // margin read
|
|
mmio_write_32(EFUSE_MODE, EFUSE_BIT_MREAD | EFUSE_BIT_CMD);
|
|
else {
|
|
ERROR("EFUSE: Unsupported read type!");
|
|
return (uint32_t)-1;
|
|
}
|
|
|
|
cvi_efuse_wait_for_ready();
|
|
|
|
return mmio_read_32(EFUSE_RD_DATA);
|
|
}
|
|
|
|
static int cvi_efuse_write_word(uint32_t vir_word_addr, uint32_t val)
|
|
{
|
|
uint32_t i, j, row_val, zero_bit;
|
|
uint32_t new_value;
|
|
int err_cnt = 0;
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
VERBOSE("EFUSE: Program physical word addr #%d\n",
|
|
(vir_word_addr << 1) | j);
|
|
|
|
// array read by word address
|
|
row_val = cvi_efuse_read_from_phy(
|
|
(vir_word_addr << 1) | j,
|
|
EFUSE_AREAD); // read low word of word_addr
|
|
zero_bit = val & (~row_val); // only program zero bit
|
|
|
|
// program row which bit is zero
|
|
for (i = 0; i < 32; i++) {
|
|
if ((zero_bit >> i) & 1)
|
|
cvi_efuse_prog_bit(vir_word_addr, i, j);
|
|
}
|
|
|
|
// check by margin read
|
|
new_value = cvi_efuse_read_from_phy((vir_word_addr << 1) | j,
|
|
EFUSE_MREAD);
|
|
VERBOSE("%s(): val=0x%x new_value=0x%x\n", __func__, val,
|
|
new_value);
|
|
if ((val & new_value) != val) {
|
|
err_cnt += 1;
|
|
ERROR("EFUSE: Program bits check failed (%d)!\n",
|
|
err_cnt);
|
|
}
|
|
}
|
|
|
|
cvi_efuse_refresh();
|
|
|
|
return err_cnt >= 2 ? -EIO : 0;
|
|
}
|
|
|
|
int __init cvi_efuse_init(void)
|
|
{
|
|
efuse_base = ioremap(EFUSE_BASE, 0x1000);
|
|
if (efuse_base == NULL)
|
|
return -ENOMEM;
|
|
|
|
efuse_clk = clk_get_sys(NULL, "clk_efuse");
|
|
if (IS_ERR(efuse_clk)) {
|
|
pr_err("%s: efuse clock not found %ld\n", __func__,
|
|
PTR_ERR(efuse_clk));
|
|
return PTR_ERR(efuse_clk);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void __exit cvi_efuse_exit(void)
|
|
{
|
|
iounmap(efuse_base);
|
|
}
|
|
|
|
int64_t cvi_efuse_read_from_shadow(uint32_t addr)
|
|
{
|
|
int64_t ret = -1;
|
|
|
|
if (addr >= EFUSE_SIZE)
|
|
return -EFAULT;
|
|
|
|
if (addr % 4 != 0)
|
|
return -EFAULT;
|
|
|
|
ret = clk_prepare_enable(efuse_clk);
|
|
if (ret) {
|
|
pr_err("%s: clock failed to prepare+enable: %lld\n", __func__,
|
|
(long long)ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = mmio_read_32(EFUSE_SHADOW_REG + addr);
|
|
clk_disable_unprepare(efuse_clk);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(cvi_efuse_read_from_shadow);
|
|
|
|
int cvi_efuse_write(uint32_t addr, uint32_t value)
|
|
{
|
|
int ret;
|
|
|
|
VERBOSE("%s(): 0x%x = 0x%x\n", __func__, addr, value);
|
|
|
|
if (addr >= EFUSE_SIZE)
|
|
return -EFAULT;
|
|
|
|
if (addr % 4 != 0)
|
|
return -EFAULT;
|
|
|
|
ret = clk_prepare_enable(efuse_clk);
|
|
if (ret) {
|
|
pr_err("%s: clock failed to prepare+enable: %lld\n", __func__,
|
|
(long long)ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = cvi_efuse_write_word(addr / 4, value);
|
|
VERBOSE("%s(): ret=%d\n", __func__, ret);
|
|
|
|
cvi_efuse_power_on(1);
|
|
cvi_efuse_refresh();
|
|
cvi_efuse_wait_for_ready();
|
|
|
|
clk_disable_unprepare(efuse_clk);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(cvi_efuse_write);
|
|
|
|
int cvi_efuse_read_buf(u32 addr, void *buf, size_t buf_size)
|
|
{
|
|
int64_t ret = -1;
|
|
int i;
|
|
|
|
if (!buf)
|
|
return -EFAULT;
|
|
|
|
if (buf_size > EFUSE_SIZE)
|
|
buf_size = EFUSE_SIZE;
|
|
|
|
memset(buf, 0, buf_size);
|
|
|
|
for (i = 0; i < buf_size; i += 4) {
|
|
ret = cvi_efuse_read_from_shadow(addr + i);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret > 0) {
|
|
u32 v = ret;
|
|
|
|
memcpy(buf + i, &v, sizeof(v));
|
|
}
|
|
}
|
|
|
|
return buf_size;
|
|
}
|
|
EXPORT_SYMBOL(cvi_efuse_read_buf);
|
|
|
|
module_init(cvi_efuse_init);
|
|
module_exit(cvi_efuse_exit);
|
|
|
|
MODULE_AUTHOR("leon.liao@cvitek.com");
|
|
MODULE_DESCRIPTION("cv180x efuse driver");
|
|
MODULE_LICENSE("GPL");
|