Files
SDK_SG200x_V2/linux_5.10/drivers/efuse/cv180x_efuse.c
carbon 0545e9dc6d init version 2024-05-07
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.
2024-05-07 19:36:36 +08:00

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");