summaryrefslogtreecommitdiff
path: root/platform/generic/sifive/fu740.c
blob: f595c0484b676cab01192e53519d3489ee1e29e9 (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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/*
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2021 SiFive
 * Copyright (c) 2021 YADRO
 *
 * Authors:
 *   David Abdurachmanov <david.abdurachmanov@sifive.com>
 *   Nikita Shubin <n.shubin@yadro.com>
 */

#include <platform_override.h>
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_console.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/fdt/fdt_fixup.h>
#include <sbi_utils/reset/fdt_reset.h>
#include <sbi_utils/i2c/fdt_i2c.h>

#define DA9063_REG_PAGE_CON		0x00
#define DA9063_REG_CONTROL_A		0x0e
#define DA9063_REG_CONTROL_D		0x11
#define DA9063_REG_CONTROL_F		0x13
#define DA9063_REG_DEVICE_ID		0x81

#define DA9063_CONTROL_A_M_POWER1_EN	(1 << 6)
#define DA9063_CONTROL_A_M_POWER_EN	(1 << 5)
#define DA9063_CONTROL_A_STANDBY	(1 << 3)

#define DA9063_CONTROL_D_TWDSCALE_MASK	0x07

#define DA9063_CONTROL_F_WAKEUP	(1 << 2)
#define DA9063_CONTROL_F_SHUTDOWN	(1 << 1)

#define PMIC_CHIP_ID_DA9063		0x61

static struct {
	struct i2c_adapter *adapter;
	uint32_t reg;
} da9063;

static int da9063_system_reset_check(u32 type, u32 reason)
{
	switch (type) {
	case SBI_SRST_RESET_TYPE_SHUTDOWN:
		return 1;
	case SBI_SRST_RESET_TYPE_COLD_REBOOT:
	case SBI_SRST_RESET_TYPE_WARM_REBOOT:
		return 255;
	}

	return 0;
}

static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
{
	uint8_t val;
	int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);

	if (rc)
		return rc;

	/* check set page*/
	rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
	if (rc)
		return rc;

	if (val != 0x02)
		return SBI_ENODEV;

	/* read and check device id */
	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
	if (rc)
		return rc;

	if (val != PMIC_CHIP_ID_DA9063)
		return SBI_ENODEV;

	return 0;
}

static inline int da9063_stop_watchdog(struct i2c_adapter *adap, uint32_t reg)
{
	uint8_t val;
	int rc = i2c_adapter_reg_write(adap, reg,
					DA9063_REG_PAGE_CON, 0x00);

	if (rc)
		return rc;

	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_CONTROL_D, &val);
	if (rc)
		return rc;

	if ((val & DA9063_CONTROL_D_TWDSCALE_MASK) == 0)
		return 0;

	val &= ~DA9063_CONTROL_D_TWDSCALE_MASK;

	return i2c_adapter_reg_write(adap, reg, DA9063_REG_CONTROL_D, val);
}

static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
{
	int rc = i2c_adapter_reg_write(adap, reg,
					DA9063_REG_PAGE_CON, 0x00);

	if (rc)
		return rc;

	return i2c_adapter_reg_write(adap, reg,
				     DA9063_REG_CONTROL_F,
				     DA9063_CONTROL_F_SHUTDOWN);
}

static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
{
	int rc = i2c_adapter_reg_write(adap, reg,
					DA9063_REG_PAGE_CON, 0x00);

	if (rc)
		return rc;

	rc = i2c_adapter_reg_write(adap, reg,
				   DA9063_REG_CONTROL_F,
				   DA9063_CONTROL_F_WAKEUP);
	if (rc)
		return rc;

	return i2c_adapter_reg_write(adap, reg,
				DA9063_REG_CONTROL_A,
				DA9063_CONTROL_A_M_POWER1_EN |
				DA9063_CONTROL_A_M_POWER_EN |
				DA9063_CONTROL_A_STANDBY);
}

static void da9063_system_reset(u32 type, u32 reason)
{
	struct i2c_adapter *adap = da9063.adapter;
	uint32_t reg = da9063.reg;
	int rc;

	if (adap) {
		/* sanity check */
		rc = da9063_sanity_check(adap, reg);
		if (rc) {
			sbi_printf("%s: chip is not da9063 PMIC\n", __func__);
			goto skip_reset;
		}

		switch (type) {
		case SBI_SRST_RESET_TYPE_SHUTDOWN:
			da9063_shutdown(adap, reg);
			break;
		case SBI_SRST_RESET_TYPE_COLD_REBOOT:
		case SBI_SRST_RESET_TYPE_WARM_REBOOT:
			da9063_stop_watchdog(adap, reg);
			da9063_reset(adap, reg);
			break;
		}
	}

skip_reset:
	sbi_hart_hang();
}

static struct sbi_system_reset_device da9063_reset_i2c = {
	.name = "da9063-reset",
	.system_reset_check = da9063_system_reset_check,
	.system_reset = da9063_system_reset
};

static int da9063_reset_init(void *fdt, int nodeoff,
			     const struct fdt_match *match)
{
	int rc, i2c_bus;
	struct i2c_adapter *adapter;
	uint64_t addr;

	/* we are dlg,da9063 node */
	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
	if (rc)
		return rc;

	da9063.reg = addr;

	/* find i2c bus parent node */
	i2c_bus = fdt_parent_offset(fdt, nodeoff);
	if (i2c_bus < 0)
		return i2c_bus;

	/* i2c adapter get */
	rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
	if (rc)
		return rc;

	da9063.adapter = adapter;

	sbi_system_reset_add_device(&da9063_reset_i2c);

	return 0;
}

static const struct fdt_match da9063_reset_match[] = {
	{ .compatible = "dlg,da9063", .data = (void *)TRUE },
	{ },
};

struct fdt_reset fdt_reset_da9063 = {
	.match_table = da9063_reset_match,
	.init = da9063_reset_init,
};

static u64 sifive_fu740_tlbr_flush_limit(const struct fdt_match *match)
{
	/*
	 * Needed to address CIP-1200 errata on SiFive FU740
	 * Title: Instruction TLB can fail to respect a non-global SFENCE
	 * Workaround: Flush the TLB using SFENCE.VMA x0, x0
	 * See Errata_FU740-C000_20210205 from
	 * https://www.sifive.com/boards/hifive-unmatched
	 */
	return 0;
}

static int sifive_fu740_final_init(bool cold_boot,
				   const struct fdt_match *match)
{
	int rc;
	void *fdt = fdt_get_address();

	if (cold_boot) {
		rc = fdt_reset_driver_init(fdt, &fdt_reset_da9063);
		if (rc)
			sbi_printf("%s: failed to find da9063 for reset\n",
				   __func__);
	}

	return 0;
}

static const struct fdt_match sifive_fu740_match[] = {
	{ .compatible = "sifive,fu740" },
	{ .compatible = "sifive,fu740-c000" },
	{ .compatible = "sifive,hifive-unmatched-a00" },
	{ },
};

const struct platform_override sifive_fu740 = {
	.match_table = sifive_fu740_match,
	.tlbr_flush_limit = sifive_fu740_tlbr_flush_limit,
	.final_init = sifive_fu740_final_init,
};