drivers/nvmem/sunplus-ocotp.c

Source file repositories/reference/linux-study-clean/drivers/nvmem/sunplus-ocotp.c

File Facts

System
Linux kernel
Corpus path
drivers/nvmem/sunplus-ocotp.c
Extension
.c
Size
5813 bytes
Lines
230
Domain
Driver Families
Bucket
drivers/nvmem
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct sp_ocotp_priv {
	struct device *dev;
	void __iomem *base[BASEMAX];
	struct clk *clk;
};

struct sp_ocotp_data {
	int size;
};

static const struct sp_ocotp_data sp_otp_v0 = {
	.size = QAC628_OTP_SIZE,
};

static int sp_otp_read_real(struct sp_ocotp_priv *otp, int addr, char *value)
{
	unsigned int addr_data;
	unsigned int byte_shift;
	unsigned int status;
	int ret;

	addr_data = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
	addr_data = addr_data / OTP_WORD_SIZE;

	byte_shift = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
	byte_shift = byte_shift % OTP_WORD_SIZE;

	addr = addr / (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
	addr = addr * OTP_BIT_ADDR_OF_BANK;

	writel(readl(otp->base[OTPRX] + OTP_STATUS) & OTP_READ_DONE_MASK &
	       OTP_LOAD_SECURE_DONE_MASK, otp->base[OTPRX] + OTP_STATUS);
	writel(addr, otp->base[OTPRX] + OTP_READ_ADDRESS);
	writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) | OTP_READ,
	       otp->base[OTPRX] + OTP_CONTROL_2);
	writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) & SEL_BAK_KEY2_MASK & SW_TRIM_EN_MASK
	       & SEL_BAK_KEY_MASK & OTP_LOAD_SECURE_DATA_MASK & OTP_DO_CRC_MASK,
	       otp->base[OTPRX] + OTP_CONTROL_2);
	writel((readl(otp->base[OTPRX] + OTP_CONTROL_2) & OTP_RD_PERIOD_MASK) | CPU_CLOCK,
	       otp->base[OTPRX] + OTP_CONTROL_2);

	ret = readl_poll_timeout(otp->base[OTPRX] + OTP_STATUS, status,
				 status & OTP_READ_DONE, 10, OTP_READ_TIMEOUT_US);

	if (ret < 0)
		return ret;

	*value = (readl(otp->base[HB_GPIO] + ADDRESS_8_DATA + addr_data * OTP_WORD_SIZE)
		  >> (8 * byte_shift)) & 0xff;

	return ret;
}

static int sp_ocotp_read(void *priv, unsigned int offset, void *value, size_t bytes)
{
	struct sp_ocotp_priv *otp = priv;
	unsigned int addr;
	char *buf = value;
	char val[4];
	int ret;

	ret = clk_enable(otp->clk);
	if (ret)
		return ret;

	*buf = 0;
	for (addr = offset; addr < (offset + bytes); addr++) {
		ret = sp_otp_read_real(otp, addr, val);
		if (ret < 0) {
			dev_err(otp->dev, "OTP read fail:%d at %d", ret, addr);
			goto disable_clk;
		}

		*buf++ = *val;
	}

disable_clk:
	clk_disable(otp->clk);

	return ret;
}

static struct nvmem_config sp_ocotp_nvmem_config = {
	.name = "sp-ocotp",
	.add_legacy_fixed_of_cells = true,
	.read_only = true,
	.word_size = 1,
	.size = QAC628_OTP_SIZE,
	.stride = 1,
	.reg_read = sp_ocotp_read,

Annotation

Implementation Notes