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
|
// 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(®s->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, ®s->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, ®s->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 (!buf || (offset >= OTP_MEM_SIZE) || (offset & 0x3)) {
printf("%s:invalid parameter.\n", __func__);
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 0;
}
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,
};
|