summaryrefslogtreecommitdiff
path: root/drivers/misc/starfive-otp.c
blob: 8ff189c35d9aca1b32419bcc396c001493afc328 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2022 Starfive, Inc.
 * Author:	yanhong <yanhong.wang@starfivetech.com>
 *
 */

#include <common.h>
#include <clk.h>
#include <dm/device.h>
#include <dm/read.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <misc.h>

/*otp param*/
#define OTP_MEM_START   0x800
#define OTP_MEM_SIZE    0x800
#define OTP_DIV_1US	1000000

/* program pulse time select. default value is 11*/
#define OTP_TPW_TIME	11

/* Read Data Access Time. max 50ns*/
#define OTP_TCD_TIME	20

#define OTP_PAOGRAM_DELAY	100
#define OTP_TURN_ON_DELAY	200

/*timing Register*/
#define OTP_CFGR_DIV_1US_MASK   0xff
#define OTP_CFGR_DIV_1US_SHIFT  8
#define OTP_CFGR_RD_CYC_MASK    0x0f
#define OTP_CFGR_RD_CYC_SHIFT   16

/*status Register*/
#define OTPC_SRR_BUSY           (1<<31)

/*otp operation mode select*/
#define OTP_OPRR_OPR_MASK           0x7
#define OTP_OPR_STANDBY             0x0 /* set otp to standby*/
#define OTP_OPR_READ                0x1 /* set otp to read*/

#define OTPC_TIMEOUT_CNT	10000
#define BYTES_PER_INT		4

struct starfive_otp_regs {
	u32 otp_cfg;        /*timing Register*/
	u32 otpc_ie;        /*interrupt Enable Register*/
	u32 otpc_sr;        /*status Register*/
	u32 otp_opr;        /*operation mode select Register*/
	u32 otpc_ctl;       /*otp control port*/
	u32 otpc_addr;      /*otp pa port*/
	u32 otpc_din;       /*otp pdin port*/
	u32 otpc_dout;      /*otp pdout*/
};

struct starfive_otp_platdata {
	struct starfive_otp_regs __iomem *regs;
	struct clk clk;
	u32 pclk_hz;
};

static int starfive_otp_regstatus(struct starfive_otp_regs *regs,
							u32 mask)
{
	int delay = OTPC_TIMEOUT_CNT;

	while (readl(&regs->otpc_sr) & mask) {
		udelay(OTP_PAOGRAM_DELAY);
		delay--;
		if (delay <= 0) {
			printf("%s: check otp status timeout\n", __func__);
			return -ETIMEDOUT;
		}
	}
	return 0;
}

static int starfive_otp_setmode(struct starfive_otp_regs *regs,
							u32 mode)
{
	writel(mode & OTP_OPRR_OPR_MASK, &regs->otp_opr);
	starfive_otp_regstatus(regs, OTPC_SRR_BUSY);

	return 0;
}

static int starfive_otp_config(struct udevice *dev)
{
	struct starfive_otp_platdata *plat = dev_get_plat(dev);
	struct starfive_otp_regs *regs = (struct starfive_otp_regs *)plat->regs;
	u32 div_1us;
	u32 rd_cyc;
	u32 val;

	div_1us = plat->pclk_hz / OTP_DIV_1US;
	rd_cyc = div_1us / OTP_TCD_TIME + 2;

	val = OTP_TPW_TIME;
	val |= (rd_cyc & OTP_CFGR_RD_CYC_MASK) << OTP_CFGR_RD_CYC_SHIFT;
	val |= (div_1us & OTP_CFGR_DIV_1US_MASK) << OTP_CFGR_DIV_1US_SHIFT;

	writel(val, &regs->otp_cfg);

	return 0;
}

static int starfive_otp_read(struct udevice *dev, int offset,
			   void *buf, int size)
{
	struct starfive_otp_platdata *plat = dev_get_plat(dev);
	void *base = (void *)plat->regs;
	u32 *databuf = (u32 *)buf;
	u32 data;
	int bytescnt;
	int i;

	if ((size % BYTES_PER_INT) || (offset % BYTES_PER_INT)) {
		printf("%s: size and offset must be multiple of 4.\n", __func__);
		return -EINVAL;
	}

	/* check bounds */
	if (!buf)
		return -EINVAL;
	if (offset >= OTP_MEM_SIZE)
		return -EINVAL;
	if ((offset + size) > OTP_MEM_SIZE)
		return -EINVAL;

	bytescnt = size / BYTES_PER_INT;

	for (i = 0; i < bytescnt; i++) {
		starfive_otp_setmode(plat->regs, OTP_OPR_READ);

		/* read the value */
		data = readl(base + OTP_MEM_START + offset);
		starfive_otp_regstatus(plat->regs, OTPC_SRR_BUSY);
		starfive_otp_setmode(plat->regs, OTP_OPR_STANDBY);
		databuf[i] = data;
		offset += 4;
	}

	return size;
}

static int starfive_otp_probe(struct udevice *dev)
{
	starfive_otp_config(dev);
	udelay(OTP_TURN_ON_DELAY);

	return 0;
}

static int starfive_otp_ofdata_to_platdata(struct udevice *dev)
{
	struct starfive_otp_platdata *plat = dev_get_plat(dev);
	int ret;

	plat->regs = dev_read_addr_ptr(dev);
	if (!plat->regs)
		goto err;

	ret = dev_read_u32(dev, "clock-frequency", &plat->pclk_hz);
	if (ret < 0)
		goto err;

	ret = clk_get_by_name(dev, "apb", &plat->clk);
	if (ret)
		goto err;

	ret = clk_enable(&plat->clk);
	if (ret < 0)
		goto err;

	return 0;
err:
	printf("%s init fail.\n", __func__);
	return ret;
}
static const struct misc_ops starfive_otp_ops = {
	.read = starfive_otp_read,
};
static const struct udevice_id starfive_otp_ids[] = {
	{ .compatible = "starfive,jh7110-otp" },
	{}
};
U_BOOT_DRIVER(starfive_otp) = {
	.name = "starfive_otp",
	.id = UCLASS_MISC,
	.of_match = starfive_otp_ids,
	.probe = starfive_otp_probe,
	.of_to_plat = starfive_otp_ofdata_to_platdata,
	.plat_auto = sizeof(struct starfive_otp_platdata),
	.ops = &starfive_otp_ops,
};